summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim6
-rw-r--r--compiler/condsyms.nim1
-rw-r--r--compiler/evaltempl.nim4
-rw-r--r--compiler/pragmas.nim5
-rw-r--r--compiler/semstmts.nim7
-rw-r--r--compiler/semtempl.nim6
-rw-r--r--compiler/vm.nim5
-rw-r--r--compiler/wordrecg.nim2
-rw-r--r--lib/system.nim9
-rw-r--r--lib/system/comparisons.nim9
-rw-r--r--lib/system/setops.nim7
-rw-r--r--tests/template/tcallsitelineinfo.nim17
-rw-r--r--tests/template/tcallsitelineinfo2.nim20
13 files changed, 73 insertions, 25 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index e1d982a43..4174e7f34 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..<n.safeLen: setInfoRecursive(n[i], info)
+    n.info = info
+
 when defined(useNodeIds):
   const nodeIdToDebug* = -1 # 2322968
   var gNodeId: int
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index 10f4b86ab..c4f4d8290 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -143,4 +143,5 @@ proc initDefines*(symbols: StringTableRef) =
   defineSymbol("nimHasTopDownInference")
   defineSymbol("nimHasTemplateRedefinitionPragma")
   defineSymbol("nimHasCstringCase")
+  defineSymbol("nimHasCallsitePragma")
   defineSymbol("nimHasAmbiguousEnumHint")
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
index 8a6bf9287..d4aa466c2 100644
--- a/compiler/evaltempl.nim
+++ b/compiler/evaltempl.nim
@@ -26,7 +26,7 @@ type
 
 proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
   result = copyNode(a)
-  if ctx.instLines: result.info = b.info
+  if ctx.instLines: setInfoRecursive(result, b.info)
 
 proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
   template handleParam(param) =
@@ -204,7 +204,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
     result = copyNode(body)
     ctx.instLines = sfCallsite in tmpl.flags
     if ctx.instLines:
-      result.info = n.info
+      setInfoRecursive(result, n.info)
     for i in 0..<body.safeLen:
       evalTemplateAux(body[i], args, ctx, result)
   result.flags.incl nfFromTemplate
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index a312478ae..d4932a877 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -38,7 +38,7 @@ const
   converterPragmas* = procPragmas
   methodPragmas* = procPragmas+{wBase}-{wImportCpp}
   templatePragmas* = {wDeprecated, wError, wGensym, wInject, wDirty,
-    wDelegator, wExportNims, wUsed, wPragma, wRedefine}
+    wDelegator, wExportNims, wUsed, wPragma, wRedefine, wCallsite}
   macroPragmas* = declPragmas + {FirstCallConv..LastCallConv,
     wMagic, wNoSideEffect, wCompilerProc, wNonReloadable, wCore,
     wDiscardable, wGensym, wInject, wDelegator}
@@ -873,6 +873,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of wRedefine:
         if sym.kind == skTemplate: incl(sym.flags, sfTemplateRedefinition)
         else: invalidPragma(c, it)
+      of wCallsite:
+        if sym.kind == skTemplate: incl(sym.flags, sfCallsite)
+        else: invalidPragma(c, it)
       of wImportCpp:
         processImportCpp(c, sym, getOptionalStr(c, it, "$1"), it.info)
       of wCppNonPod:
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 270086e66..26356a33c 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -2338,11 +2338,6 @@ proc evalInclude(c: PContext, n: PNode): PNode =
     else:
       incMod(c, n, it, result)
 
-proc setLine(n: PNode, info: TLineInfo) =
-  if n != nil:
-    for i in 0..<n.safeLen: setLine(n[i], info)
-    n.info = info
-
 proc recursiveSetFlag(n: PNode, flag: TNodeFlag) =
   if n != nil:
     for i in 0..<n.safeLen: recursiveSetFlag(n[i], flag)
@@ -2371,7 +2366,7 @@ proc semPragmaBlock(c: PContext, n: PNode; expectedType: PType = nil): PNode =
   result.typ = n[1].typ
   for i in 0..<pragmaList.len:
     case whichPragma(pragmaList[i])
-    of wLine: setLine(result, pragmaList[i].info)
+    of wLine: setInfoRecursive(result, pragmaList[i].info)
     of wNoRewrite: recursiveSetFlag(result, nfNoRewrite)
     else: discard
 
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index 2d5f077f9..ed2475341 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -620,12 +620,6 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
     s = semIdentVis(c, skTemplate, n[namePos], {})
   assert s.kind == skTemplate
 
-  if s.owner != nil:
-    const names = ["!=", ">=", ">", "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 1717fe119..7a6dbd0ee 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 d33982b0f..c21eda1f9 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 fd31cf3da..1b6b3c9a8 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 eedac9d84..36d4d06a8 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 755eafdb8..b42c8b4a8 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 000000000..5fed93363
--- /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 000000000..d5f257474
--- /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")