diff options
Diffstat (limited to 'tests/macros')
49 files changed, 1628 insertions, 73 deletions
diff --git a/tests/macros/m18235.nim b/tests/macros/m18235.nim new file mode 100644 index 000000000..6bb4547e0 --- /dev/null +++ b/tests/macros/m18235.nim @@ -0,0 +1,42 @@ +import macros + +# Necessary code to update the AST on a symbol across module boundaries when +# processed by a type macro. Used by a test of a corresponding name of this +# file. + +macro eexport(n: typed): untyped = + result = copyNimTree(n) + # turn exported nnkSym -> nnkPostfix(*, nnkIdent), forcing re-sem + result[0] = nnkPostfix.newTree(ident"*").add: + n.name.strVal.ident + +macro unexport(n: typed): untyped = + result = copyNimTree(n) + # turn nnkSym -> nnkIdent, forcing re-sem and dropping any exported-ness + # that might be present + result[0] = n.name.strVal.ident + +proc foo*() {.unexport.} = discard +proc bar() {.eexport.} = discard + +proc foooof*() {.unexport, eexport, unexport.} = discard +proc barrab() {.eexport, unexport, eexport.} = discard + +macro eexportMulti(n: typed): untyped = + # use the call version of `eexport` macro for one or more decls + result = copyNimTree(n) + for i in 0..<result.len: + result[i] = newCall(ident"eexport", result[i]) + +macro unexportMulti(n: typed): untyped = + # use the call version of `unexport` macro for one or more decls + result = copyNimTree(n) + for i in 0..<result.len: + result[i] = newCall(ident"unexport", result[i]) + +unexportMulti: + proc oof*() = discard + +eexportMulti: + proc rab() = discard + proc baz*() = discard \ No newline at end of file diff --git a/tests/macros/mparsefile.nim b/tests/macros/mparsefile.nim new file mode 100644 index 000000000..8ac99d568 --- /dev/null +++ b/tests/macros/mparsefile.nim @@ -0,0 +1,4 @@ +let a = 1 +let b = 2 +let c = +let d = 4 diff --git a/tests/macros/t14227.nim b/tests/macros/t14227.nim new file mode 100644 index 000000000..4206e2c06 --- /dev/null +++ b/tests/macros/t14227.nim @@ -0,0 +1,23 @@ +discard """ + action: "compile" +""" +import sugar + + +block: + let y = @[@[1, 2], @[2, 4, 6]] + let x = collect(newSeq): + for i in y: + if i.len > 2: + for j in i: + j + echo(x) + +block: + let y = @[@[1, 2], @[2, 4, 6]] + let x = collect(newSeq): + for i in y: + for j in i: + if i.len > 2: + j + echo(x) diff --git a/tests/macros/t14329.nim b/tests/macros/t14329.nim new file mode 100644 index 000000000..b5606424a --- /dev/null +++ b/tests/macros/t14329.nim @@ -0,0 +1,4 @@ +import macros + +macro myMacro(n) = + let x = if true: newLit"test" else: error "error", n diff --git a/tests/macros/t14511.nim b/tests/macros/t14511.nim new file mode 100644 index 000000000..f3b1e2894 --- /dev/null +++ b/tests/macros/t14511.nim @@ -0,0 +1,54 @@ +discard """ + output: "true\n(y: XInt, a: 5)\n(y: XString, b: \"abc\")" +""" + +import macros + +block TEST_1: + # https://github.com/nim-lang/Nim/issues/14511 + + template myPragma() {.pragma.} + + type + XType = enum + XInt, + XString, + XUnused + X = object + case y {.myPragma.}: XType + of XInt, XUnused: + a: int + else: # <-- Else case caused the "Error: index 1 not in 0 .. 0" error + b: string + + var x: X = X(y: XInt, a: 5) + echo x.y.hasCustomPragma(myPragma) + echo x + echo X(y: XString, b: "abc") + + +block TEST_2: + template myDevice(val: string) {.pragma.} + template myKey(val: string) {.pragma.} + template myMouse(val: string) {.pragma.} + + type + Device {.pure.} = enum Keyboard, Mouse + Key = enum Key1, Key2 + Mouse = enum Mouse1, Mouse2 + + type + Obj = object of RootObj + case device {.myDevice: "MyDevicePragmaStr".}: Device + of Device.Keyboard: + key {.myKey: "MyKeyPragmaStr".}: Key + else: # <-- Else case caused the "Error: index 1 not in 0 .. 0" error + mouse {.myMouse: "MyMousePragmaStr".}: Mouse + + var obj: Obj + assert obj.device.hasCustomPragma(myDevice) == true + assert obj.key.hasCustomPragma(myKey) == true + assert obj.mouse.hasCustomPragma(myMouse) == true + assert obj.device.getCustomPragmaVal(myDevice) == "MyDevicePragmaStr" + assert obj.key.getCustomPragmaVal(myKey) == "MyKeyPragmaStr" + assert obj.mouse.getCustomPragmaVal(myMouse) == "MyMousePragmaStr" \ No newline at end of file diff --git a/tests/macros/t14847.nim b/tests/macros/t14847.nim new file mode 100644 index 000000000..0e6d0dd2d --- /dev/null +++ b/tests/macros/t14847.nim @@ -0,0 +1,20 @@ +discard """ + output: "98" +""" +import macros + +#bug #14847 +proc hello*(b: string) = + echo b + +macro dispatch(pro: typed, params: untyped): untyped = + var impl = pro.getImpl + let id = ident(pro.strVal & "_coverage") + impl[0] = id + let call = newCall(id, params) + + result = newStmtList() + result.add(impl) + result.add(call) + +dispatch(hello, "98") diff --git a/tests/macros/t15691.nim b/tests/macros/t15691.nim new file mode 100644 index 000000000..c1e8a8648 --- /dev/null +++ b/tests/macros/t15691.nim @@ -0,0 +1,22 @@ +discard """ + action: compile +""" + +import std/macros + +macro simplifiedExpandMacros(body: typed): untyped = + result = body + +simplifiedExpandMacros: + proc testProc() = discard + +simplifiedExpandMacros: + template testTemplate(): untyped = discard + +# Error: illformed AST: macro testMacro(): untyped = +simplifiedExpandMacros: + macro testMacro(): untyped = discard + +# Error: illformed AST: converter testConverter(x: int): float = +simplifiedExpandMacros: + converter testConverter(x: int): float = discard diff --git a/tests/macros/t15751.nim b/tests/macros/t15751.nim new file mode 100644 index 000000000..fcabb2f9e --- /dev/null +++ b/tests/macros/t15751.nim @@ -0,0 +1,11 @@ +discard """ + cmd: "nim c --hints:off $file" + nimout: "out" +""" + +# bug #15751 +macro print(n: untyped): untyped = + echo n.repr + +print: + out diff --git a/tests/macros/t16758.nim b/tests/macros/t16758.nim new file mode 100644 index 000000000..66b6d42c5 --- /dev/null +++ b/tests/macros/t16758.nim @@ -0,0 +1,38 @@ +discard """ +errormsg: "'blk.p(a)' has nil child at index 1" +action: reject +""" +import macros + +type BlockLiteral[T] = object + p: T + +proc p[T](a:int) = echo 1 +proc p[T](a:string) = echo "a" + +iterator arguments(formalParams: NimNode): NimNode = + var iParam = 0 + for i in 1 ..< formalParams.len: + let pp = formalParams[i] + for j in 0 .. pp.len - 3: + yield pp[j] + inc iParam + +macro implementInvoke(T: typedesc): untyped = + let t = getTypeImpl(T)[1] + + let call = newCall(newDotExpr(ident"blk", ident"p")) + let params = copyNimTree(t[0]) + result = newProc(ident"invoke", body = call) + # result[2] = newTree(nnkGenericParams,T) + for n in arguments(params): + call.add(n) + + params.insert(1, newIdentDefs(ident"blk", newTree(nnkBracketExpr, bindSym"BlockLiteral", T))) + result.params = params + +proc getInvoke(T: typedesc) = + implementInvoke(T) + + +getInvoke(proc(a: int)) diff --git a/tests/macros/t17836.nim b/tests/macros/t17836.nim new file mode 100644 index 000000000..2453637f5 --- /dev/null +++ b/tests/macros/t17836.nim @@ -0,0 +1,15 @@ +import macros + +# Ensure that `isNil` works in the typed macro context when pass procs. + +type + O = object + fn: proc(i: int): int + +var o: O + +macro typedBug(expr: typed) = + doAssert expr[1] != nil + doAssert not expr[1].isNil + +typedBug(o.fn) \ No newline at end of file diff --git a/tests/macros/t18203.nim b/tests/macros/t18203.nim new file mode 100644 index 000000000..aae0a2690 --- /dev/null +++ b/tests/macros/t18203.nim @@ -0,0 +1,15 @@ +discard """ + matrix: "--hint:SuccessX:off --hint:Link:off --hint:Conf:off --hint:CC:off --hint:XDeclaredButNotUsed:on" + nimout: ''' +''' +nimoutFull: true +action: compile +""" + +# bug #18203 +import std/macros + +macro foo(x: typed) = newProc ident"bar" +proc bar() {.foo.} = raise +bar() + diff --git a/tests/macros/t18235.nim b/tests/macros/t18235.nim new file mode 100644 index 000000000..ba5c48a24 --- /dev/null +++ b/tests/macros/t18235.nim @@ -0,0 +1,18 @@ +import m18235 + +# this must error out because it was never actually exported +doAssert(not declared(foo)) +doAssert not compiles(foo()) + +doAssert(not declared(foooof)) +doAssert not compiles(foooof()) + +doAssert(not declared(oof)) +doAssert not compiles(oof()) + +# this should have been exported just fine + +bar() +barrab() +rab() +baz() \ No newline at end of file diff --git a/tests/macros/t19766_20114.nim b/tests/macros/t19766_20114.nim new file mode 100644 index 000000000..ac336f150 --- /dev/null +++ b/tests/macros/t19766_20114.nim @@ -0,0 +1,16 @@ +discard """ + action: compile + nimout: ''' +const + foo {.strdefine.} = "abc" +let hey {.tddd.} = 5 +''' +""" + +import macros + +template tddd {.pragma.} + +expandMacros: + const foo {.strdefine.} = "abc" + let hey {.tddd.} = 5 diff --git a/tests/macros/t20067.nim b/tests/macros/t20067.nim new file mode 100644 index 000000000..0ee3a4712 --- /dev/null +++ b/tests/macros/t20067.nim @@ -0,0 +1,28 @@ +discard """ + output: ''' +b.defaultVal = foo +$c.defaultVal = bar +''' +""" + +import macros + +# #18976 + +macro getString(identifier): string = + result = newLit($identifier) +doAssert getString(abc) == "abc" +doAssert getString(`a b c`) == "abc" + +# #20067 + +template defaultVal*(value : typed) {.pragma.} + +type A = ref object + b {.defaultVal: "foo".}: string + `$c` {.defaultVal: "bar".}: string + +let a = A(b: "a", `$c`: "b") + +echo "b.defaultVal = " & a.b.getCustomPragmaVal(defaultVal) +echo "$c.defaultVal = " & a.`$c`.getCustomPragmaVal(defaultVal) diff --git a/tests/macros/t20435.nim b/tests/macros/t20435.nim new file mode 100644 index 000000000..824282198 --- /dev/null +++ b/tests/macros/t20435.nim @@ -0,0 +1,30 @@ + +#[ + A better test requires matching, so the use of @ working can be showcased + For example: + + proc regularCase[T]() = + case [(1, 3), (3, 4)]: + of [(1, @a), (_, @b)]: + echo a, b + else: discard +]# + +{.experimental: "caseStmtMacros".} + +import macros + +type Foo = object + +macro `case`(obj: Foo) = quote do: discard + +proc notGeneric() = + case Foo() + of a b c d: discard + +proc generic[T]() = + case Foo() + of a b c d: discard + +notGeneric() +generic[int]() diff --git a/tests/macros/t21593.nim b/tests/macros/t21593.nim new file mode 100644 index 000000000..b0b7ebe75 --- /dev/null +++ b/tests/macros/t21593.nim @@ -0,0 +1,13 @@ +discard """ +nimout: ''' +StmtList + UIntLit 18446744073709551615 + IntLit -1''' +""" + +import macros + +dumpTree: + 0xFFFFFFFF_FFFFFFFF'u + 0xFFFFFFFF_FFFFFFFF + diff --git a/tests/macros/t23032_1.nim b/tests/macros/t23032_1.nim new file mode 100644 index 000000000..4e1707414 --- /dev/null +++ b/tests/macros/t23032_1.nim @@ -0,0 +1,19 @@ +import std/macros + +type A[T, H] = object + +proc `%*`(a: A): bool = true +proc `%*`[T](a: A[int, T]): bool = false + +macro collapse(s: untyped) = + result = newStmtList() + result.add quote do: + doAssert(`s`(A[float, int]()) == true) + +macro startHere(n: untyped): untyped = + result = newStmtList() + let s = n[0] + result.add quote do: + `s`.collapse() + +startHere(`a` %* `b`) diff --git a/tests/macros/t23032_2.nim b/tests/macros/t23032_2.nim new file mode 100644 index 000000000..8dde29e10 --- /dev/null +++ b/tests/macros/t23032_2.nim @@ -0,0 +1,20 @@ +discard """ + action: "reject" + errormsg: "ambiguous identifier: '%*'" +""" +import std/macros + +type A[T, H] = object + +proc `%*`[T](a: A) = discard +proc `%*`[T](a: A[int, T]) = discard + +macro collapse(s: typed) = discard + +macro startHere(n: untyped): untyped = + result = newStmtList() + let s = n[0] + result.add quote do: + collapse(`s`.typeof()) + +startHere(`a` %* `b`) diff --git a/tests/macros/t23547.nim b/tests/macros/t23547.nim new file mode 100644 index 000000000..9a2bff9ff --- /dev/null +++ b/tests/macros/t23547.nim @@ -0,0 +1,23 @@ +# https://github.com/nim-lang/Nim/issues/23547 + +type + A[T] = object + x: T + +proc mulCheckSparse[F](dummy: var A[F], xmulchecksparse: static A[F]) = + static: + echo "mulCheckSparse: ", typeof(dummy), ", ", typeof(xmulchecksparse) # when generic params not specified: A[system.int], A + +template sumImpl(xsumimpl: typed) = + static: + echo "sumImpl: ", typeof(xsumimpl) # A + var a = A[int](x: 55) + mulCheckSparse(a, xsumimpl) # fails here + +proc sum[T](xsum: static T) = + static: + echo "sum: ", typeof(xsum) # A[system.int] + sumImpl(xsum) + +const constA = A[int](x : 100) +sum[A[int]](constA) diff --git a/tests/macros/t23784.nim b/tests/macros/t23784.nim new file mode 100644 index 000000000..31b4544c6 --- /dev/null +++ b/tests/macros/t23784.nim @@ -0,0 +1,157 @@ +discard """ + joinable: false +""" + + +# debug ICE: genCheckedRecordField +# apparently after https://github.com/nim-lang/Nim/pull/23477 + +# bug #23784 + +import std/bitops, std/macros + +# -------------------------------------------------------------- + +type Algebra = enum + BN254_Snarks + +type SecretWord* = distinct uint64 +const WordBitWidth* = sizeof(SecretWord) * 8 + +func wordsRequired*(bits: int): int {.inline.} = + const divShiftor = fastLog2(WordBitWidth) + result = (bits + WordBitWidth - 1) shr divShiftor + +type + BigInt*[bits: static int] = object + limbs*: array[bits.wordsRequired, SecretWord] # <--- crash points to here + +# -------------------------------------------------------------- + +const CurveBitWidth = [ + BN254_Snarks: 254 +] + +const BN254_Snarks_Modulus = BigInt[254](limbs: [SecretWord 0x1, SecretWord 0x2, SecretWord 0x3, SecretWord 0x4]) +const BN254_Snarks_Order = BigInt[254](limbs: [SecretWord 0x1, SecretWord 0x1, SecretWord 0x2, SecretWord 0x2]) + +func montyOne*(M: BigInt[254]): BigInt[254] = + ## Returns "1 (mod M)" in the Montgomery domain. + ## This is equivalent to R (mod M) in the natural domain + BigInt[254](limbs: [SecretWord 0x1, SecretWord 0x1, SecretWord 0x1, SecretWord 0x1]) + + +{.experimental: "dynamicBindSym".} + +type + DerivedConstantMode* = enum + kModulus + kOrder + +macro genDerivedConstants*(mode: static DerivedConstantMode): untyped = + ## Generate constants derived from the main constants + ## + ## For example + ## - the Montgomery magic constant "R^2 mod N" in ROM + ## For each curve under the private symbol "MyCurve_R2modP" + ## - the Montgomery magic constant -1/P mod 2^Wordbitwidth + ## For each curve under the private symbol "MyCurve_NegInvModWord + ## - ... + + # Now typedesc are NimNode and there is no way to translate + # NimNode -> typedesc easily so we can't + # "for curve in low(Curve) .. high(Curve):" + # As an ugly workaround, we count + # The item at position 0 is a pragma + result = newStmtList() + + template used(name: string): NimNode = + nnkPragmaExpr.newTree( + ident(name), + nnkPragma.newTree(ident"used") + ) + + let ff = if mode == kModulus: "_Fp" else: "_Fr" + + for curveSym in low(Algebra) .. high(Algebra): + let curve = $curveSym + let M = if mode == kModulus: bindSym(curve & "_Modulus") + else: bindSym(curve & "_Order") + + # const MyCurve_montyOne = montyOne(MyCurve_Modulus) + result.add newConstStmt( + used(curve & ff & "_MontyOne"), newCall( + bindSym"montyOne", + M + ) + ) + +# -------------------------------------------------------------- + +{.experimental: "dynamicBindSym".} + +genDerivedConstants(kModulus) +genDerivedConstants(kOrder) + +proc bindConstant(ff: NimNode, property: string): NimNode = + # Need to workaround https://github.com/nim-lang/Nim/issues/14021 + # which prevents checking if a type FF[Name] = Fp[Name] or Fr[Name] + # was instantiated with Fp or Fr. + # getTypeInst only returns FF and sameType doesn't work. + # so quote do + when checks. + let T = getTypeInst(ff) + T.expectKind(nnkBracketExpr) + doAssert T[0].eqIdent("typedesc") + + let curve = + if T[1].kind == nnkBracketExpr: # typedesc[Fp[BLS12_381]] as used internally + # doAssert T[1][0].eqIdent"Fp" or T[1][0].eqIdent"Fr", "Found ident: '" & $T[1][0] & "' instead of 'Fp' or 'Fr'" + T[1][1].expectKind(nnkIntLit) # static enum are ints in the VM + $Algebra(T[1][1].intVal) + else: # typedesc[bls12381_fp] alias as used for C exports + let T1 = getTypeInst(T[1].getImpl()[2]) + if T1.kind != nnkBracketExpr or + T1[1].kind != nnkIntLit: + echo T.repr() + echo T1.repr() + echo getTypeInst(T1).treerepr() + error "getTypeInst didn't return the full instantiation." & + " Dealing with types in macros is hard, complain at https://github.com/nim-lang/RFCs/issues/44" + $Algebra(T1[1].intVal) + + let curve_fp = bindSym(curve & "_Fp_" & property) + let curve_fr = bindSym(curve & "_Fr_" & property) + result = quote do: + when `ff` is Fp: + `curve_fp` + elif `ff` is Fr: + `curve_fr` + else: + {.error: "Unreachable, received type: " & $`ff`.} + +# -------------------------------------------------------------- + +template matchingBigInt*(Name: static Algebra): untyped = + ## BigInt type necessary to store the prime field Fp + # Workaround: https://github.com/nim-lang/Nim/issues/16774 + # as we cannot do array accesses in type section. + # Due to generic sandwiches, it must be exported. + BigInt[CurveBitWidth[Name]] + +type + Fp*[Name: static Algebra] = object + mres*: matchingBigInt(Name) + +macro getMontyOne*(ff: type Fp): untyped = + ## Get one in Montgomery representation (i.e. R mod P) + result = bindConstant(ff, "MontyOne") + +func getOne*(T: type Fp): T {.noInit, inline.} = + result = cast[ptr T](unsafeAddr getMontyOne(T))[] + +# -------------------------------------------------------------- +proc foo(T: Fp) = + discard T + +let a = Fp[BN254_Snarks].getOne() +foo(a) # oops this was a leftover that broke the bisect. diff --git a/tests/macros/t7875.nim b/tests/macros/t7875.nim new file mode 100644 index 000000000..7b6e47b86 --- /dev/null +++ b/tests/macros/t7875.nim @@ -0,0 +1,22 @@ +discard """ + nimout: "var mysym`gensym0: MyType[float32]" + joinable: false +""" + +import macros + +type + MyType[T] = object + +# this is totally fine +var mysym: MyType[float32] + +macro foobar(): untyped = + let floatSym = bindSym"float32" + + result = quote do: + var mysym: MyType[`floatSym`] + + echo result.repr + +foobar() diff --git a/tests/macros/tastrepr.nim b/tests/macros/tastrepr.nim index e4271f019..96a37c7a2 100644 --- a/tests/macros/tastrepr.nim +++ b/tests/macros/tastrepr.nim @@ -1,5 +1,6 @@ discard """ output: ''' + var data = @[(1, "one"), (2, "two")] for (i, d) in pairs(data): discard @@ -7,7 +8,11 @@ for i, d in pairs(data): discard for i, (x, y) in pairs(data): discard -var (a, b) = (1, 2) +var + a = 1 + b = 2 +type + A* = object var data = @[(1, "one"), (2, "two")] for (i, d) in pairs(data): @@ -17,6 +22,11 @@ for i, d in pairs(data): for i, (x, y) in pairs(data): discard var (a, b) = (1, 2) +type + A* = object + +var t04 = 1.0'f128 +t04 = 2.0'f128 ''' """ @@ -41,3 +51,8 @@ echoTypedAndUntypedRepr: for i, (x,y) in pairs(data): discard var (a,b) = (1,2) + type A* = object # issue #22933 + +echoUntypedRepr: + var t04 = 1'f128 + t04 = 2'f128 diff --git a/tests/macros/tcasestmtmacro.nim b/tests/macros/tcasestmtmacro.nim new file mode 100644 index 000000000..32019a92a --- /dev/null +++ b/tests/macros/tcasestmtmacro.nim @@ -0,0 +1,33 @@ +discard """ + output: ''' +yes +''' +""" + +import macros + +macro `case`(n: tuple): untyped = + result = newTree(nnkIfStmt) + let selector = n[0] + for i in 1 ..< n.len: + let it = n[i] + case it.kind + of nnkElse, nnkElifBranch, nnkElifExpr, nnkElseExpr: + result.add it + of nnkOfBranch: + for j in 0..it.len-2: + let cond = newCall("==", selector, it[j]) + result.add newTree(nnkElifBranch, cond, it[^1]) + else: + error "custom 'case' for tuple cannot handle this node", it + +var correct = false + +case ("foo", 78) +of ("foo", 78): + correct = true + echo "yes" +of ("bar", 88): echo "no" +else: discard + +doAssert correct diff --git a/tests/macros/tclosuremacro.nim b/tests/macros/tclosuremacro.nim index 5c41c317a..44c2411a5 100644 --- a/tests/macros/tclosuremacro.nim +++ b/tests/macros/tclosuremacro.nim @@ -5,8 +5,6 @@ calling mystuff yes calling mystuff yes -calling sugarWithPragma -sugarWithPragma called ''' """ @@ -40,7 +38,7 @@ proc pass2(f: (int, int) -> int): (int) -> int = doAssert pass2((x, y) => x + y)(4) == 6 -fun(x, y: int) {.noSideEffect.} => x + y +const fun = (x, y: int) {.noSideEffect.} => x + y doAssert typeof(fun) is (proc (x, y: int): int {.nimcall.}) doAssert fun(3, 4) == 7 @@ -70,4 +68,13 @@ m: proc mystuff() = echo "yes" -sugarWithPragma() {.m.} => echo "sugarWithPragma called" +const typedParamAndPragma = (x, y: int) -> int => x + y +doAssert typedParamAndPragma(1, 2) == 3 + +type + Bot = object + call: proc (): string {.noSideEffect.} + +var myBot = Bot() +myBot.call = () {.noSideEffect.} => "I'm a bot." +doAssert myBot.call() == "I'm a bot." diff --git a/tests/macros/tdumpast.nim b/tests/macros/tdumpast.nim index b9d224ab8..484b3c2f3 100644 --- a/tests/macros/tdumpast.nim +++ b/tests/macros/tdumpast.nim @@ -2,34 +2,69 @@ import macros -template plus(a, b: untyped): untyped {.dirty} = - a + b +block: + template plus(a, b: untyped): untyped {.dirty} = + a + b -macro call(e: untyped): untyped = - result = newCall("foo", newStrLitNode("bar")) + macro call(e: untyped): untyped = + result = newCall("foo", newStrLitNode("bar")) -macro dumpAST(n: untyped): untyped = - # dump AST as a side-effect and return the inner node - let n = callsite() - echo n.lispRepr - echo n.treeRepr + macro dumpAST(n: untyped): string = + var msg = "" + msg.add "lispRepr:\n" & n.lispRepr & "\n" + msg.add "treeRepr:\n" & n.treeRepr & "\n" - var plusAst = getAst(plus(1, 2)) - echo plusAst.lispRepr + var plusAst = getAst(plus(1, 2)) + msg.add "lispRepr:\n" & n.lispRepr & "\n" - var callAst = getAst(call(4)) - echo callAst.lispRepr + var callAst = getAst(call(4)) + msg.add "callAst.lispRepr:\n" & callAst.lispRepr & "\n" - var e = parseExpr("foo(bar + baz)") - echo e.lispRepr + var e = parseExpr("foo(bar + baz)") + msg.add "e.lispRepr:\n" & e.lispRepr & "\n" + result = msg.newLit - result = n[1] + let a = dumpAST: + proc add(x, y: int): int = + return x + y + const foo = 3 -dumpAST: - proc add(x, y: int): int = - return x + y - - proc sub(x, y: int): int = return x - y + doAssert a == """ +lispRepr: +(StmtList (ProcDef (Ident "add") (Empty) (Empty) (FormalParams (Ident "int") (IdentDefs (Ident "x") (Ident "y") (Ident "int") (Empty))) (Empty) (Empty) (StmtList (ReturnStmt (Infix (Ident "+") (Ident "x") (Ident "y"))))) (ConstSection (ConstDef (Ident "foo") (Empty) (IntLit 3)))) +treeRepr: +StmtList + ProcDef + Ident "add" + Empty + Empty + FormalParams + Ident "int" + IdentDefs + Ident "x" + Ident "y" + Ident "int" + Empty + Empty + Empty + StmtList + ReturnStmt + Infix + Ident "+" + Ident "x" + Ident "y" + ConstSection + ConstDef + Ident "foo" + Empty + IntLit 3 +lispRepr: +(StmtList (ProcDef (Ident "add") (Empty) (Empty) (FormalParams (Ident "int") (IdentDefs (Ident "x") (Ident "y") (Ident "int") (Empty))) (Empty) (Empty) (StmtList (ReturnStmt (Infix (Ident "+") (Ident "x") (Ident "y"))))) (ConstSection (ConstDef (Ident "foo") (Empty) (IntLit 3)))) +callAst.lispRepr: +(Call (Ident "foo") (StrLit "bar")) +e.lispRepr: +(Call (Ident "foo") (Infix (Ident "+") (Ident "bar") (Ident "baz"))) +""" macro fun() = let n = quote do: @@ -48,3 +83,79 @@ macro fun3(): untyped = int | float | array | seq | object | ptr | pointer | float32 doAssert n.repr == "int | float | array | seq | object | ptr | pointer | float32", n.repr fun3() + +macro fun4() = + let n = quote do: + (a: 1) + doAssert n.repr == "(a: 1)", n.repr +fun4() + +# nkTupleConstr vs nkPar tests: +block: # lispRepr + macro lispRepr2(a: untyped): string = newLit a.lispRepr + + doAssert lispRepr2(()) == """(TupleConstr)""" + doAssert lispRepr2((a: 1)) == """(TupleConstr (ExprColonExpr (Ident "a") (IntLit 1)))""" + doAssert lispRepr2((a: 1, b: 2)) == """(TupleConstr (ExprColonExpr (Ident "a") (IntLit 1)) (ExprColonExpr (Ident "b") (IntLit 2)))""" + doAssert lispRepr2((1,)) == """(TupleConstr (IntLit 1))""" + doAssert lispRepr2((1, 2)) == """(TupleConstr (IntLit 1) (IntLit 2))""" + doAssert lispRepr2((1, 2, 3.0)) == """(TupleConstr (IntLit 1) (IntLit 2) (FloatLit 3.0))""" + doAssert lispRepr2((1)) == """(Par (IntLit 1))""" + doAssert lispRepr2((1+2)) == """(Par (Infix (Ident "+") (IntLit 1) (IntLit 2)))""" + +block: # repr + macro repr2(a: untyped): string = newLit a.repr + + doAssert repr2(()) == "()" + doAssert repr2((a: 1)) == "(a: 1)" + doAssert repr2((a: 1, b: 2)) == "(a: 1, b: 2)" + doAssert repr2((1,)) == "(1,)" + doAssert repr2((1, 2)) == "(1, 2)" + doAssert repr2((1, 2, 3.0)) == "(1, 2, 3.0)" + doAssert repr2((1)) == "(1)" + doAssert repr2((1+2)) == "(1 + 2)" + +block: # treeRepr + macro treeRepr2(a: untyped): string = newLit a.treeRepr + macro treeRepr3(a: typed): string = newLit a.treeRepr + + doAssert treeRepr2(1+1 == 2) == """ +Infix + Ident "==" + Infix + Ident "+" + IntLit 1 + IntLit 1 + IntLit 2""" + + proc baz() = discard + proc baz(a: int) = discard + proc baz(a: float) = discard + + doAssert treeRepr3(baz()) == """ +Call + Sym "baz"""" + + let a = treeRepr3(block: + proc bar(a: auto) = baz()) + doAssert a == """ +BlockStmt + Empty + ProcDef + Sym "bar" + Empty + GenericParams + Sym "a:type" + FormalParams + Empty + IdentDefs + Sym "a" + Sym "auto" + Empty + Empty + Bracket + Empty + Empty + StmtList + Call + OpenSymChoice 3 "baz"""" diff --git a/tests/macros/tfail_parse.nim b/tests/macros/tfail_parse.nim new file mode 100644 index 000000000..1925f2b69 --- /dev/null +++ b/tests/macros/tfail_parse.nim @@ -0,0 +1,15 @@ +discard """ +action: "reject" +cmd: "nim check $file" +errormsg: "expected expression, but got multiple statements [ValueError]" +file: "macros.nim" +""" + +import macros +static: + discard parseStmt("'") + discard parseExpr("'") + discard parseExpr(""" +proc foo() +proc foo() = discard +""") diff --git a/tests/macros/tforloop_macro1.nim b/tests/macros/tforloop_macro1.nim index 49918563d..a8f45c7ac 100644 --- a/tests/macros/tforloop_macro1.nim +++ b/tests/macros/tforloop_macro1.nim @@ -12,7 +12,7 @@ discard """ """ import macros -{.experimental: "forLoopMacros".} + macro mymacro(): untyped = result = newLit([1, 2, 3]) diff --git a/tests/macros/tgetimpl.nim b/tests/macros/tgetimpl.nim index d231a4336..ab33131b0 100644 --- a/tests/macros/tgetimpl.nim +++ b/tests/macros/tgetimpl.nim @@ -1,5 +1,5 @@ discard """ - nimout: '''"muhaha" + nimout: '''foo = "muhaha" proc poo(x, y: int) = let y = x echo ["poo"]''' @@ -44,10 +44,11 @@ static: doAssert checkOwner(poo, 2) == "nskProc" doAssert checkOwner(poo, 3) == "nskModule" doAssert isSameOwner(foo, poo) - doAssert isSameOwner(foo, echo) == false - doAssert isSameOwner(poo, len) == false - -#--------------------------------------------------------------- + proc wrappedScope() = + proc dummyproc() = discard + doAssert isSameOwner(foo, dummyproc) == false + doAssert isSameOwner(poo, dummyproc) == false + wrappedScope() macro check_gen_proc(ex: typed): (bool, bool) = let lenChoice = bindsym"len" @@ -65,3 +66,38 @@ macro check_gen_proc(ex: typed): (bool, bool) = let a = @[1,2,3] assert: check_gen_proc(len(a)) == (false, true) + +#--------------------------------------------------------------- +# issue #16110 + +macro check(x: type): untyped = + let z = getType(x) + let y = getImpl(z[1]) + var sym = y[0] + if sym.kind == nnkPragmaExpr: sym = sym[0] + if sym.kind == nnkPostfix: sym = sym[1] + expectKind(z[1], nnkSym) + expectKind(sym, nnkSym) + expectKind(y[2], nnkObjectTy) + doAssert(sym == z[1]) + +type + TirePtr = ptr object + code: int + + TireRef* = ref object + code: int + + TireRef2* {.inheritable.} = ref object + code: int + + TireRef3* {.inheritable.} = object + code: int + +var z1: TirePtr +check(typeof(z1[])) +var z2: TireRef +check(typeof(z2[])) +var z3: TireRef2 +check(typeof(z3[])) +check(TireRef3) diff --git a/tests/macros/tgetraiseslist.nim b/tests/macros/tgetraiseslist.nim new file mode 100644 index 000000000..79694a66f --- /dev/null +++ b/tests/macros/tgetraiseslist.nim @@ -0,0 +1,29 @@ +discard """ + nimout: '''##[ValueError, Gen[string]]## +%%[RootEffect]%% +true true''' +""" + +import macros +import std / effecttraits + +type + Gen[T] = object of CatchableError + x: T + +macro m(call: typed): untyped = + echo "##", repr getRaisesList(call[0]), "##" + echo "%%", repr getTagsList(call[0]), "%%" + echo isGcSafe(call[0]), " ", hasNoSideEffects(call[0]) + result = call + +proc gutenTag() {.tags: RootEffect.} = discard + +proc r(inp: int) = + if inp == 0: + raise newException(ValueError, "bah") + elif inp == 1: + raise newException(Gen[string], "bahB") + gutenTag() + +m r(2) diff --git a/tests/macros/tgettype.nim b/tests/macros/tgettype.nim index bd70a1c30..d91efe1fe 100644 --- a/tests/macros/tgettype.nim +++ b/tests/macros/tgettype.nim @@ -1,29 +1,85 @@ -discard """ -output: ''' -(ObjectTy (Empty) (Sym "Model") (RecList (Sym "name") (Sym "password"))) -(BracketExpr (Sym "typeDesc") (Sym "User")) -''' -""" -import macros +import std/macros +import stdtest/testutils -type - Model = object of RootObj - User = object of Model - name : string - password : string +# getType -macro testUser: string = - result = newLit(User.getType.lispRepr) +block: + type + Model = object of RootObj + User = object of Model + name : string + password : string -macro testGeneric(T: typedesc[Model]): string= - result = newLit(T.getType.lispRepr) + macro testUser: string = + result = newLit(User.getType.lispRepr) -echo testUser -echo User.testGeneric + macro testGeneric(T: typedesc[Model]): string= + result = newLit(T.getType.lispRepr) -macro assertVoid(e: typed): untyped = - assert(getTypeInst(e).typeKind == ntyVoid) + doAssert testUser == """(ObjectTy (Empty) (Sym "Model") (RecList (Sym "name") (Sym "password")))""" + doAssert User.testGeneric == """(BracketExpr (Sym "typeDesc") (Sym "User"))""" -proc voidProc() = discard + macro assertVoid(e: typed): untyped = + assert(getTypeInst(e).typeKind == ntyVoid) -assertVoid voidProc() + proc voidProc() = discard + + assertVoid voidProc() + +block: + # refs #18220; not an actual solution (yet) but at least shows what's currently + # possible + + type Callable1[R, T, U] = concept fn + fn(default(T)) is R + fn is U + + # note that typetraits.arity doesn't work + macro arity(a: typed): int = + # number of params + # this is not production code! + let a2 = a.getType[1] # this used to crash nim, with: `vmdeps.nim(292, 25) `false`` + newLit a2.len - 1 + + type Callable2[R, T, U] = concept fn + fn(default(T)) is R + fn is U + arity(U) == 2 + + proc map1[T, R, U](a: T, fn: Callable1[R, T, U]): R = + let fn = U(fn) + # `cast[U](fn)` would also work; + # this is currently needed otherwise, sigmatch errors with: + # Error: attempting to call routine: 'fn' + # found 'fn' [param declared in tgettype.nim(53, 28)] + # this can be fixed in future work + fn(a) + + proc map2[T, R, U](a: T, fn: Callable2[R, T, U]): R = + let fn = U(fn) + fn(a) + + proc fn1(a: int, a2 = 'x'): string = $(a, a2, "fn1") + proc fn2(a: int, a2 = "zoo"): string = $(a, a2, "fn2") + proc fn3(a: int, a2 = "zoo2"): string = $(a, a2, "fn3") + proc fn4(a: int): string {.inline.} = $(a, "fn4") + proc fn5(a: int): string = $(a, "fn5") + + assertAll: + # Callable1 + 1.map1(fn1) == """(1, 'x', "fn1")""" + 1.map1(fn2) == """(1, "zoo", "fn2")""" + 1.map1(fn3) == """(1, "zoo", "fn3")""" + # fn3's optional param is not honored, because fn3 and fn2 yield same + # generic instantiation; this is a caveat with this approach + # There are several possible ways to improve things in future work. + 1.map1(fn4) == """(1, "fn4")""" + 1.map1(fn5) == """(1, "fn5")""" + + # Callable2; prevents passing procs with optional params to avoid above + # mentioned caveat, but more restrictive + not compiles(1.map2(fn1)) + not compiles(1.map2(fn2)) + not compiles(1.map2(fn3)) + 1.map2(fn4) == """(1, "fn4")""" + 1.map2(fn5) == """(1, "fn5")""" diff --git a/tests/macros/tgettypeinst.nim b/tests/macros/tgettypeinst.nim index 2f9557160..e722f14aa 100644 --- a/tests/macros/tgettypeinst.nim +++ b/tests/macros/tgettypeinst.nim @@ -191,14 +191,14 @@ template myAttr3() {.pragma.} template serializationKey(key: string) {.pragma.} type - MyObj = object {.packed,myAttr,serializationKey: "one".} + MyObj {.packed,myAttr,serializationKey: "one".} = object myField {.myAttr2,serializationKey: "two".}: int myField2 {.myAttr3,serializationKey: "three".}: float # field pragmas not currently supported test(MyObj): type - _ = object {.packed,myAttr,serializationKey: "one".} + _ {.packed,myAttr,serializationKey: "one".} = object myField: int myField2: float diff --git a/tests/macros/tgettypeinst7737.nim b/tests/macros/tgettypeinst7737.nim new file mode 100644 index 000000000..e49f82562 --- /dev/null +++ b/tests/macros/tgettypeinst7737.nim @@ -0,0 +1,61 @@ +discard """ + nimout: ''' +seq[int] +CustomSeq[int] +''' +""" + +import macros, typetraits, sequtils + +block: # issue #7737 original + type + CustomSeq[T] = object + data: seq[T] + + proc getSubType(T: NimNode): NimNode = + echo getTypeInst(T).repr + result = getTypeInst(T)[1] + + macro typed_helper(x: varargs[typed]): untyped = + let foo = getSubType(x[0]) + result = quote do: discard + + macro untyped_heavylifting(x: varargs[untyped]): untyped = + var containers = nnkArgList.newTree() + for arg in x: + case arg.kind: + of nnkInfix: + if eqIdent(arg[0], "in"): + containers.add arg[2] + else: + discard + result = quote do: + typed_helper(`containers`) + var a, b, c: seq[int] + untyped_heavylifting z in c, x in a, y in b: + discard + ## The following gives me CustomSeq instead + ## of CustomSeq[int] in getTypeInst + var u, v, w: CustomSeq[int] + untyped_heavylifting z in u, x in v, y in w: + discard + +block: # issue #7737 comment + type + CustomSeq[T] = object + data: seq[T] + # when using just one argument, `foo` and `bar` should be exactly + # identical. + macro foo(arg: typed): string = + result = newLit(arg.getTypeInst.repr) + macro bar(args: varargs[typed]): untyped = + result = newTree(nnkBracket) + for arg in args: + result.add newLit(arg.getTypeInst.repr) + var + a: seq[int] + b: CustomSeq[int] + doAssert foo(a) == "seq[int]" + doAssert bar(a) == ["seq[int]"] + doAssert foo(b) == "CustomSeq[int]" + doAssert bar(b) == ["CustomSeq[int]"] diff --git a/tests/macros/tmacro2.nim b/tests/macros/tmacro2.nim index 72972c0c1..1ad63ec8c 100644 --- a/tests/macros/tmacro2.nim +++ b/tests/macros/tmacro2.nim @@ -26,3 +26,11 @@ const t = mac("HEllo World") echo s, " ", t +#----------------------------------------------------------------------------- +# issue #15326 +macro m(n:typed):auto = + result = n + +proc f[T](x:T): T {.m.} = x + +discard f(3) \ No newline at end of file diff --git a/tests/macros/tmacrogetimpl.nim b/tests/macros/tmacrogetimpl.nim new file mode 100644 index 000000000..1d996ff29 --- /dev/null +++ b/tests/macros/tmacrogetimpl.nim @@ -0,0 +1,31 @@ +import macros + +# bug #5034 + +macro copyImpl(srsProc: typed, toSym: untyped) = + result = copyNimTree(getImplTransformed(srsProc)) + result[0] = ident $toSym.toStrLit() + +proc foo1(x: float, one: bool = true): float = + if one: + return 1'f + result = x + +proc bar1(what: string): string = + ## be a little more adversarial with `skResult` + proc buzz: string = + result = "lightyear" + if what == "buzz": + result = "buzz " & buzz() + else: + result = what + return result + +copyImpl(foo1, foo2) +doAssert foo1(1'f) == 1.0 +doAssert foo2(10.0, false) == 10.0 +doAssert foo2(10.0) == 1.0 + +copyImpl(bar1, bar2) +doAssert bar1("buzz") == "buzz lightyear" +doAssert bar1("macros") == "macros" diff --git a/tests/macros/tmacros1.nim b/tests/macros/tmacros1.nim index 53af4287c..c588ff7e6 100644 --- a/tests/macros/tmacros1.nim +++ b/tests/macros/tmacros1.nim @@ -60,3 +60,22 @@ macro foo(t: static Tuple): untyped = doAssert t.b == 12345 foo((a: "foo", b: 12345)) + + +# bug #16307 + +macro bug(x: untyped): string = + newLit repr(x) + +let res = bug: + block: + ## one + ## two + ## three + +doAssert res == """ + +block: + ## one + ## two + ## three""" diff --git a/tests/macros/tmacros_issues.nim b/tests/macros/tmacros_issues.nim index df385c445..a81c51658 100644 --- a/tests/macros/tmacros_issues.nim +++ b/tests/macros/tmacros_issues.nim @@ -19,6 +19,8 @@ nil 42 false true +@[i0, i1, i2, i3, i4] +@[tmp, tmp, tmp, tmp, tmp] ''' output: ''' @@ -27,6 +29,18 @@ array[0 .. 100, int] 10 test 0o377'i8 +0o000000000755'i32 +1 +2 +3 +foo1 +foo2 +foo3 +true +false +true +false +1.0 ''' """ @@ -50,7 +64,7 @@ block t7723: block t8706: macro varargsLen(args:varargs[untyped]): untyped = - doAssert args.kind == nnkArglist + doAssert args.kind == nnkArgList doAssert args.len == 0 result = newLit(args.len) @@ -244,3 +258,264 @@ macro toRendererBug(n): untyped = result = newLit repr(n) echo toRendererBug(0o377'i8) +echo toRendererBug(0o755'i32) + +# bug #12129 +macro foobar() = + var loopVars = newSeq[NimNode](5) + for i, sym in loopVars.mpairs(): + sym = ident("i" & $i) + echo loopVars + for sym in loopVars.mitems(): + sym = ident("tmp") + echo loopVars + +foobar() + + +# bug #13253 +import macros + +type + FooBar = object + a: seq[int] + +macro genFoobar(a: static FooBar): untyped = + result = newStmtList() + for b in a.a: + result.add(newCall(bindSym"echo", newLit b)) + +proc foobar(a: static FooBar) = + genFoobar(a) # removing this make it work + for b in a.a: + echo "foo" & $b + +proc main() = + const a: seq[int] = @[1, 2,3] + # Error: type mismatch: got <array[0..2, int]> but expected 'seq[int]' + const fb = Foobar(a: a) + foobar(fb) +main() + +# bug #13484 + +proc defForward(id, nid: NimNode): NimNode = + result = newProc(id, @[newIdentNode("bool"), newIdentDefs(nid, newIdentNode("int"))], body=newEmptyNode()) + +proc defEven(evenid, oddid, nid: NimNode): NimNode = + result = quote do: + proc `evenid`(`nid`: int): bool = + if `nid` == 0: + return true + else: + return `oddid`(`nid` - 1) + +proc defOdd(evenid, oddid, nid: NimNode): NimNode = + result = quote do: + proc `oddid`(`nid`: int): bool = + if `nid` == 0: + return false + else: + return `evenid`(`nid` - 1) + +proc callNode(funid, param: NimNode): NimNode = + result = quote do: + `funid`(`param`) + +macro testEvenOdd3(): untyped = + let + evenid = newIdentNode("even3") + oddid = newIdentNode("odd3") + nid = newIdentNode("n") + oddForward = defForward(oddid, nid) + even = defEven(evenid, oddid, nid) + odd = defOdd(evenid, oddid, nid) + callEven = callNode(evenid, newLit(42)) + callOdd = callNode(oddid, newLit(42)) + result = quote do: + `oddForward` + `even` + `odd` + echo `callEven` + echo `callOdd` + +macro testEvenOdd4(): untyped = + let + evenid = newIdentNode("even4") + oddid = newIdentNode("odd4") + nid = newIdentNode("n") + oddForward = defForward(oddid, nid) + even = defEven(evenid, oddid, nid) + odd = defOdd(evenid, oddid, nid) + callEven = callNode(evenid, newLit(42)) + callOdd = callNode(oddid, newLit(42)) + # rewrite the body of proc node. + oddForward[6] = newStmtList() + result = quote do: + `oddForward` + `even` + `odd` + echo `callEven` + echo `callOdd` + +macro testEvenOdd5(): untyped = + let + evenid = genSym(nskProc, "even5") + oddid = genSym(nskProc, "odd5") + nid = newIdentNode("n") + oddForward = defForward(oddid, nid) + even = defEven(evenid, oddid, nid) + odd = defOdd(evenid, oddid, nid) + callEven = callNode(evenid, newLit(42)) + callOdd = callNode(oddid, newLit(42)) + result = quote do: + `oddForward` + `even` + `odd` + echo `callEven` + echo `callOdd` + +macro testEvenOdd6(): untyped = + let + evenid = genSym(nskProc, "even6") + oddid = genSym(nskProc, "odd6") + nid = newIdentNode("n") + oddForward = defForward(oddid, nid) + even = defEven(evenid, oddid, nid) + odd = defOdd(evenid, oddid, nid) + callEven = callNode(evenid, newLit(42)) + callOdd = callNode(oddid, newLit(42)) + # rewrite the body of proc node. + oddForward[6] = newStmtList() + result = quote do: + `oddForward` + `even` + `odd` + echo `callEven` + echo `callOdd` + +# it works +testEvenOdd3() + +# it causes an error (redefinition of odd4), which is correct +assert not compiles testEvenOdd4() + +# it caused an error (still forwarded: odd5) +testEvenOdd5() + +# it works, because the forward decl and definition share the symbol and the compiler is forgiving here +#testEvenOdd6() #Don't test it though, the compiler may become more strict in the future + +# bug #15385 +var captured_funcs {.compileTime.}: seq[NimNode] = @[] + +macro aad*(fns: varargs[typed]): typed = + result = newStmtList() + for fn in fns: + captured_funcs.add fn[0] + result.add fn + +func exp*(x: float): float ## get different error if you remove forward declaration + +func exp*(x: float): float {.aad.} = + var x1 = min(max(x, -708.4), 709.8) + var result: float ## looks weird because it is taken from template expansion + result = x1 + 1.0 + result + +template check_accuracy(f: untyped, rng: Slice[float], n: int, verbose = false): auto = + + proc check_accuracy: tuple[avg_ulp: float, max_ulp: int] {.gensym.} = + let k = (rng.b - rng.a) / (float) n + var + res, x: float + i, max_ulp = 0 + avg_ulp = 0.0 + + x = rng.a + while (i < n): + res = f(x) + i.inc + x = x + 0.001 + (avg_ulp, max_ulp) + check_accuracy() + +discard check_accuracy(exp, -730.0..709.4, 4) + +# And without forward decl +macro aad2*(fns: varargs[typed]): typed = + result = newStmtList() + for fn in fns: + captured_funcs.add fn[0] + result.add fn + +func exp2*(x: float): float {.aad2.} = + var x1 = min(max(x, -708.4), 709.8) + var result: float ## looks weird because it is taken from template expansion + result = x1 + 1.0 + result + +template check_accuracy2(f: untyped, rng: Slice[float], n: int, verbose = false): auto = + + proc check_accuracy2: tuple[avg_ulp: float, max_ulp: int] {.gensym.} = + let k = (rng.b - rng.a) / (float) n + var + res, x: float + i, max_ulp = 0 + avg_ulp = 0.0 + + x = rng.a + while (i < n): + res = f(x) + i.inc + x = x + 0.001 + (avg_ulp, max_ulp) + check_accuracy2() + +discard check_accuracy2(exp2, -730.0..709.4, 4) + +# And minimized: +macro aadMin(fn: typed): typed = fn + +func expMin: float + +func expMin: float {.aadMin.} = 1 + +echo expMin() + + +# doubly-typed forward decls +macro noop(x: typed) = x +noop: + proc cally() = discard + +cally() + +noop: + proc barry() + +proc barry() = discard + +# some more: +proc barry2() {.noop.} +proc barry2() = discard + +proc barry3() {.noop.} +proc barry3() {.noop.} = discard + + +# issue #15389 +block double_sem_for_procs: + + macro aad(fns: varargs[typed]): typed = + result = newStmtList() + for fn in fns: + result.add fn + + func exp(x: float): float {.aad.} = + var x1 = min(max(x, -708.4), 709.8) + if x1 > 0.0: + return x1 + 1.0 + result = 10.0 + + discard exp(5.0) diff --git a/tests/macros/tmacros_various.nim b/tests/macros/tmacros_various.nim index 533db25e1..e351b4527 100644 --- a/tests/macros/tmacros_various.nim +++ b/tests/macros/tmacros_various.nim @@ -22,6 +22,9 @@ a[1]: 45 x: some string ([("key", "val"), ("keyB", "2")], [("val", "key"), ("2", "keyB")]) ([("key", "val"), ("keyB", "2")], [("val", "key"), ("2", "keyB")]) +0 +0 +0 ''' """ @@ -88,7 +91,7 @@ block tlexerex: -block tlineinfo: +block tcopylineinfo: # issue #5617, feature request type Test = object @@ -100,6 +103,27 @@ block tlineinfo: var z = mixer(Test) doAssert z +block tsetgetlineinfo: + # issue #21098, feature request + type Test = object + + macro mixer1(n: typed): untyped = + let x = newIdentNode("echo") + var lineInfo = n.lineInfoObj + x.setLineInfo lineInfo + result = newLit(x.lineInfo == n.lineInfo) + + macro mixer2(n: typed): untyped = + let x = newIdentNode("echo") + var lineInfo = n.lineInfoObj + lineInfo.line += 1 + x.setLineInfo lineInfo + result = newLit(x.lineInfo != n.lineInfo) + + doAssert mixer1(Test) + + doAssert mixer2(Test) + block tdebugstmt: @@ -202,3 +226,166 @@ block tupleNewLitTests: # this `$` test is needed because tuple equality doesn't distinguish # between named vs unnamed tuples doAssert t() == (1, "foo", (), (1, ), (a1: 'x', a2: @["ba"])) + +from strutils import contains +block getImplTransformed: + macro bar(a: typed): string = + # newLit a.getImpl.repr # this would be before code transformation + let b = a.getImplTransformed + newLit b.repr + template toExpand() = + for ai in 0..2: echo ai + proc baz(a=1): int = + defer: discard + toExpand() + 12 + const code = bar(baz) + # sanity check: + doAssert "finally" in code # `defer` is lowered to try/finally + doAssert "while" in code # `for` is lowered to `while` + doAssert "toExpand" notin code + # template is expanded (but that would already be the case with + # `a.getImpl.repr`, unlike the other transformations mentioned above + + +# test macro resemming +macro makeVar(): untyped = + quote: + var tensorY {.inject.}: int + +macro noop(a: typed): untyped = + a + +noop: + makeVar +echo tensorY + +macro xbenchmark(body: typed): untyped = + result = body + +xbenchmark: + proc fastSHA(inputtest: string) = + discard inputtest + fastSHA("hey") + +block: # issue #4547 + macro lazy(stmtList : typed) : untyped = + let decl = stmtList[0] + decl.expectKind nnkLetSection + let name = decl[0][0].strVal + let call = decl[0][2].copy + call.expectKind nnkCall + let ident = newIdentNode("get" & name) + result = quote do: + var value : type(`call`) + proc `ident`() : type(`call`) = + if value.isNil: + value = `call` + value + type MyObject = object + a,b: int + # this part, the macro call and it's result (written in the comment below) is important + lazy: + let y = new(MyObject) + #[ + var value: type(new(MyObject)) + proc gety(): type(new(MyObject)) = + if value.isNil: + value = new(MyObject) + value + ]# + doAssert gety().a == 0 # works and should work + doAssert gety().b == 0 # works and should work + doAssert not declared(y) + doAssert not compiles(y.a) # identifier y should not exist anymore + doAssert not compiles(y.b) # identifier y should not exist anymore + +block: # bug #13511 + type + Builder = ref object + components: seq[Component] + Component = object + + proc add(builder: var Builder, component: Component) {.compileTime.} = + builder.components.add(component) + + macro debugAst(arg: typed): untyped = + ## just for debugging purpose. + discard arg.treeRepr + return arg + + static: + var component = Component() + var builder = Builder() + + template foo(): untyped = + ## WAS: this doc comment causes compilation failure. + builder + + debugAst: + add(foo(), component) + +block: # bug #15118 + macro flop(name: static string) = + let id = genSym(nskType, "env") + let r = + nnkStmtList.newTree( + nnkTypeSection.newTree( + nnkTypeDef.newTree( + id, + newEmptyNode(), + nnkRefTy.newTree( + nnkObjectTy.newTree( + newEmptyNode(), + newEmptyNode(), + nnkRecList.newTree( + nnkIdentDefs.newTree( + newIdentNode(name), + newIdentNode("int"), + newEmptyNode() + ) + ) + ) + ) + ) + ), + + # var f: T + + nnkVarSection.newTree( + nnkIdentDefs.newTree( + newIdentNode("f"), + id, + newEmptyNode() + ) + ), + + # echo f.a + nnkCommand.newTree( + newIdentNode("new"), + newIdentNode("f") + ), + + nnkCommand.newTree( + newIdentNode("echo"), + nnkDotExpr.newTree( + newIdentNode("f"), + newIdentNode(name) + ) + ) + ) + r + + + block: + flop("a") + + block: + flop("b") + +static: + block: + const containsTable = CacheTable"containsTable" + doAssert "foo" notin containsTable + containsTable["foo"] = newLit 42 + doAssert "foo" in containsTable diff --git a/tests/macros/tmacrostmt.nim b/tests/macros/tmacrostmt.nim index 79be5f764..817bc8352 100644 --- a/tests/macros/tmacrostmt.nim +++ b/tests/macros/tmacrostmt.nim @@ -124,7 +124,7 @@ static: let fn4s = "proc fn4(x: int): int =\n if x mod 2 == 0:\n return x + 2\n else:\n return 0\n" let fn5s = "proc fn5(a, b: float): float =\n result = -a * a / (b * b)\n" let fn6s = "proc fn6() =\n var a = @[1.0, 2.0]\n let z = a{0, 1}\n a{2} = 5.0\n" - let fnAddr = "proc fn_unsafeaddr(x: int): int =\n result = cast[int](unsafeAddr(x))\n" + let fnAddr = "proc fn_unsafeaddr(x: int): int =\n result = cast[int](addr(x))\n" doAssert fn1.repr_to_string == fn1s doAssert fn2.repr_to_string == fn2s diff --git a/tests/macros/tmacrotypes.nim b/tests/macros/tmacrotypes.nim index cfc4ef7fb..13b421303 100644 --- a/tests/macros/tmacrotypes.nim +++ b/tests/macros/tmacrotypes.nim @@ -1,9 +1,9 @@ discard """ - nimout: '''intProc; ntyProc; proc[int, int, float]; proc (a: int; b: float): int + nimout: '''intProc; ntyProc; proc[int, int, float]; proc (a: int; b: float): int {.nimcall.} void; ntyVoid; void; void int; ntyInt; int; int -proc (); ntyProc; proc[void]; proc () -voidProc; ntyProc; proc[void]; proc () +proc () {.nimcall.}; ntyProc; proc[void]; proc () {.nimcall.} +voidProc; ntyProc; proc[void]; proc () {.nimcall.} listing fields for ObjType a: string b: int @@ -43,6 +43,8 @@ macro checkType(ex: typed): untyped = echo ex.getTypeInst.repr, "; ", ex.typeKind, "; ", ex.getType.repr, "; ", ex.getTypeImpl.repr macro checkProcType(fn: typed): untyped = + if fn.kind == nnkProcDef: + result = fn let fn_sym = if fn.kind == nnkProcDef: fn[0] else: fn echo fn_sym, "; ", fn_sym.typeKind, "; ", fn_sym.getType.repr, "; ", fn_sym.getTypeImpl.repr diff --git a/tests/macros/tmemit.nim b/tests/macros/tmemit.nim index 06ab8a1e2..6c9f9f935 100644 --- a/tests/macros/tmemit.nim +++ b/tests/macros/tmemit.nim @@ -1,6 +1,5 @@ discard """ output: ''' -HELLO WORLD c_func 12 ''' @@ -8,8 +7,6 @@ c_func import macros, strutils -emit("echo " & '"' & "hello world".toUpperAscii & '"') - # bug #1025 macro foo(icname): untyped = diff --git a/tests/macros/tparsefile.nim b/tests/macros/tparsefile.nim new file mode 100644 index 000000000..a41223f80 --- /dev/null +++ b/tests/macros/tparsefile.nim @@ -0,0 +1,11 @@ +import macros + +static: + let fn = "mparsefile.nim" + var raised = false + try: + discard parseStmt(staticRead(fn), filename = fn) + except ValueError as e: + raised = true + doAssert e.msg == "mparsefile.nim(4, 1) Error: invalid indentation" + doAssert raised diff --git a/tests/macros/tprocgettype.nim b/tests/macros/tprocgettype.nim new file mode 100644 index 000000000..0c1cc4270 --- /dev/null +++ b/tests/macros/tprocgettype.nim @@ -0,0 +1,28 @@ +discard """ + nimout: ''' +var x: proc () {.cdecl.} = foo +var x: iterator (): int {.closure.} = bar +''' +""" + +# issue #19010 + +import macros + +macro createVar(x: typed): untyped = + result = nnkVarSection.newTree: + newIdentDefs(ident"x", getTypeInst(x), copy(x)) + + echo repr result + +block: + proc foo() {.cdecl.} = discard + + createVar(foo) + x() + +block: + iterator bar(): int {.closure.} = discard + + createVar(bar) + for a in x(): discard diff --git a/tests/macros/tprochelpers.nim b/tests/macros/tprochelpers.nim new file mode 100644 index 000000000..d95a2ced8 --- /dev/null +++ b/tests/macros/tprochelpers.nim @@ -0,0 +1,22 @@ +import std/macros +import stdtest/testutils + +macro test1(prc: untyped): untyped = + assertAll: + prc.params.len == 2 + prc.params[1].len == 4 + prc.pragma.len == 2 + + prc.params = nnkFormalParams.newTree( + ident("int") + ) + prc.pragma = newEmptyNode() + + assertAll: + prc.params.len == 1 + prc.pragma.len == 0 + prc + +proc test(a, b: int): int {.gcsafe, raises: [], test1.} = 5 + +type hello = proc(a, b: int): int {.gcsafe, raises: [], test1.} diff --git a/tests/macros/tstaticparamsmacro.nim b/tests/macros/tstaticparamsmacro.nim index 8bd653920..2632ca730 100644 --- a/tests/macros/tstaticparamsmacro.nim +++ b/tests/macros/tstaticparamsmacro.nim @@ -7,9 +7,9 @@ numbers 11 22 AST a -[(11, 22), (33, 44)] +@[(c: 11, d: 22), (c: 33, d: 44)] AST b -([55, 66], [77, 88]) +(e: @[55, 66], f: @[77, 88]) 55 10 20Test diff --git a/tests/macros/tstructuredlogging.nim b/tests/macros/tstructuredlogging.nim index 05bb52a40..649c6c0bd 100644 --- a/tests/macros/tstructuredlogging.nim +++ b/tests/macros/tstructuredlogging.nim @@ -1,7 +1,7 @@ discard """ output: ''' main started: a=10, b=inner-b, c=10, d=some-d, x=16, z=20 -exiting: a=12, b=overriden-b, c=100, msg=bye bye, x=16 +exiting: a=12, b=overridden-b, c=100, msg=bye bye, x=16 ''' """ @@ -66,7 +66,7 @@ macro mergeScopes(scopeHolders: typed, newBindings: untyped): untyped = newScopeDefinition.add newAssignment(newIdentNode(k), v) result = quote: - template scopeHolder = `newScopeDefinition` + template scopeHolder {.redefine.} = `newScopeDefinition` template scope(newBindings: untyped) {.dirty.} = mergeScopes(bindSym"scopeHolder", newBindings) @@ -134,7 +134,7 @@ scope: scope: x = 16 - b = "overriden-b" + b = "overridden-b" scope: c = 100 diff --git a/tests/macros/ttemplatesymbols.nim b/tests/macros/ttemplatesymbols.nim index 280b34ff1..3182de79d 100644 --- a/tests/macros/ttemplatesymbols.nim +++ b/tests/macros/ttemplatesymbols.nim @@ -148,8 +148,6 @@ proc overloadedProc[T](x: T) = echo x """ - # XXX: There seems to be a repr rendering problem above. - # Notice that `echo [x]` inspectSymbol overloadedProc[float], """ proc overloadedProc(x: T) = diff --git a/tests/macros/ttryparseexpr.nim b/tests/macros/ttryparseexpr.nim index fc0ee61d0..e6e9e9880 100644 --- a/tests/macros/ttryparseexpr.nim +++ b/tests/macros/ttryparseexpr.nim @@ -18,3 +18,7 @@ const c = test("\"") # bug #2504 echo a, " ", b + +static: + # Issue #9918 + discard parseStmt("echo(1+1);") diff --git a/tests/macros/ttypenodes.nim b/tests/macros/ttypenodes.nim new file mode 100644 index 000000000..233ea9780 --- /dev/null +++ b/tests/macros/ttypenodes.nim @@ -0,0 +1,16 @@ +import macros + +macro makeEnum(): untyped = + newTree(nnkEnumTy, newEmptyNode(), ident"a", ident"b", ident"c") + +macro makeObject(): untyped = + newTree(nnkObjectTy, newEmptyNode(), newEmptyNode(), newTree(nnkRecList, + newTree(nnkIdentDefs, ident"x", ident"y", ident"int", newEmptyNode()))) + +type + Foo = makeEnum() + Bar = makeObject() + +doAssert {a, b, c} is set[Foo] +let bar = Bar(x: 3, y: 4) +doAssert (bar.x, bar.y) == (3, 4) diff --git a/tests/macros/tvtable.nim b/tests/macros/tvtable.nim index dc07a726b..0f322b7d5 100644 --- a/tests/macros/tvtable.nim +++ b/tests/macros/tvtable.nim @@ -18,7 +18,7 @@ type # it's also possible to use a strongly typed tuple here VTable = array[0..1, pointer] - TBase = object {.inheritable.} + TBase {.inheritable.} = object vtbl: ptr VTable TUserObject1 = object of TBase |