From a27542195c9ba760d58e9d1e977313bc322a1ede Mon Sep 17 00:00:00 2001 From: metagn Date: Thu, 26 Sep 2024 07:34:50 +0300 Subject: [PATCH] only merge valid implicit pragmas to routine AST, include templates (#24171) fixes #19277, refs #24169, refs #18124 When pragmas are pushed to a routine, if the routine symbol AST isn't nil by the time the pushed pragmas are being processed, the pragmas are implicitly added to the symbol AST. However this is done without restriction on the pragma, if the pushed pragma isn't supposed to apply to the routine, it's still added to the routine. This is why the symbol AST for templates wasn't set before the pushed pragma processing in #18124. Now, the pragmas added to the AST are restricted to ones that apply to the given routine. This means we can set the template symbol AST earlier so that the pragmas get added to the template AST. --- compiler/pragmas.nim | 13 ++++++++++++- compiler/semtempl.nim | 6 +----- tests/pragmas/tpush.nim | 12 ++++++++++++ tests/template/m19277_1.nim | 2 ++ tests/template/m19277_2.nim | 2 ++ tests/template/t19277.nim | 19 +++++++++++++++++++ 6 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 tests/template/m19277_1.nim create mode 100644 tests/template/m19277_2.nim create mode 100644 tests/template/t19277.nim diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index c4be1223b8a6..9a298cd90e8a 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -1344,6 +1344,16 @@ proc mergePragmas(n, pragmas: PNode) = else: for p in pragmas: n[pragmasPos].add p +proc mergeValidPragmas(n, pragmas: PNode, validPragmas: TSpecialWords) = + if n[pragmasPos].kind == nkEmpty: + n[pragmasPos] = newNodeI(nkPragma, n.info) + for p in pragmas: + let prag = whichPragma(p) + if prag in validPragmas: + let copy = copyTree(p) + overwriteLineInfo copy, n.info + n[pragmasPos].add copy + proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo, validPragmas: TSpecialWords) = if sym != nil and sym.kind != skModule: @@ -1357,7 +1367,8 @@ proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo, internalError(c.config, info, "implicitPragmas") inc i popInfoContext(c.config) - if sym.kind in routineKinds and sym.ast != nil: mergePragmas(sym.ast, o) + if sym.kind in routineKinds and sym.ast != nil: + mergeValidPragmas(sym.ast, o, validPragmas) if lfExportLib in sym.loc.flags and sfExportc notin sym.flags: localError(c.config, info, ".dynlib requires .exportc") diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 8bc68728cb80..817cb6249132 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -693,6 +693,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = pushOwner(c, s) openScope(c) n[namePos] = newSymNode(s) + s.ast = n # for implicitPragmas to use pragmaCallable(c, s, n, templatePragmas) implicitPragmas(c, s, n.info, templatePragmas) @@ -763,11 +764,6 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = closeScope(c) popOwner(c) - # set the symbol AST after pragmas, at least. This stops pragma that have - # been pushed (implicit) to be explicitly added to the template definition - # and misapplied to the body. see #18113 - s.ast = n - if sfCustomPragma in s.flags: if n[bodyPos].kind != nkEmpty: localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s) diff --git a/tests/pragmas/tpush.nim b/tests/pragmas/tpush.nim index a505bb32f104..9c6b85c4e822 100644 --- a/tests/pragmas/tpush.nim +++ b/tests/pragmas/tpush.nim @@ -125,8 +125,20 @@ foo41() {.pop.} +import macros + block: {.push deprecated.} template test() = discard test() {.pop.} + macro foo(): bool = + let ast = getImpl(bindSym"test") + var found = false + if ast[4].kind == nnkPragma: + for x in ast[4]: + if x.eqIdent"deprecated": + found = true + break + result = newLit(found) + doAssert foo() diff --git a/tests/template/m19277_1.nim b/tests/template/m19277_1.nim new file mode 100644 index 000000000000..840bd47674ab --- /dev/null +++ b/tests/template/m19277_1.nim @@ -0,0 +1,2 @@ +template foo*(x: untyped) = + echo "got: ", x diff --git a/tests/template/m19277_2.nim b/tests/template/m19277_2.nim new file mode 100644 index 000000000000..de72dad450db --- /dev/null +++ b/tests/template/m19277_2.nim @@ -0,0 +1,2 @@ +proc foo*(a: string) = + echo "got string: ", a diff --git a/tests/template/t19277.nim b/tests/template/t19277.nim new file mode 100644 index 000000000000..16435a09c45d --- /dev/null +++ b/tests/template/t19277.nim @@ -0,0 +1,19 @@ +discard """ + output: ''' +got: 0 +''' +""" + +# issue #19277 + +import m19277_1, m19277_2 + +template injector(val: untyped): untyped = + template subtemplate: untyped = val + subtemplate() + +template methodCall(val: untyped): untyped = val + +{.push raises: [Defect].} + +foo(injector(0).methodCall())