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.. 0 or s.final.len > 0 # later and use it to eliminate the temporary when theres no need for it, but its # tricky because you would have to intercept moveOrCopy at a certain point let tmp = c.getTemp(s.parent[], ret.typ, ret.info) - tmp.sym.flags.incl sfSingleUsedTemp + tmp.sym.flags = tmpFlags let cpy = if hasDestructor(c, ret.typ): s.parent[].final.add c.genDestroy(tmp) moveOrCopy(tmp, ret, c, s, isDecl = true) @@ -542,7 +542,8 @@ template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: unt result -template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) = +template handleNestedTempl(n, processCall: untyped, willProduceStmt = false, + tmpFlags = {sfSingleUsedTemp}) = template maybeVoid(child, s): untyped = if isEmptyType(child.typ): p(child, c, s, normal) else: processCall(child, s) @@ -570,7 +571,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) = branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt: processScope(c, ofScope, maybeVoid(it[^1], ofScope)) else: - processScopeExpr(c, ofScope, it[^1], processCall) + processScopeExpr(c, ofScope, it[^1], processCall, tmpFlags) result.add branch of nkWhileStmt: @@ -603,7 +604,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) = result.add if n[1].typ.isEmptyType or willProduceStmt: processScope(c, bodyScope, processCall(n[1], bodyScope)) else: - processScopeExpr(c, bodyScope, n[1], processCall) + processScopeExpr(c, bodyScope, n[1], processCall, tmpFlags) of nkIfStmt, nkIfExpr: result = copyNode(n) @@ -618,7 +619,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) = branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt: processScope(c, branchScope, maybeVoid(it[^1], branchScope)) else: - processScopeExpr(c, branchScope, it[^1], processCall) + processScopeExpr(c, branchScope, it[^1], processCall, tmpFlags) result.add branch of nkTryStmt: @@ -627,7 +628,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) = result.add if n[0].typ.isEmptyType or willProduceStmt: processScope(c, tryScope, maybeVoid(n[0], tryScope)) else: - processScopeExpr(c, tryScope, n[0], maybeVoid) + processScopeExpr(c, tryScope, n[0], maybeVoid, tmpFlags) for i in 1..=", ">", "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/arc/t18645.nim b/tests/arc/t18645.nim new file mode 100644 index 0000000000..c5fddd4bbc --- /dev/null +++ b/tests/arc/t18645.nim @@ -0,0 +1,18 @@ +discard """ + matrix: "--gc:arc; --gc:refc" + output: ''' +1 +2 +3 +''' +""" + +proc bitTypeIdUnion() = + var bitId {.global.} = block: + 0 + inc bitId + echo bitId + +bitTypeIdUnion() +bitTypeIdUnion() +bitTypeIdUnion() diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim index 5a68b7677b..ad25cad98c 100644 --- a/tests/pragmas/tcustom_pragma.nim +++ b/tests/pragmas/tcustom_pragma.nim @@ -423,6 +423,31 @@ when false: doAssert hasMyAttr(TObj) + +# bug #11415 +template noserialize() {.pragma.} + +type + Point[T] = object + x, y: T + + ReplayEventKind = enum + FoodAppeared, FoodEaten, DirectionChanged + + ReplayEvent = object + case kind: ReplayEventKind + of FoodEaten, FoodAppeared: # foodPos is in multiple branches + foodPos {.noserialize.}: Point[float] + of DirectionChanged: + playerPos: float +let ev = ReplayEvent( + kind: FoodEaten, + foodPos: Point[float](x: 5.0, y: 1.0) + ) + +doAssert ev.foodPos.hasCustomPragma(noserialize) + + when false: # misc {.pragma: haha.} 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")