From b550fb54d94170ddf6d50fc4117077af71268345 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 23 Jan 2025 21:45:31 +0800 Subject: [PATCH 1/6] fixes #24641; `quote do` captures no variables under current macros --- compiler/semexprs.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index f959783225a1..a191453f27a8 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2447,7 +2447,11 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[i], newNodeIT(nkType, n.info, typ), c.graph.emptyNode) else: dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[i], getSysSym(c.graph, n.info, "typed").newSymNode, c.graph.emptyNode) + # don't allow templates to capture variables in macors without backticks + let oldScope = c.currentScope + c.currentScope = c.topLevelScope var tmpl = semTemplateDef(c, dummyTemplate) + c.currentScope = oldScope quotes[0] = tmpl[namePos] # This adds a call to newIdentNode("result") as the first argument to the template call let identNodeSym = getCompilerProc(c.graph, "newIdentNode") From 7ce4433843718713f08c4231f1af53afafaa102a Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 23 Jan 2025 22:18:28 +0800 Subject: [PATCH 2/6] progress --- compiler/semexprs.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a191453f27a8..75c914731f53 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2447,9 +2447,9 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[i], newNodeIT(nkType, n.info, typ), c.graph.emptyNode) else: dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[i], getSysSym(c.graph, n.info, "typed").newSymNode, c.graph.emptyNode) - # don't allow templates to capture variables in macors without backticks + # don't allow templates to capture syms without backticks let oldScope = c.currentScope - c.currentScope = c.topLevelScope + c.currentScope = PScope(parent: nil, symbols: initStrTable(), depthLevel: 0) var tmpl = semTemplateDef(c, dummyTemplate) c.currentScope = oldScope quotes[0] = tmpl[namePos] From c70f67b0d679b1fe40e6cff9ec1d2fa517390971 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 24 Jan 2025 15:25:10 +0800 Subject: [PATCH 3/6] uses `topLevelScope` --- compiler/semexprs.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 75c914731f53..d7ade66c36f2 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2449,7 +2449,10 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[i], getSysSym(c.graph, n.info, "typed").newSymNode, c.graph.emptyNode) # don't allow templates to capture syms without backticks let oldScope = c.currentScope - c.currentScope = PScope(parent: nil, symbols: initStrTable(), depthLevel: 0) + # c.currentScope = PScope(parent: nil, symbols: initStrTable(), depthLevel: 0) + # TODO: allows toplevel syms to be captured for backwards compatibility + # perhaps `{.dirty.}` can be used for `dummyTemplate` + c.currentScope = c.topLevelScope var tmpl = semTemplateDef(c, dummyTemplate) c.currentScope = oldScope quotes[0] = tmpl[namePos] From 1b004f16326600860870992f9bca3942c839ab47 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 24 Jan 2025 17:10:44 +0800 Subject: [PATCH 4/6] progress: skips the current routine scopes --- compiler/semexprs.nim | 14 +++++++++++--- tests/lexer/tcustom_numeric_literals.nim | 4 ++-- tests/stdlib/tmacros.nim | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index d7ade66c36f2..f4ff443069ee 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2450,9 +2450,17 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = # don't allow templates to capture syms without backticks let oldScope = c.currentScope # c.currentScope = PScope(parent: nil, symbols: initStrTable(), depthLevel: 0) - # TODO: allows toplevel syms to be captured for backwards compatibility - # perhaps `{.dirty.}` can be used for `dummyTemplate` - c.currentScope = c.topLevelScope + if c.p.owner.kind in routineKinds: + # skips the current routine scopes + block exitLabel: + while c.currentScope != nil: + c.currentScope = c.currentScope.parent + block continueLabel: + for s in items(c.currentScope.symbols): + if s.owner != c.p.owner: + break exitLabel + else: + break continueLabel var tmpl = semTemplateDef(c, dummyTemplate) c.currentScope = oldScope quotes[0] = tmpl[namePos] diff --git a/tests/lexer/tcustom_numeric_literals.nim b/tests/lexer/tcustom_numeric_literals.nim index 35b4803d36c1..235e800d1b43 100644 --- a/tests/lexer/tcustom_numeric_literals.nim +++ b/tests/lexer/tcustom_numeric_literals.nim @@ -163,9 +163,9 @@ template main = doAssert fn3() == "[[-12]]" block: # bug 9 from https://github.com/nim-lang/Nim/pull/17020#issuecomment-803193947 + func wrap1(a: string): string = "{" & a & "}" + func `'wrap3`(a: string): string = "{" & a & "}" macro metawrap(): untyped = - func wrap1(a: string): string = "{" & a & "}" - func `'wrap3`(a: string): string = "{" & a & "}" result = quote do: let a1 {.inject.} = wrap1"-128" let a2 {.inject.} = -128'wrap3 diff --git a/tests/stdlib/tmacros.nim b/tests/stdlib/tmacros.nim index a4f13f951958..7c86eeb62bba 100644 --- a/tests/stdlib/tmacros.nim +++ b/tests/stdlib/tmacros.nim @@ -332,7 +332,7 @@ block: macro main = let x = 12 result = quote do: - `hello`(12, type(x)) + `hello`(12, type(`x`)) main() From b66ff0b6ac5351c59ca980500c1181ef37fa380e Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 24 Jan 2025 17:34:58 +0800 Subject: [PATCH 5/6] Apply suggestions from code review --- compiler/semexprs.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index f4ff443069ee..5b40ef2ee6c7 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2450,7 +2450,7 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = # don't allow templates to capture syms without backticks let oldScope = c.currentScope # c.currentScope = PScope(parent: nil, symbols: initStrTable(), depthLevel: 0) - if c.p.owner.kind in routineKinds: + if c.p.owner != nil and c.p.owner.kind in routineKinds: # skips the current routine scopes block exitLabel: while c.currentScope != nil: From 1a16f5594865a263193da08eef250da39222e2fb Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 24 Jan 2025 18:12:38 +0800 Subject: [PATCH 6/6] fixes --- compiler/semexprs.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 5b40ef2ee6c7..9ddc3f53d0c9 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2453,14 +2453,14 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = if c.p.owner != nil and c.p.owner.kind in routineKinds: # skips the current routine scopes block exitLabel: - while c.currentScope != nil: - c.currentScope = c.currentScope.parent + while c.currentScope != c.topLevelScope: block continueLabel: for s in items(c.currentScope.symbols): if s.owner != c.p.owner: break exitLabel else: break continueLabel + c.currentScope = c.currentScope.parent var tmpl = semTemplateDef(c, dummyTemplate) c.currentScope = oldScope quotes[0] = tmpl[namePos]