From 700ca2eb60dafa3b9da2bd590730fe73a8283bfc Mon Sep 17 00:00:00 2001 From: metagn Date: Mon, 7 Oct 2024 12:39:26 +0300 Subject: [PATCH] process non-language pragma nodes in templates (#24183) fixes #24186 When encountering pragma nodes in templates, if it's a language pragma, we don't process the name, and only any values if they exist. If it's not a language pragma, we process the full node. Previously only the values of colon expressions were processed. To make this simpler, `whichPragma` is patched to consider bracketed hint/warning etc pragmas like `{.hint[HintName]: off.}` as being a pragma of kind `wHint` rather than an invalid pragma which would have to be checked separately. From looking at the uses of `whichPragma` this doesn't seem like it would cause problems. Generics have [the same problem](https://github.com/nim-lang/Nim/blob/a27542195c9ba760d58e9d1e977313bc322a1ede/compiler/semgnrc.nim#L619) (causing #18649), but to make it work we need to make sure the templates/macros don't get evaluated or get evaluated correctly (i.e. passing the proc node as the final argument), either with #23094 or by completely disabling template/macro evaluation when processing the pragma node, which would also cover `{.pragma.}` templates. (cherry picked from commit 911cef1621f5b00adff853696d45979474ff1dc3) --- compiler/semtempl.nim | 15 +++++++++++---- compiler/trees.nim | 7 +++++++ tests/template/tpragma.nim | 28 ++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 tests/template/tpragma.nim diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 817cb62491..f71d7986a5 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -535,13 +535,20 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = of nkConverterDef: result = semRoutineInTemplBody(c, n, skConverter) of nkPragmaExpr: - result[0] = semTemplBody(c, n[0]) + result = semTemplBodySons(c, n) of nkPostfix: result[1] = semTemplBody(c, n[1]) of nkPragma: - for x in n: - if x.kind == nkExprColonExpr: - x[1] = semTemplBody(c, x[1]) + for i in 0 ..< n.len: + let x = n[i] + let prag = whichPragma(x) + if prag == wInvalid: + # only sem if not a language-level pragma + result[i] = semTemplBody(c, x) + elif x.kind in nkPragmaCallKinds: + # is pragma, but value still needs to be checked + for j in 1 ..< x.len: + x[j] = semTemplBody(c, x[j]) of nkBracketExpr: if n.typ == nil: # if a[b] is nested inside a typed expression, don't convert it diff --git a/compiler/trees.nim b/compiler/trees.nim index 41b54eb096..da58878f82 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -147,6 +147,13 @@ proc whichPragma*(n: PNode): TSpecialWord = of nkCast: return wCast of nkClosedSymChoice, nkOpenSymChoice: return whichPragma(key[0]) + of nkBracketExpr: + if n.kind notin nkPragmaCallKinds: return wInvalid + result = whichPragma(key[0]) + if result notin {wHint, wHintAsError, wWarning, wWarningAsError}: + # note bracket pragmas, see processNote + result = wInvalid + return else: return wInvalid if result in nonPragmaWordsLow..nonPragmaWordsHigh: result = wInvalid diff --git a/tests/template/tpragma.nim b/tests/template/tpragma.nim new file mode 100644 index 0000000000..bc8c7b7416 --- /dev/null +++ b/tests/template/tpragma.nim @@ -0,0 +1,28 @@ +# issue #24186 + +macro mymacro(typ: typedesc; def) = + def + +macro mymacro2(typ: typedesc; typ2: typedesc; def) = + def + +template mytemplate(typ: typedesc) = # works + proc myproc() {.mymacro: typ .} = + discard + +template mytemplate2(typ: typedesc) = # Error: undeclared identifier: 'typ' + proc myproc2() {.mymacro(typ) .} = + discard + +template mytemplate3(typ: typedesc, typ2: typedesc) = # Error: undeclared identifier: 'typ' + proc myproc3() {.mymacro2(typ, typ2) .} = + discard + +template mytemplate4() = # works + proc myproc4() {.mymacro2(string, int) .} = + discard + +mytemplate(string) +mytemplate2(string) +mytemplate3(string, int) +mytemplate4()