From 77e1f1c436303d1257e916ba08353e504ec848ed Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 20 Mar 2021 01:56:15 -0700 Subject: [PATCH] test in vm + rt --- tests/stdlib/tgenast.nim | 517 ++++++++++++++++++++------------------- 1 file changed, 263 insertions(+), 254 deletions(-) diff --git a/tests/stdlib/tgenast.nim b/tests/stdlib/tgenast.nim index 8c420b4057f50..0904b83dd1a0f 100644 --- a/tests/stdlib/tgenast.nim +++ b/tests/stdlib/tgenast.nim @@ -1,260 +1,269 @@ +# xxx also test on js + import std/genasts import std/macros from std/strformat import `&` import ./mgenast -block: - macro bar(x0: static Foo, x1: Foo, x2: Foo, xignored: Foo): untyped = - let s0 = "not captured!" - let s1 = "not captured!" - let xignoredLocal = kfoo4 - - # newLit optional: - let x3 = newLit kfoo4 - let x3b = kfoo4 - - result = genAstOpt({kDirtyTemplate}, s1=true, s2="asdf", x0, x1=x1, x2, x3, x3b): - doAssert not declared(xignored) - doAssert not declared(xignoredLocal) - (s1, s2, s0, x0, x1, x2, x3, x3b) - - let s0 = "caller scope!" - - doAssert bar(kfoo1, kfoo2, kfoo3, kfoo4) == - (true, "asdf", "caller scope!", kfoo1, kfoo2, kfoo3, kfoo4, kfoo4) - -block: - # doesn't have limitation mentioned in https://github.com/nim-lang/RFCs/issues/122#issue-401636535 - macro abc(name: untyped): untyped = - result = genAst(name): - type name = object - - abc(Bar) - doAssert Bar.default == Bar() - -block: - # backticks parser limitations / ambiguities not are an issue with `genAst`: - # (#10326 #9745 are fixed but `quote do` still has underlying ambiguity issue - # with backticks) - type Foo = object - a: int - - macro m1(): untyped = - # result = quote do: # Error: undeclared identifier: 'a1' - result = genAst: - template `a1=`(x: var Foo, val: int) = - x.a = val - - m1() - var x0: Foo - x0.a1 = 10 - doAssert x0 == Foo(a: 10) - -block: - # avoids bug #7375 - macro fun(b: static[bool], b2: bool): untyped = - result = newStmtList() - macro foo(c: bool): untyped = - var b = false - result = genAst(b, c): - fun(b, c) - - foo(true) - -block: - # avoids bug #7589 - # since `==` works with genAst, the problem goes away - macro foo2(): untyped = - # result = quote do: # Error: '==' cannot be passed to a procvar - result = genAst: - `==`(3,4) - doAssert not foo2() - -block: - # avoids bug #7726 - # expressions such as `a.len` are just passed as arguments to `genAst`, and - # caller scope is not polluted with definitions such as `let b = newLit a.len` - macro foo(): untyped = - let a = @[1, 2, 3, 4, 5] - result = genAst(a, b = a.len): # shows 2 ways to get a.len - (a.len, b) - doAssert foo() == (5, 5) - -block: - # avoids bug #9607 - proc fun1(info:LineInfo): string = "bar1" - proc fun2(info:int): string = "bar2" - - macro bar2(args: varargs[untyped]): untyped = - let info = args.lineInfoObj - let fun1 = bindSym"fun1" # optional; we can remove this and also the - # capture of fun1, as show in next example - result = genAst(info, fun1): - (fun1(info), fun2(info.line)) - doAssert bar2() == ("bar1", "bar2") - - macro bar3(args: varargs[untyped]): untyped = - let info = args.lineInfoObj - result = genAst(info): - (fun1(info), fun2(info.line)) - doAssert bar3() == ("bar1", "bar2") - - macro bar(args: varargs[untyped]): untyped = - let info = args.lineInfoObj - let fun1 = bindSym"fun1" - let fun2 = bindSym"fun2" - result = genAstOpt({kDirtyTemplate}, info): - (fun1(info), fun2(info.line)) - doAssert bar() == ("bar1", "bar2") - -block: - # example from bug #7889 works - # after changing method call syntax to regular call syntax; this is a - # limitation described in bug #7085 - # note that `quote do` would also work after that change in this example. - doAssert bindme2() == kfoo1 - doAssert bindme3() == kfoo1 - doAssert not compiles(bindme4()) # correctly gives Error: undeclared identifier: 'myLocalPriv' - proc myLocalPriv2(): auto = kfoo2 - doAssert bindme5UseExpose() == kfoo1 - - # example showing hijacking behavior when using `kDirtyTemplate` - doAssert bindme5UseExposeFalse() == kfoo2 - # local `myLocalPriv2` hijacks symbol `mgenast.myLocalPriv2`. In most - # use cases this is probably not what macro writer intends as it's - # surprising; hence `kDirtyTemplate` is not the default. - - bindme6UseExpose() - bindme6UseExposeFalse() - -block: - macro mbar(x3: Foo, x3b: static Foo): untyped = - var x1=kfoo3 - var x2=newLit kfoo3 - var x4=kfoo3 - var xLocal=kfoo3 - - proc funLocal(): auto = kfoo4 - - result = genAst(x1, x2, x3, x4): - # local x1 overrides remote x1 - when false: - # one advantage of using `kDirtyTemplate` is that these would hold: - doAssert not declared xLocal - doAssert not compiles(echo xLocal) - # however, even without it, we at least correctly generate CT error - # if trying to use un-captured symbol; this correctly gives: - # Error: internal error: environment misses: xLocal - echo xLocal - - proc foo1(): auto = - # note that `funLocal` is captured implicitly, according to hygienic - # template rules; with `kDirtyTemplate` it would not unless - # captured in `genAst` capture list explicitly - (a0: xRemote, a1: x1, a2: x2, a3: x3, a4: x4, a5: funLocal()) - - return result - - proc main()= - var xRemote=kfoo1 - var x1=kfoo2 - mbar(kfoo4, kfoo4) - doAssert foo1() == (a0: kfoo1, a1: kfoo3, a2: kfoo3, a3: kfoo4, a4: kfoo3, a5: kfoo4) - - main() - -block: - # With `kDirtyTemplate`, the example from #8220 works. - # See https://nim-lang.github.io/Nim/strformat.html#limitations for - # an explanation of why {.dirty.} is needed. - macro foo(): untyped = - result = genAstOpt({kDirtyTemplate}): - let bar = "Hello, World" - &"Let's interpolate {bar} in the string" - doAssert foo() == "Let's interpolate Hello, World in the string" - - -block: # nested application of genAst - macro createMacro(name, obj, field: untyped): untyped = - result = genAst(obj = newDotExpr(obj, field), lit = 10, name, field): - # can't reuse `result` here, would clash - macro name(arg: untyped): untyped = - genAst(arg2=arg): # somehow `arg2` rename is needed - (obj, astToStr(field), lit, arg2) - - var x = @[1, 2, 3] - createMacro foo, x, len - doAssert (foo 20) == (3, "len", 10, 20) - -block: # test with kNoNewLit - macro bar(): untyped = - let s1 = true - template boo(x): untyped = - fun(x) - result = genAstOpt({kNoNewLit}, s1=newLit(s1), s1b=s1): (s1, s1b) - doAssert bar() == (true, 1) - -block: # sanity check: check passing `{}` also works - macro bar(): untyped = - result = genAstOpt({}, s1=true): s1 - doAssert bar() == true - -block: # test passing function and type symbols - proc z1(): auto = 41 - type Z4 = type(1'i8) - macro bar(Z1: typedesc): untyped = - proc z2(): auto = 42 - proc z3[T](a: T): auto = 43 - let Z2 = genAst(): - type(true) - let z4 = genAst(): - proc myfun(): auto = 44 - myfun - type Z3 = type(1'u8) - result = genAst(z4, Z1, Z2): - # z1, z2, z3, Z3, Z4 are captured automatically - # z1, z2, z3 can optionally be specified in capture list - (z1(), z2(), z3('a'), z4(), $Z1, $Z2, $Z3, $Z4) - type Z1 = type('c') - doAssert bar(Z1) == (41, 42, 43, 44, "char", "bool", "uint8", "int8") - -block: # fix bug #11986 - proc foo(): auto = - var s = { 'a', 'b' } - # var n = quote do: `s` # would print {97, 98} - var n = genAst(s): s - n.repr - static: doAssert foo() == "{'a', 'b'}" - -block: # also from #11986 - macro foo(): untyped = - var s = { 'a', 'b' } - # quote do: - # let t = `s` - # $typeof(t) # set[range 0..65535(int)] - genAst(s): - let t = s - $typeof(t) - doAssert foo() == "set[char]" - -block: - macro foo(): untyped = +proc main = + block: + macro bar(x0: static Foo, x1: Foo, x2: Foo, xignored: Foo): untyped = + let s0 = "not captured!" + let s1 = "not captured!" + let xignoredLocal = kfoo4 + + # newLit optional: + let x3 = newLit kfoo4 + let x3b = kfoo4 + + result = genAstOpt({kDirtyTemplate}, s1=true, s2="asdf", x0, x1=x1, x2, x3, x3b): + doAssert not declared(xignored) + doAssert not declared(xignoredLocal) + (s1, s2, s0, x0, x1, x2, x3, x3b) + + let s0 = "caller scope!" + + doAssert bar(kfoo1, kfoo2, kfoo3, kfoo4) == + (true, "asdf", "caller scope!", kfoo1, kfoo2, kfoo3, kfoo4, kfoo4) + + block: + # doesn't have limitation mentioned in https://github.com/nim-lang/RFCs/issues/122#issue-401636535 + macro abc(name: untyped): untyped = + result = genAst(name): + type name = object + + abc(Bar) + doAssert Bar.default == Bar() + + block: + # backticks parser limitations / ambiguities not are an issue with `genAst`: + # (#10326 #9745 are fixed but `quote do` still has underlying ambiguity issue + # with backticks) type Foo = object - template baz2(a: int): untyped = a*10 - macro baz3(a: int): untyped = newLit 13 - result = newStmtList() - - result.add genAst(Foo, baz2, baz3) do: # shows you can pass types, templates etc - var x: Foo - $($typeof(x), baz2(3), baz3(4)) - - let ret = genAst() do: # shows you don't have to, since they're inject'd - var x: Foo - $($typeof(x), baz2(3), baz3(4)) - doAssert foo() == """("Foo", 30, 13)""" - -block: # illustrates how symbol visiblity can be controlled precisely using `mixin` - proc locafun1(): auto = "in locafun1 (caller scope)" # this will be used because of `mixin locafun1` => explicit hijacking is ok - proc locafun2(): auto = "in locafun2 (caller scope)" # this won't be used => no hijacking - proc locafun3(): auto = "in locafun3 (caller scope)" - doAssert mixinExample() == ("in locafun1 (caller scope)", "in locafun2", "in locafun3 (caller scope)") + a: int + + macro m1(): untyped = + # result = quote do: # Error: undeclared identifier: 'a1' + result = genAst: + template `a1=`(x: var Foo, val: int) = + x.a = val + + m1() + var x0: Foo + x0.a1 = 10 + doAssert x0 == Foo(a: 10) + + block: + # avoids bug #7375 + macro fun(b: static[bool], b2: bool): untyped = + result = newStmtList() + macro foo(c: bool): untyped = + var b = false + result = genAst(b, c): + fun(b, c) + + foo(true) + + block: + # avoids bug #7589 + # since `==` works with genAst, the problem goes away + macro foo2(): untyped = + # result = quote do: # Error: '==' cannot be passed to a procvar + result = genAst: + `==`(3,4) + doAssert not foo2() + + block: + # avoids bug #7726 + # expressions such as `a.len` are just passed as arguments to `genAst`, and + # caller scope is not polluted with definitions such as `let b = newLit a.len` + macro foo(): untyped = + let a = @[1, 2, 3, 4, 5] + result = genAst(a, b = a.len): # shows 2 ways to get a.len + (a.len, b) + doAssert foo() == (5, 5) + + block: + # avoids bug #9607 + proc fun1(info:LineInfo): string = "bar1" + proc fun2(info:int): string = "bar2" + + macro bar2(args: varargs[untyped]): untyped = + let info = args.lineInfoObj + let fun1 = bindSym"fun1" # optional; we can remove this and also the + # capture of fun1, as show in next example + result = genAst(info, fun1): + (fun1(info), fun2(info.line)) + doAssert bar2() == ("bar1", "bar2") + + macro bar3(args: varargs[untyped]): untyped = + let info = args.lineInfoObj + result = genAst(info): + (fun1(info), fun2(info.line)) + doAssert bar3() == ("bar1", "bar2") + + macro bar(args: varargs[untyped]): untyped = + let info = args.lineInfoObj + let fun1 = bindSym"fun1" + let fun2 = bindSym"fun2" + result = genAstOpt({kDirtyTemplate}, info): + (fun1(info), fun2(info.line)) + doAssert bar() == ("bar1", "bar2") + + block: + # example from bug #7889 works + # after changing method call syntax to regular call syntax; this is a + # limitation described in bug #7085 + # note that `quote do` would also work after that change in this example. + doAssert bindme2() == kfoo1 + doAssert bindme3() == kfoo1 + doAssert not compiles(bindme4()) # correctly gives Error: undeclared identifier: 'myLocalPriv' + proc myLocalPriv2(): auto = kfoo2 + doAssert bindme5UseExpose() == kfoo1 + + # example showing hijacking behavior when using `kDirtyTemplate` + doAssert bindme5UseExposeFalse() == kfoo2 + # local `myLocalPriv2` hijacks symbol `mgenast.myLocalPriv2`. In most + # use cases this is probably not what macro writer intends as it's + # surprising; hence `kDirtyTemplate` is not the default. + + when nimvm: # disabled because `newStringStream` is used + discard + else: + bindme6UseExpose() + bindme6UseExposeFalse() + + block: + macro mbar(x3: Foo, x3b: static Foo): untyped = + var x1=kfoo3 + var x2=newLit kfoo3 + var x4=kfoo3 + var xLocal=kfoo3 + + proc funLocal(): auto = kfoo4 + + result = genAst(x1, x2, x3, x4): + # local x1 overrides remote x1 + when false: + # one advantage of using `kDirtyTemplate` is that these would hold: + doAssert not declared xLocal + doAssert not compiles(echo xLocal) + # however, even without it, we at least correctly generate CT error + # if trying to use un-captured symbol; this correctly gives: + # Error: internal error: environment misses: xLocal + echo xLocal + + proc foo1(): auto = + # note that `funLocal` is captured implicitly, according to hygienic + # template rules; with `kDirtyTemplate` it would not unless + # captured in `genAst` capture list explicitly + (a0: xRemote, a1: x1, a2: x2, a3: x3, a4: x4, a5: funLocal()) + + return result + + proc main()= + var xRemote=kfoo1 + var x1=kfoo2 + mbar(kfoo4, kfoo4) + doAssert foo1() == (a0: kfoo1, a1: kfoo3, a2: kfoo3, a3: kfoo4, a4: kfoo3, a5: kfoo4) + + main() + + block: + # With `kDirtyTemplate`, the example from #8220 works. + # See https://nim-lang.github.io/Nim/strformat.html#limitations for + # an explanation of why {.dirty.} is needed. + macro foo(): untyped = + result = genAstOpt({kDirtyTemplate}): + let bar = "Hello, World" + &"Let's interpolate {bar} in the string" + doAssert foo() == "Let's interpolate Hello, World in the string" + + + block: # nested application of genAst + macro createMacro(name, obj, field: untyped): untyped = + result = genAst(obj = newDotExpr(obj, field), lit = 10, name, field): + # can't reuse `result` here, would clash + macro name(arg: untyped): untyped = + genAst(arg2=arg): # somehow `arg2` rename is needed + (obj, astToStr(field), lit, arg2) + + var x = @[1, 2, 3] + createMacro foo, x, len + doAssert (foo 20) == (3, "len", 10, 20) + + block: # test with kNoNewLit + macro bar(): untyped = + let s1 = true + template boo(x): untyped = + fun(x) + result = genAstOpt({kNoNewLit}, s1=newLit(s1), s1b=s1): (s1, s1b) + doAssert bar() == (true, 1) + + block: # sanity check: check passing `{}` also works + macro bar(): untyped = + result = genAstOpt({}, s1=true): s1 + doAssert bar() == true + + block: # test passing function and type symbols + proc z1(): auto = 41 + type Z4 = type(1'i8) + macro bar(Z1: typedesc): untyped = + proc z2(): auto = 42 + proc z3[T](a: T): auto = 43 + let Z2 = genAst(): + type(true) + let z4 = genAst(): + proc myfun(): auto = 44 + myfun + type Z3 = type(1'u8) + result = genAst(z4, Z1, Z2): + # z1, z2, z3, Z3, Z4 are captured automatically + # z1, z2, z3 can optionally be specified in capture list + (z1(), z2(), z3('a'), z4(), $Z1, $Z2, $Z3, $Z4) + type Z1 = type('c') + doAssert bar(Z1) == (41, 42, 43, 44, "char", "bool", "uint8", "int8") + + block: # fix bug #11986 + proc foo(): auto = + var s = { 'a', 'b' } + # var n = quote do: `s` # would print {97, 98} + var n = genAst(s): s + n.repr + static: doAssert foo() == "{'a', 'b'}" + + block: # also from #11986 + macro foo(): untyped = + var s = { 'a', 'b' } + # quote do: + # let t = `s` + # $typeof(t) # set[range 0..65535(int)] + genAst(s): + let t = s + $typeof(t) + doAssert foo() == "set[char]" + + block: + macro foo(): untyped = + type Foo = object + template baz2(a: int): untyped = a*10 + macro baz3(a: int): untyped = newLit 13 + result = newStmtList() + + result.add genAst(Foo, baz2, baz3) do: # shows you can pass types, templates etc + var x: Foo + $($typeof(x), baz2(3), baz3(4)) + + let ret = genAst() do: # shows you don't have to, since they're inject'd + var x: Foo + $($typeof(x), baz2(3), baz3(4)) + doAssert foo() == """("Foo", 30, 13)""" + + block: # illustrates how symbol visiblity can be controlled precisely using `mixin` + proc locafun1(): auto = "in locafun1 (caller scope)" # this will be used because of `mixin locafun1` => explicit hijacking is ok + proc locafun2(): auto = "in locafun2 (caller scope)" # this won't be used => no hijacking + proc locafun3(): auto = "in locafun3 (caller scope)" + doAssert mixinExample() == ("in locafun1 (caller scope)", "in locafun2", "in locafun3 (caller scope)") + +static: main() +main()