From 2cca38d33c50650d1606c6ec09c73af6b8b0c3c8 Mon Sep 17 00:00:00 2001 From: metagn Date: Mon, 3 Oct 2022 07:07:55 +0300 Subject: [PATCH] pragma for sfCallsite instead of name check + better semantics, test (#20464) * pragma for sfCallsite instead of name check at every template definition Not documented because it seems to be for internal use? Should also make it possible to make comparisons and setops imports, but this doesn't have to be done. I can reuse a name like `cursor` for the pragma as well, added a new name just to be safe. * make sfCallsite recursive, add tests --- compiler/ast.nim | 6 ++++++ compiler/condsyms.nim | 1 + compiler/evaltempl.nim | 4 ++-- compiler/pragmas.nim | 5 ++++- compiler/semstmts.nim | 7 +------ compiler/semtempl.nim | 6 ------ compiler/vm.nim | 5 ++++- compiler/wordrecg.nim | 2 +- lib/system.nim | 9 ++++++--- lib/system/comparisons.nim | 9 ++++++--- lib/system/setops.nim | 7 +++++-- tests/template/tcallsitelineinfo.nim | 17 +++++++++++++++++ tests/template/tcallsitelineinfo2.nim | 20 ++++++++++++++++++++ 13 files changed, 73 insertions(+), 25 deletions(-) create mode 100644 tests/template/tcallsitelineinfo.nim create mode 100644 tests/template/tcallsitelineinfo2.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index e1d982a434..4174e7f34d 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1254,6 +1254,12 @@ proc skipPragmaExpr*(n: PNode): PNode = else: result = n +proc setInfoRecursive*(n: PNode, info: TLineInfo) = + ## set line info recursively + if n != nil: + for i in 0..=", ">", "incl", "excl", "in", "notin", "isnot"] - if sfSystemModule in s.owner.flags and s.name.s in names or - s.owner.name.s == "vm" and s.name.s == "stackTrace": - incl(s.flags, sfCallsite) - styleCheckDef(c, s) onDef(n[namePos].info, s) # check parameter list: diff --git a/compiler/vm.nim b/compiler/vm.nim index 1717fe1194..7a6dbd0ee5 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -75,8 +75,11 @@ proc stackTraceImpl(c: PCtx, tos: PStackFrame, pc: int, let lineInfo = if lineInfo == TLineInfo.default: c.debug[pc] else: lineInfo liMessage(c.config, lineInfo, errGenerated, msg, action, infoOrigin) +when not defined(nimHasCallsitePragma): + {.pragma: callsite.} + template stackTrace(c: PCtx, tos: PStackFrame, pc: int, - msg: string, lineInfo: TLineInfo = TLineInfo.default) = + msg: string, lineInfo: TLineInfo = TLineInfo.default) {.callsite.} = stackTraceImpl(c, tos, pc, msg, lineInfo, instantiationInfo(-2, fullPaths = true)) return diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index d33982b0fe..c21eda1f9c 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -87,7 +87,7 @@ type wGlobal = "global", wCodegenDecl = "codegenDecl", wUnchecked = "unchecked", wGuard = "guard", wLocks = "locks", wPartial = "partial", wExplain = "explain", wLiftLocals = "liftlocals", wEnforceNoRaises = "enforceNoRaises", - wRedefine = "redefine", + wRedefine = "redefine", wCallsite = "callsite", wAuto = "auto", wBool = "bool", wCatch = "catch", wChar = "char", wClass = "class", wCompl = "compl", wConstCast = "const_cast", wDefault = "default", diff --git a/lib/system.nim b/lib/system.nim index fd31cf3da3..1b6b3c9a80 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -731,13 +731,16 @@ proc contains*[U, V, W](s: HSlice[U, V], value: W): bool {.noSideEffect, inline. ## ``` result = s.a <= value and value <= s.b -template `in`*(x, y: untyped): untyped {.dirty.} = contains(y, x) +when not defined(nimHasCallsitePragma): + {.pragma: callsite.} + +template `in`*(x, y: untyped): untyped {.dirty, callsite.} = contains(y, x) ## Sugar for `contains`. ## ``` ## assert(1 in (1..3) == true) ## assert(5 in (1..3) == false) ## ``` -template `notin`*(x, y: untyped): untyped {.dirty.} = not contains(y, x) +template `notin`*(x, y: untyped): untyped {.dirty, callsite.} = not contains(y, x) ## Sugar for `not contains`. ## ``` ## assert(1 notin (1..3) == false) @@ -762,7 +765,7 @@ proc `is`*[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.} ## assert(test[int](3) == 3) ## assert(test[string]("xyz") == 0) ## ``` -template `isnot`*(x, y: untyped): untyped = not (x is y) +template `isnot`*(x, y: untyped): untyped {.callsite.} = not (x is y) ## Negated version of `is <#is,T,S>`_. Equivalent to `not(x is y)`. ## ``` ## assert 42 isnot float diff --git a/lib/system/comparisons.nim b/lib/system/comparisons.nim index eedac9d840..36d4d06a8e 100644 --- a/lib/system/comparisons.nim +++ b/lib/system/comparisons.nim @@ -125,15 +125,18 @@ proc `<`*[T](x, y: ref T): bool {.magic: "LtPtr", noSideEffect.} proc `<`*[T](x, y: ptr T): bool {.magic: "LtPtr", noSideEffect.} proc `<`*(x, y: pointer): bool {.magic: "LtPtr", noSideEffect.} -template `!=`*(x, y: untyped): untyped = +when not defined(nimHasCallsitePragma): + {.pragma: callsite.} + +template `!=`*(x, y: untyped): untyped {.callsite.} = ## Unequals operator. This is a shorthand for `not (x == y)`. not (x == y) -template `>=`*(x, y: untyped): untyped = +template `>=`*(x, y: untyped): untyped {.callsite.} = ## "is greater or equals" operator. This is the same as `y <= x`. y <= x -template `>`*(x, y: untyped): untyped = +template `>`*(x, y: untyped): untyped {.callsite.} = ## "is greater" operator. This is the same as `y < x`. y < x diff --git a/lib/system/setops.nim b/lib/system/setops.nim index 755eafdb81..b42c8b4a8d 100644 --- a/lib/system/setops.nim +++ b/lib/system/setops.nim @@ -9,7 +9,10 @@ func incl*[T](x: var set[T], y: T) {.magic: "Incl".} = a.incl(4) assert a == {1, 2, 3, 4, 5} -template incl*[T](x: var set[T], y: set[T]) = +when not defined(nimHasCallsitePragma): + {.pragma: callsite.} + +template incl*[T](x: var set[T], y: set[T]) {.callsite.} = ## Includes the set `y` in the set `x`. runnableExamples: var a = {1, 3, 5, 7} @@ -27,7 +30,7 @@ func excl*[T](x: var set[T], y: T) {.magic: "Excl".} = b.excl(5) assert b == {2, 3, 6, 12, 545} -template excl*[T](x: var set[T], y: set[T]) = +template excl*[T](x: var set[T], y: set[T]) {.callsite.} = ## Excludes the set `y` from the set `x`. runnableExamples: var a = {1, 3, 5, 7} diff --git a/tests/template/tcallsitelineinfo.nim b/tests/template/tcallsitelineinfo.nim new file mode 100644 index 0000000000..5fed933630 --- /dev/null +++ b/tests/template/tcallsitelineinfo.nim @@ -0,0 +1,17 @@ +discard """ + nimout: ''' +tcallsitelineinfo.nim(17, 4) Warning: abc [User] +''' + exitcode: 1 + outputsub: ''' +tcallsitelineinfo.nim(17) tcallsitelineinfo +Error: unhandled exception: def [ValueError] +''' +""" + +template foo(): untyped {.callsite.} = + {.warning: "abc".} + raise newException(ValueError, "def") + echo "hello" + +foo() diff --git a/tests/template/tcallsitelineinfo2.nim b/tests/template/tcallsitelineinfo2.nim new file mode 100644 index 0000000000..d5f2574749 --- /dev/null +++ b/tests/template/tcallsitelineinfo2.nim @@ -0,0 +1,20 @@ +discard """ + nimout: ''' +tcallsitelineinfo2.nim(18, 1) Warning: abc [User] +tcallsitelineinfo2.nim(19, 12) Warning: def [User] +''' + exitcode: 1 + outputsub: ''' +tcallsitelineinfo2.nim(20) tcallsitelineinfo2 +Error: unhandled exception: ghi [ValueError] +''' +""" + +template foo(a: untyped): untyped {.callsite.} = + {.warning: "abc".} + a + echo "hello" + +foo: # with `{.line.}:`, the following do not keep their line information: + {.warning: "def".} + raise newException(ValueError, "ghi")