From 89c10b1c24c67509a23e60d78fcc10d74062509a Mon Sep 17 00:00:00 2001 From: Peter Munch-Ellingsen Date: Thu, 15 Mar 2018 22:48:31 +0100 Subject: [PATCH 1/5] Fix result not being able to use in quote do This fixes the annoying issue of not be able to use result inside a quote do block. It works by a simple trick. The quote do mechanic is based on dynamically creating a template and immediately calling it with the arguments found within the quote do block. Since this is called in the scope of the macro the result variable is shadowed. This trick works by changing all occurences of result (which shouldn't cause any issues as result isn't used for anything else for the same reason) to another name and then passing in an IdentNode with result as a named parameter with that name. Note that currently this just replaces it with a fixed named variable "res" which should be changed to a non-colliding, dynamically created name. --- compiler/semexprs.nim | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index e683984c5e22..1f3c7fa3aadd 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1812,6 +1812,7 @@ proc processQuotations(c: PContext; n: var PNode, op: string, ids.add n return + if n.kind == nkPrefix: checkSonsLen(n, 2, c.config) if n[0].kind == nkIdent: @@ -1822,6 +1823,10 @@ proc processQuotations(c: PContext; n: var PNode, op: string, n.sons[0] = newIdentNode(getIdent(c.cache, examinedOp.substr(op.len)), n.info) elif n.kind == nkAccQuoted and op == "``": returnQuote n[0] + elif n.kind == nkIdent: + if n.ident.s == "result": + # TODO: Create a non-colliding symbol + n = newIdentNode(getIdent("res"), n.info) for i in 0 ..< n.safeLen: processQuotations(c, n.sons[i], op, quotes, ids) @@ -1833,15 +1838,19 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = var quotedBlock = n[^1] op = if n.len == 3: expectString(c, n[1]) else: "``" - quotes = newSeq[PNode](1) + quotes = newSeq[PNode](2) # the quotes will be added to a nkCall statement - # leave some room for the callee symbol - ids = newSeq[PNode]() + # leave some room for the callee symbol and the result symbol + ids = newSeq[PNode](1) # this will store the generated param names + # leave some room for the result symbol if quotedBlock.kind != nkStmtList: localError(c.config, n.info, errXExpected, "block") + # TODO: Create a non-colliding symbol + # This adds a default first field to pass the result symbol + ids[0] = newIdentNode(getIdent("res"), n.info) processQuotations(c, quotedBlock, op, quotes, ids) var dummyTemplate = newProcNode( @@ -1860,6 +1869,8 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = var tmpl = semTemplateDef(c, dummyTemplate) quotes[0] = tmpl[namePos] + # This adds a call to newIdentNode("result") as the first argument to the template call + quotes[1] = newNode(nkCall, n.info, @[newIdentNode(getIdent("newIdentNode"), n.info), newStrNode(nkStrLit, "result")]) result = newNode(nkCall, n.info, @[ createMagic(c.graph, "getAst", mExpandToAst).newSymNode, newNode(nkCall, n.info, quotes)]) From 430b26c7bf0c9dc65d0b4bf77e9b1f5072ef7e81 Mon Sep 17 00:00:00 2001 From: Peter Munch-Ellingsen Date: Fri, 16 Mar 2018 09:23:01 +0100 Subject: [PATCH 2/5] Fix hard coded parameter "res" to anonymous symbol This fixes the hard coded parameter "res" to be an anonymous symbol instead so it won't collide with other parts of the argument list. --- compiler/semexprs.nim | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 1f3c7fa3aadd..f0768b703aa9 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1825,8 +1825,7 @@ proc processQuotations(c: PContext; n: var PNode, op: string, returnQuote n[0] elif n.kind == nkIdent: if n.ident.s == "result": - # TODO: Create a non-colliding symbol - n = newIdentNode(getIdent("res"), n.info) + n = ids[0] for i in 0 ..< n.safeLen: processQuotations(c, n.sons[i], op, quotes, ids) @@ -1848,9 +1847,8 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = if quotedBlock.kind != nkStmtList: localError(c.config, n.info, errXExpected, "block") - # TODO: Create a non-colliding symbol # This adds a default first field to pass the result symbol - ids[0] = newIdentNode(getIdent("res"), n.info) + ids[0] = newAnonSym(c, skParam, n.info).newSymNode processQuotations(c, quotedBlock, op, quotes, ids) var dummyTemplate = newProcNode( From 827cf272f244588f8a479a578bce9c1bad12656c Mon Sep 17 00:00:00 2001 From: Peter Munch-Ellingsen Date: Fri, 16 Mar 2018 09:33:51 +0100 Subject: [PATCH 3/5] Add test case for result in quote do block A simple test case based on GitHub issue #7323 on how you can't put result in a quote do block. This test verifies that it actually works correctly now. --- tests/macros/tquotedo.nim | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/macros/tquotedo.nim diff --git a/tests/macros/tquotedo.nim b/tests/macros/tquotedo.nim new file mode 100644 index 000000000000..20593782aee6 --- /dev/null +++ b/tests/macros/tquotedo.nim @@ -0,0 +1,9 @@ +import macros + +macro mac(): untyped = + quote do: + proc test(): int = + (proc(): int = result = 123)() + +mac() +echo test() From f43fd5061e938c349a062e4bae2c1054a96608fa Mon Sep 17 00:00:00 2001 From: PMunch Date: Wed, 31 Oct 2018 16:16:26 +0100 Subject: [PATCH 4/5] Add test for explicit capturing of result --- tests/macros/tquotedo.nim | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/macros/tquotedo.nim b/tests/macros/tquotedo.nim index 20593782aee6..b663b81ba96c 100644 --- a/tests/macros/tquotedo.nim +++ b/tests/macros/tquotedo.nim @@ -7,3 +7,11 @@ macro mac(): untyped = mac() echo test() + +macro foobar(arg: untyped): untyped = + result = arg + result.add quote do: + `result` + +foobar: + echo "Hallo Welt" From 0e5de008f08021bd82f17f2c717f682f5032f76f Mon Sep 17 00:00:00 2001 From: PMunch Date: Wed, 31 Oct 2018 16:23:14 +0100 Subject: [PATCH 5/5] Rebased against devel --- compiler/semexprs.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index f0768b703aa9..7fdaabf81983 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1868,7 +1868,7 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = var tmpl = semTemplateDef(c, dummyTemplate) quotes[0] = tmpl[namePos] # This adds a call to newIdentNode("result") as the first argument to the template call - quotes[1] = newNode(nkCall, n.info, @[newIdentNode(getIdent("newIdentNode"), n.info), newStrNode(nkStrLit, "result")]) + quotes[1] = newNode(nkCall, n.info, @[newIdentNode(getIdent(c.cache, "newIdentNode"), n.info), newStrNode(nkStrLit, "result")]) result = newNode(nkCall, n.info, @[ createMagic(c.graph, "getAst", mExpandToAst).newSymNode, newNode(nkCall, n.info, quotes)])