fixes #21326; fixes #7375; fixes #11986; fixes #9607; rework quote do; getAst uses type info to annotate the type of quoted variables; no more type erasures for quoted variables (#21433)

* fixes #21326; getAst uses type info to annotateType quoted variables

* simplify logics; sem types first

* fixes important packages

* add testcases

* tiny
This commit is contained in:
ringabout
2023-03-02 12:25:55 +08:00
committed by GitHub
parent 070938720a
commit 9948fed919
4 changed files with 150 additions and 8 deletions

View File

@@ -2203,10 +2203,13 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
if ids.len > 0:
dummyTemplate[paramsPos] = newNodeI(nkFormalParams, n.info)
dummyTemplate[paramsPos].add getSysSym(c.graph, n.info, "untyped").newSymNode # return type
ids.add getSysSym(c.graph, n.info, "untyped").newSymNode # params type
ids.add c.graph.emptyNode # no default value
dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids)
dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[0], getSysSym(c.graph, n.info, "typed").newSymNode, c.graph.emptyNode)
for i in 1..<ids.len:
let typ = semExprWithType(c, quotes[i+1], {}).typ
if tfTriggersCompileTime notin typ.flags:
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)
var tmpl = semTemplateDef(c, dummyTemplate)
quotes[0] = tmpl[namePos]
# This adds a call to newIdentNode("result") as the first argument to the template call

View File

@@ -10,7 +10,7 @@
## This file implements the new evaluation engine for Nim code.
## An instruction is 1-3 int32s in memory, it is a register based VM.
import semmacrosanity
import
std/[strutils, tables, parseutils],
msgs, vmdef, vmgen, nimsets, types, passes,
@@ -1408,6 +1408,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
for i in 1..rc-1:
let node = regs[rb+i].regToNode
node.info = c.debug[pc]
if prc.typ[i].kind notin {tyTyped, tyUntyped}:
node.annotateType(prc.typ[i], c.config)
macroCall.add(node)
var a = evalTemplate(macroCall, prc, genSymOwner, c.config, c.cache, c.templInstCounter, c.idgen)
if a.kind == nkStmtList and a.len == 1: a = a[0]

View File

@@ -83,7 +83,7 @@ pkg "iterutils"
pkg "jstin"
pkg "karax", "nim c -r tests/tester.nim"
pkg "kdtree", "nimble test -d:nimLegacyRandomInitRand", "https://github.com/jblindsay/kdtree"
pkg "loopfusion"
pkg "loopfusion", url = "https://github.com/nim-lang/loop-fusion"
pkg "lockfreequeues"
pkg "macroutils"
pkg "manu"
@@ -115,7 +115,7 @@ pkg "nimpy", "nim c -r tests/nimfrompy.nim"
pkg "nimquery"
pkg "nimsl"
pkg "nimsvg"
pkg "nimterop", "nimble minitest"
pkg "nimterop", "nimble minitest", url = "https://github.com/nim-lang/nimterop"
pkg "nimwc", "nim c nimwc.nim", allowFailure = true
pkg "nimx", "nim c --threads:on test/main.nim", allowFailure = true
pkg "nitter", "nim c src/nitter.nim", "https://github.com/zedeus/nitter"
@@ -135,7 +135,7 @@ pkg "protobuf", "nim c -o:protobuff -r src/protobuf.nim"
pkg "pylib"
pkg "rbtree"
pkg "react", "nimble example"
pkg "regex", "nim c src/regex"
pkg "regex", "nim c src/regex", url = "https://github.com/nim-lang/nim-regex"
pkg "result", "nim c -r result.nim"
pkg "RollingHash", "nim c -r tests/test_cyclichash.nim"
pkg "rosencrantz", "nim c -o:rsncntz -r rosencrantz.nim"

View File

@@ -175,3 +175,139 @@ block getCustomPragmaValGeneric:
var f: Foo[string]
doAssert f.getCustomPragmaVal(examplePragma) == 42
doAssert f.x.getCustomPragmaVal(examplePragma) == 25
block: # bug #21326
macro foo(body: untyped): untyped =
let a = body.lineInfoObj()
let aLit = a.newLit
result = quote do:
doAssert $`a` == $`aLit`
foo:
let c = 1
template name(a: LineInfo): untyped =
discard a # `aLit` works though
macro foo3(body: untyped): untyped =
let a = body.lineInfoObj()
# let ax = newLit(a)
result = getAst(name(a))
foo3:
let c = 1
block: # bug #7375
macro fails(b: static[bool]): untyped =
doAssert b == false
result = newStmtList()
macro foo(): untyped =
var b = false
## Fails
result = quote do:
fails(`b`)
foo()
macro someMacro(): untyped =
template tmpl(boolean: bool) =
when boolean:
discard "it's true!"
else:
doAssert false
result = getAst(tmpl(true))
someMacro()
block:
macro foo(): untyped =
result = quote do: `littleEndian`
doAssert littleEndian == foo()
block:
macro eqSym(x, y: untyped): untyped =
let eq = $x == $y # Unfortunately eqIdent compares to string.
result = quote do: `eq`
var r, a, b: int
template fma(result: var int, a, b: int, op: untyped) =
# fused multiple-add
when eqSym(op, `+=`):
discard "+="
else:
discard "+"
fma(r, a, b, `+=`)
block:
template test(boolArg: bool) =
static:
doAssert typeof(boolArg) is bool
let x: bool = boolArg # compile error here, because boolArg became an int
macro testWrapped1(boolArg: bool): untyped =
# forwarding boolArg directly works
result = getAst(test(boolArg))
macro testWrapped2(boolArg: bool): untyped =
# forwarding boolArg via a local variable also works
let b = boolArg
result = getAst(test(b))
macro testWrapped3(boolArg: bool): untyped =
# but using a literal `true` as a local variable will be converted to int
let b = true
result = getAst(test(b))
test(true) # ok
testWrapped1(true) # ok
testWrapped2(true) # ok
testWrapped3(true)
block:
macro foo(): untyped =
var s = { 'a', 'b' }
quote do:
let t = `s`
doAssert $typeof(t) == "set[char]"
foo()
block: # bug #9607
proc fun1(info:LineInfo): string = "bar"
proc fun2(info:int): string = "bar"
macro echoL(args: varargs[untyped]): untyped =
let info = args.lineInfoObj
let fun1 = bindSym"fun1"
let fun2 = bindSym"fun2"
# this would work instead
# result = newCall(bindSym"fun2", info.line.newLit)
result = quote do:
# BUG1: ???(0, 0) Error: internal error: genLiteral: ty is nil
`fun1`(`info`)
macro echoM(args: varargs[untyped]): untyped =
let info = args.lineInfoObj
let fun1 = bindSym"fun1"
let fun2 = bindSym"fun2"
# this would work instead
# result = newCall(bindSym"fun2", info.line.newLit)
result = quote do:
# BUG1: ???(0, 0) Error: internal error: genLiteral: ty is nil
`fun2`(`info`.line)
doAssert echoL() == "bar"
doAssert echoM() == "bar"