new gensym handling (#11985)

* new .gensym implementation
* make astspec test green again
* introduce a --useVersion switch to group compatibility switches
* fixes #10180
* fixes #11494 
* fixes #11483
* object constructor fields and named parameters are also not gensym'ed
* disabled broken package
This commit is contained in:
Andreas Rumpf 2019-08-23 16:15:02 +02:00 • committed by GitHub
commit b07694cd90
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 346 additions and 114 deletions

View file

@ -10,6 +10,13 @@
to UTF-8. Use the new switch `-d:nimDontSetUtf8CodePage` to disable this to UTF-8. Use the new switch `-d:nimDontSetUtf8CodePage` to disable this
feature. feature.
- The language definition and compiler are now stricter about ``gensym``'ed
symbols in hygienic templates. See the section in the
[manual](https://nim-lang.org/docs/manual.html#templates-hygiene-in-templates)
for further details. Use the compiler switch `--useVersion:0.19` for a
transition period.
### Breaking changes in the standard library ### Breaking changes in the standard library

View file

@ -788,6 +788,15 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "expandmacro": of "expandmacro":
expectArg(conf, switch, arg, pass, info) expectArg(conf, switch, arg, pass, info)
conf.macrosToExpand[arg] = "T" conf.macrosToExpand[arg] = "T"
of "useversion":
expectArg(conf, switch, arg, pass, info)
case arg
of "0.19":
conf.globalOptions.incl optNimV019
of "1.0":
discard "the default"
else:
localError(conf, info, "unknown Nim version; currently supported values are: {0.19, 1.0}")
of "": of "":
conf.projectName = "-" conf.projectName = "-"
else: else:

View file

@ -10,7 +10,7 @@
## Template evaluation engine. Now hygienic. ## Template evaluation engine. Now hygienic.
import import
strutils, options, ast, astalgo, msgs, renderer, lineinfos strutils, options, ast, astalgo, msgs, renderer, lineinfos, idents
type type
TemplCtx = object TemplCtx = object
@ -20,6 +20,7 @@ type
mapping: TIdTable # every gensym'ed symbol needs to be mapped to some mapping: TIdTable # every gensym'ed symbol needs to be mapped to some
# new symbol # new symbol
config: ConfigRef config: ConfigRef
ic: IdentCache
proc copyNode(ctx: TemplCtx, a, b: PNode): PNode = proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
result = copyNode(a) result = copyNode(a)
@ -52,6 +53,10 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
#if x.kind == skParam and x.owner.kind == skModule: #if x.kind == skParam and x.owner.kind == skModule:
# internalAssert c.config, false # internalAssert c.config, false
idTablePut(c.mapping, s, x) idTablePut(c.mapping, s, x)
if sfGenSym in s.flags and optNimV019 notin c.config.globalOptions:
result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $x.id),
if c.instLines: actual.info else: templ.info)
else:
result.add newSymNode(x, if c.instLines: actual.info else: templ.info) result.add newSymNode(x, if c.instLines: actual.info else: templ.info)
else: else:
result.add copyNode(c, templ, actual) result.add copyNode(c, templ, actual)
@ -160,7 +165,9 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
result.typ = res.typ result.typ = res.typ
proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
conf: ConfigRef; fromHlo=false): PNode = conf: ConfigRef;
ic: IdentCache;
fromHlo=false): PNode =
inc(conf.evalTemplateCounter) inc(conf.evalTemplateCounter)
if conf.evalTemplateCounter > evalTemplateLimit: if conf.evalTemplateCounter > evalTemplateLimit:
globalError(conf, n.info, errTemplateInstantiationTooNested) globalError(conf, n.info, errTemplateInstantiationTooNested)
@ -172,6 +179,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
ctx.owner = tmpl ctx.owner = tmpl
ctx.genSymOwner = genSymOwner ctx.genSymOwner = genSymOwner
ctx.config = conf ctx.config = conf
ctx.ic = ic
initIdTable(ctx.mapping) initIdTable(ctx.mapping)
let body = tmpl.getBody let body = tmpl.getBody

View file

@ -18,7 +18,7 @@ import
sem, idents, passes, extccomp, sem, idents, passes, extccomp,
cgen, json, nversion, cgen, json, nversion,
platform, nimconf, passaux, depends, vm, idgen, platform, nimconf, passaux, depends, vm, idgen,
parser, modules, modules,
modulegraphs, tables, rod, lineinfos, pathutils modulegraphs, tables, rod, lineinfos, pathutils
when not defined(leanCompiler): when not defined(leanCompiler):

View file

@ -84,6 +84,7 @@ type # please make sure we have under 32 options
optDynlibOverrideAll optDynlibOverrideAll
optNimV2 optNimV2
optMultiMethods optMultiMethods
optNimV019
TGlobalOptions* = set[TGlobalOption] TGlobalOptions* = set[TGlobalOption]

View file

@ -30,7 +30,7 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
# Note: This is n.info on purpose. It prevents template from creating an info # Note: This is n.info on purpose. It prevents template from creating an info
# context when called from an another template # context when called from an another template
pushInfoContext(c.config, n.info, s.detailedInfo) pushInfoContext(c.config, n.info, s.detailedInfo)
result = evalTemplate(n, s, getCurrOwner(c), c.config, efFromHlo in flags) result = evalTemplate(n, s, getCurrOwner(c), c.config, c.cache, efFromHlo in flags)
if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags) if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags)
popInfoContext(c.config) popInfoContext(c.config)
@ -1236,6 +1236,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
else: else:
let info = getCallLineInfo(n) let info = getCallLineInfo(n)
#if efInCall notin flags:
markUsed(c, info, s) markUsed(c, info, s)
onUse(info, s) onUse(info, s)
result = newSymNode(s, info) result = newSymNode(s, info)

View file

@ -47,7 +47,8 @@ type
TSymChoiceRule = enum TSymChoiceRule = enum
scClosed, scOpen, scForceOpen scClosed, scOpen, scForceOpen
proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode = proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule;
isField = false): PNode =
var var
a: PSym a: PSym
o: TOverloadIter o: TOverloadIter
@ -63,9 +64,12 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode =
# XXX this makes more sense but breaks bootstrapping for now: # XXX this makes more sense but breaks bootstrapping for now:
# (s.kind notin routineKinds or s.magic != mNone): # (s.kind notin routineKinds or s.magic != mNone):
# for instance 'nextTry' is both in tables.nim and astalgo.nim ... # for instance 'nextTry' is both in tables.nim and astalgo.nim ...
if not isField or sfGenSym notin s.flags:
result = newSymNode(s, info) result = newSymNode(s, info)
markUsed(c, info, s) markUsed(c, info, s)
onUse(info, s) onUse(info, s)
else:
result = n
else: else:
# semantic checking requires a type; ``fitNode`` deals with it # semantic checking requires a type; ``fitNode`` deals with it
# appropriately # appropriately
@ -74,7 +78,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode =
result = newNodeIT(kind, info, newTypeS(tyNone, c)) result = newNodeIT(kind, info, newTypeS(tyNone, c))
a = initOverloadIter(o, c, n) a = initOverloadIter(o, c, n)
while a != nil: while a != nil:
if a.kind != skModule: if a.kind != skModule and (not isField or sfGenSym notin s.flags):
incl(a.flags, sfUsed) incl(a.flags, sfUsed)
addSon(result, newSymNode(a, info)) addSon(result, newSymNode(a, info))
onUse(info, a) onUse(info, a)
@ -119,6 +123,7 @@ type
owner: PSym owner: PSym
cursorInBody: bool # only for nimsuggest cursorInBody: bool # only for nimsuggest
scopeN: int scopeN: int
noGenSym: int
template withBracketExpr(ctx, x, body: untyped) = template withBracketExpr(ctx, x, body: untyped) =
body body
@ -228,7 +233,7 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
else: else:
replaceIdentBySym(c.c, n, ident) replaceIdentBySym(c.c, n, ident)
proc semTemplSymbol(c: PContext, n: PNode, s: PSym): PNode = proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode =
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
# we do not call onUse here, as the identifier is not really # we do not call onUse here, as the identifier is not really
# resolved here. We will fixup the used identifiers later. # resolved here. We will fixup the used identifiers later.
@ -237,15 +242,18 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym): PNode =
# Introduced in this pass! Leave it as an identifier. # Introduced in this pass! Leave it as an identifier.
result = n result = n
of OverloadableSyms: of OverloadableSyms:
result = symChoice(c, n, s, scOpen) result = symChoice(c, n, s, scOpen, isField)
of skGenericParam: of skGenericParam:
result = newSymNodeTypeDesc(s, n.info) if isField: result = n
else: result = newSymNodeTypeDesc(s, n.info)
of skParam: of skParam:
result = n result = n
of skType: of skType:
result = newSymNodeTypeDesc(s, n.info) if isField: result = n
else: result = newSymNodeTypeDesc(s, n.info)
else: else:
result = newSymNode(s, n.info) if isField: result = n
else: result = newSymNode(s, n.info)
proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode = proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode =
result = n result = n
@ -322,22 +330,23 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
if n.ident.id in c.toInject: return n if n.ident.id in c.toInject: return n
let s = qualifiedLookUp(c.c, n, {}) let s = qualifiedLookUp(c.c, n, {})
if s != nil: if s != nil:
if s.owner == c.owner and s.kind == skParam: if s.owner == c.owner and s.kind == skParam and
(sfGenSym notin s.flags or c.noGenSym == 0):
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
onUse(n.info, s) onUse(n.info, s)
elif contains(c.toBind, s.id): elif contains(c.toBind, s.id):
result = symChoice(c.c, n, s, scClosed) result = symChoice(c.c, n, s, scClosed, c.noGenSym > 0)
elif contains(c.toMixin, s.name.id): elif contains(c.toMixin, s.name.id):
result = symChoice(c.c, n, s, scForceOpen) result = symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0)
elif s.owner == c.owner and sfGenSym in s.flags: elif s.owner == c.owner and sfGenSym in s.flags and c.noGenSym == 0:
# template tmp[T](x: var seq[T]) = # template tmp[T](x: var seq[T]) =
# var yz: T # var yz: T
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
onUse(n.info, s) onUse(n.info, s)
else: else:
result = semTemplSymbol(c.c, n, s) result = semTemplSymbol(c.c, n, s, c.noGenSym > 0)
of nkBind: of nkBind:
result = semTemplBody(c, n.sons[0]) result = semTemplBody(c, n.sons[0])
of nkBindStmt: of nkBindStmt:
@ -524,11 +533,26 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
onUse(n.info, s) onUse(n.info, s)
return newSymNode(s, n.info) return newSymNode(s, n.info)
elif contains(c.toBind, s.id): elif contains(c.toBind, s.id):
return symChoice(c.c, n, s, scClosed) return symChoice(c.c, n, s, scClosed, c.noGenSym > 0)
elif contains(c.toMixin, s.name.id): elif contains(c.toMixin, s.name.id):
return symChoice(c.c, n, s, scForceOpen) return symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0)
else:
return symChoice(c.c, n, s, scOpen, c.noGenSym > 0)
if n.kind == nkDotExpr:
result = n
result.sons[0] = semTemplBody(c, n.sons[0])
inc c.noGenSym
result.sons[1] = semTemplBody(c, n.sons[1])
dec c.noGenSym
else:
result = semTemplBodySons(c, n)
of nkExprColonExpr, nkExprEqExpr:
if n.len == 2:
inc c.noGenSym
result.sons[0] = semTemplBody(c, n.sons[0])
dec c.noGenSym
result.sons[1] = semTemplBody(c, n.sons[1])
else: else:
return symChoice(c.c, n, s, scOpen)
result = semTemplBodySons(c, n) result = semTemplBodySons(c, n)
else: else:
result = semTemplBodySons(c, n) result = semTemplBodySons(c, n)

View file

@ -261,15 +261,15 @@ proc getQuality(s: PSym): range[0..100] =
if exp.kind in {tyUntyped, tyTyped, tyGenericParam, tyAnything}: return 50 if exp.kind in {tyUntyped, tyTyped, tyGenericParam, tyAnything}: return 50
return 100 return 100
template wholeSymTab(cond, section: untyped) = template wholeSymTab(cond, section: untyped) {.dirty.} =
var isLocal = true var isLocal = true
var scopeN = 0 var scopeN = 0
for scope in walkScopes(c.currentScope): for scope in walkScopes(c.currentScope):
if scope == c.topLevelScope: isLocal = false if scope == c.topLevelScope: isLocal = false
dec scopeN dec scopeN
for item in scope.symbols: for item in scope.symbols:
let it {.inject.} = item let it = item
var pm {.inject.}: PrefixMatch var pm: PrefixMatch
if cond: if cond:
outputs.add(symToSuggest(c.config, it, isLocal = isLocal, section, info, getQuality(it), outputs.add(symToSuggest(c.config, it, isLocal = isLocal, section, info, getQuality(it),
pm, c.inTypeContext > 0, scopeN)) pm, c.inTypeContext > 0, scopeN))

View file

@ -1137,7 +1137,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let node = regs[rb+i].regToNode let node = regs[rb+i].regToNode
node.info = c.debug[pc] node.info = c.debug[pc]
macroCall.add(node) macroCall.add(node)
var a = evalTemplate(macroCall, prc, genSymOwner, c.config) var a = evalTemplate(macroCall, prc, genSymOwner, c.config, c.cache)
if a.kind == nkStmtList and a.len == 1: a = a[0] if a.kind == nkStmtList and a.len == 1: a = a[0]
a.recSetFlagIsRef a.recSetFlagIsRef
ensureKind(rkNode) ensureKind(rkNode)

View file

@ -122,7 +122,7 @@ Advanced options:
enable experimental language feature enable experimental language feature
--legacy:$2 --legacy:$2
enable obsolete/legacy language feature enable obsolete/legacy language feature
legacy code. --useVersion:0.19|1.0 emulate Nim version X of the Nim compiler
--newruntime use an alternative runtime that uses destructors --newruntime use an alternative runtime that uses destructors
and that uses a shared heap via -d:useMalloc and that uses a shared heap via -d:useMalloc
--profiler:on|off enable profiling; requires `import nimprof`, and --profiler:on|off enable profiling; requires `import nimprof`, and

View file

@ -4899,6 +4899,45 @@ no semantics outside of a template definition and cannot be abstracted over:
To get rid of hygiene in templates, one can use the `dirty`:idx: pragma for To get rid of hygiene in templates, one can use the `dirty`:idx: pragma for
a template. ``inject`` and ``gensym`` have no effect in ``dirty`` templates. a template. ``inject`` and ``gensym`` have no effect in ``dirty`` templates.
``gensym``'ed symbols cannot be used as ``field`` in the ``x.field`` syntax.
Nor can they be used in the ``ObjectConstruction(field: value)``
and ``namedParameterCall(field = value)`` syntactic constructs.
The reason for this is that code like
.. code-block:: nim
:test: "nim c $1"
type
T = object
f: int
template tmp(x: T) =
let f = 34
echo x.f, T(f: 4)
should work as expected.
However, this means that the method call syntax is not available for
``gensym``'ed symbols:
.. code-block:: nim
:test: "nim c $1"
:status: 1
template tmp(x) =
type
T {.gensym.} = int
echo x.T # invalid: instead use: 'echo T(x)'.
tmp(12)
**Note**: The Nim compiler prior to version 1 was more lenient about this
requirement. Use the ``--useVersion:0.19`` switch for a transition period.
Limitations of the method call syntax Limitations of the method call syntax

View file

@ -60,11 +60,11 @@ template withLock*(a: Lock, body: untyped) =
## Acquires the given lock, executes the statements in body and ## Acquires the given lock, executes the statements in body and
## releases the lock after the statements finish executing. ## releases the lock after the statements finish executing.
mixin acquire, release mixin acquire, release
a.acquire() acquire(a)
{.locks: [a].}: {.locks: [a].}:
try: try:
body body
finally: finally:
a.release() release(a)
{.pop.} {.pop.}

View file

@ -984,7 +984,7 @@ when defined(nimTypeNames):
# ---------------------- thread memory region ------------------------------- # ---------------------- thread memory region -------------------------------
template instantiateForRegion(allocator: untyped) = template instantiateForRegion(allocator: untyped) {.dirty.} =
{.push stackTrace: off.} {.push stackTrace: off.}
when defined(fulldebug): when defined(fulldebug):
@ -1006,8 +1006,8 @@ template instantiateForRegion(allocator: untyped) =
proc dealloc(p: pointer) = proc dealloc(p: pointer) =
dealloc(allocator, p) dealloc(allocator, p)
proc realloc(p: pointer, newsize: Natural): pointer = proc realloc(p: pointer, newSize: Natural): pointer =
result = realloc(allocator, p, newsize) result = realloc(allocator, p, newSize)
when false: when false:
proc countFreeMem(): int = proc countFreeMem(): int =
@ -1054,13 +1054,13 @@ template instantiateForRegion(allocator: untyped) =
else: else:
dealloc(p) dealloc(p)
proc reallocShared(p: pointer, newsize: Natural): pointer = proc reallocShared(p: pointer, newSize: Natural): pointer =
when hasThreadSupport: when hasThreadSupport:
acquireSys(heapLock) acquireSys(heapLock)
result = realloc(sharedHeap, p, newsize) result = realloc(sharedHeap, p, newSize)
releaseSys(heapLock) releaseSys(heapLock)
else: else:
result = realloc(p, newsize) result = realloc(p, newSize)
when hasThreadSupport: when hasThreadSupport:
template sharedMemStatsShared(v: int) = template sharedMemStatsShared(v: int) =

View file

@ -5,9 +5,9 @@ template pkg(name: string; cmd = "nimble test"; hasDeps = false; url = ""): unty
var packages*: seq[tuple[name, cmd: string; hasDeps: bool; url: string]] = @[] var packages*: seq[tuple[name, cmd: string; hasDeps: bool; url: string]] = @[]
pkg "argparse" #pkg "argparse"
pkg "arraymancer", "nim c -r src/arraymancer.nim", true pkg "arraymancer", "nim c -r src/arraymancer.nim", true
pkg "ast_pattern_matching", "nim c -r tests/test1.nim" pkg "ast_pattern_matching", "nim c -r --useVersion=0.19 tests/test1.nim"
pkg "binaryheap", "nim c -r binaryheap.nim" pkg "binaryheap", "nim c -r binaryheap.nim"
pkg "blscurve", "", true pkg "blscurve", "", true
pkg "bncurve", "", true pkg "bncurve", "", true
@ -61,7 +61,7 @@ pkg "npeg"
pkg "ormin", "nim c -o:orminn ormin.nim", true pkg "ormin", "nim c -o:orminn ormin.nim", true
pkg "parsetoml" pkg "parsetoml"
pkg "patty" pkg "patty"
pkg "plotly", "nim c examples/all.nim", true pkg "plotly", "nim c --useVersion:0.19 examples/all.nim", true
pkg "protobuf", "nim c -o:protobuff -r src/protobuf.nim", true pkg "protobuf", "nim c -o:protobuff -r src/protobuf.nim", true
pkg "regex", "nim c src/regex", true pkg "regex", "nim c src/regex", true
pkg "result", "nim c -r result.nim" pkg "result", "nim c -r result.nim"
@ -71,7 +71,7 @@ pkg "sdl2_nim", "nim c -r sdl2/sdl.nim"
pkg "snip", "", false, "https://github.com/genotrance/snip" pkg "snip", "", false, "https://github.com/genotrance/snip"
pkg "stint", "nim c -o:stintt -r stint.nim" pkg "stint", "nim c -o:stintt -r stint.nim"
pkg "strunicode", "nim c -r src/strunicode.nim", true pkg "strunicode", "nim c -r src/strunicode.nim", true
pkg "telebot", "nim c -o:tbot -r telebot.nim", true pkg "telebot", "nim c -o:tbot --useVersion:0.19 -r telebot.nim", true
pkg "tiny_sqlite" pkg "tiny_sqlite"
pkg "unicodedb" pkg "unicodedb"
pkg "unicodeplus", "", true pkg "unicodeplus", "", true

View file

@ -6,6 +6,74 @@ action: compile
import ../ast_pattern_matching import ../ast_pattern_matching
template expectNimNode(arg: untyped): NimNode = arg
## This template here is just to be injected by `myquote`, so that
## a nice error message appears when the captured symbols are not of
## type `NimNode`.
proc substitudeComments(symbols, values, n: NimNode): NimNode =
## substitudes all nodes of kind nnkCommentStmt to parameter
## symbols. Consumes the argument `n`.
if n.kind == nnkCommentStmt:
values.add newCall(bindSym"newCommentStmtNode", newLit(n.strVal))
# Gensym doesn't work for parameters. These identifiers won't
# clash unless an argument is constructed to clash here.
symbols.add ident("comment" & $values.len & "_XObBdOnh6meCuJK2smZV")
return symbols[^1]
for i in 0 ..< n.len:
n[i] = substitudeComments(symbols, values, n[i])
return n
macro myquote*(args: varargs[untyped]): untyped =
expectMinLen(args, 1)
# This is a workaround for #10430 where comments are removed in
# template expansions. This workaround lifts all comments
# statements to be arguments of the temporary template.
let extraCommentSymbols = newNimNode(nnkBracket)
let extraCommentGenExpr = newNimNode(nnkBracket)
let body = substitudeComments(
extraCommentSymbols, extraCommentGenExpr, args[^1]
)
let formalParams = nnkFormalParams.newTree(ident"untyped")
for i in 0 ..< args.len-1:
formalParams.add nnkIdentDefs.newTree(
args[i], ident"untyped", newEmptyNode()
)
for sym in extraCommentSymbols:
formalParams.add nnkIdentDefs.newTree(
sym, ident"untyped", newEmptyNode()
)
let templateSym = genSym(nskTemplate)
let templateDef = nnkTemplateDef.newTree(
templateSym,
newEmptyNode(),
newEmptyNode(),
formalParams,
nnkPragma.newTree(ident"dirty"),
newEmptyNode(),
args[^1]
)
let templateCall = newCall(templateSym)
for i in 0 ..< args.len-1:
let symName = args[i]
# identifiers and quoted identifiers are allowed.
if symName.kind == nnkAccQuoted:
symName.expectLen 1
symName[0].expectKind nnkIdent
else:
symName.expectKind nnkIdent
templateCall.add newCall(bindSym"expectNimNode", symName)
for expr in extraCommentGenExpr:
templateCall.add expr
let getAstCall = newCall(bindSym"getAst", templateCall)
result = newStmtList(templateDef, getAstCall)
macro testAddrAst(arg: typed): bool = macro testAddrAst(arg: typed): bool =
arg.expectKind nnkStmtListExpr arg.expectKind nnkStmtListExpr
arg[0].expectKind(nnkVarSection) arg[0].expectKind(nnkVarSection)
@ -49,34 +117,35 @@ static:
echo "OK" echo "OK"
testPattern nnkIntLit(intVal = 42) , 42 testPattern nnkIntLit(intVal = 42), 42
testPattern nnkInt8Lit(intVal = 42) , 42'i8 testPattern nnkInt8Lit(intVal = 42), 42'i8
testPattern nnkInt16Lit(intVal = 42) , 42'i16 testPattern nnkInt16Lit(intVal = 42), 42'i16
testPattern nnkInt32Lit(intVal = 42) , 42'i32 testPattern nnkInt32Lit(intVal = 42), 42'i32
testPattern nnkInt64Lit(intVal = 42) , 42'i64 testPattern nnkInt64Lit(intVal = 42), 42'i64
testPattern nnkUInt8Lit(intVal = 42) , 42'u8 testPattern nnkUInt8Lit(intVal = 42), 42'u8
testPattern nnkUInt16Lit(intVal = 42) , 42'u16 testPattern nnkUInt16Lit(intVal = 42), 42'u16
testPattern nnkUInt32Lit(intVal = 42) , 42'u32 testPattern nnkUInt32Lit(intVal = 42), 42'u32
testPattern nnkUInt64Lit(intVal = 42) , 42'u64 testPattern nnkUInt64Lit(intVal = 42), 42'u64
#testPattern nnkFloat64Lit(floatVal = 42.0) , 42.0 #testPattern nnkFloat64Lit(floatVal = 42.0), 42.0
testPattern nnkFloat32Lit(floatVal = 42.0) , 42.0'f32 testPattern nnkFloat32Lit(floatVal = 42.0), 42.0'f32
#testPattern nnkFloat64Lit(floatVal = 42.0) , 42.0'f64 #testPattern nnkFloat64Lit(floatVal = 42.0), 42.0'f64
testPattern nnkStrLit(strVal = "abc") , "abc" testPattern nnkStrLit(strVal = "abc"), "abc"
testPattern nnkRStrLit(strVal = "abc") , r"abc" testPattern nnkRStrLit(strVal = "abc"), r"abc"
testPattern nnkTripleStrLit(strVal = "abc") , """abc""" testPattern nnkTripleStrLit(strVal = "abc"), """abc"""
testPattern nnkCharLit(intVal = 32) , ' ' testPattern nnkCharLit(intVal = 32), ' '
testPattern nnkNilLit() , nil testPattern nnkNilLit(), nil
testPattern nnkIdent(strVal = "myIdentifier") , myIdentifier testPattern nnkIdent(strVal = "myIdentifier"), myIdentifier
testPatternFail nnkInt8Lit(intVal = 42), 42'i16
testPatternFail nnkInt16Lit(intVal = 42), 42'i8
testPatternFail nnkInt8Lit(intVal = 42) , 42'i16
testPatternFail nnkInt16Lit(intVal = 42) , 42'i8
# this should be just `block` but it doesn't work that way anymore because of VM. # this should be just `block` but it doesn't work that way anymore because of VM.
macro scope(arg: untyped): untyped = macro scope(arg: untyped): untyped =
let procSym = genSym(nskProc) let procSym = genSym(nskProc)
result = quote do: result = quote do:
proc `procSym`(): void {.compileTime.} = proc `procSym`() {.compileTime.} =
`arg` `arg`
`procSym`() `procSym`()
@ -85,7 +154,7 @@ static:
## Command call ## Command call
scope: scope:
let ast = quote do: let ast = myquote:
echo "abc", "xyz" echo "abc", "xyz"
ast.matchAst: ast.matchAst:
@ -95,7 +164,7 @@ static:
## Call with ``()`` ## Call with ``()``
scope: scope:
let ast = quote do: let ast = myquote:
echo("abc", "xyz") echo("abc", "xyz")
ast.matchAst: ast.matchAst:
@ -140,7 +209,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
? "xyz" ? "xyz"
ast.matchAst(err): ast.matchAst(err):
@ -155,7 +224,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
proc identifier* proc identifier*
ast[0].matchAst(err): ast[0].matchAst(err):
@ -185,7 +254,7 @@ static:
## Call with raw string literal ## Call with raw string literal
scope: scope:
let ast = quote do: let ast = myquote:
echo"abc" echo"abc"
@ -230,7 +299,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
cast[T](x) cast[T](x)
ast.matchAst: ast.matchAst:
@ -242,7 +311,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
x.y x.y
ast.matchAst: ast.matchAst:
@ -264,7 +333,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
(1, 2, (3)) (1, 2, (3))
ast.matchAst: ast.matchAst:
@ -276,7 +345,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
{1, 2, 3} {1, 2, 3}
ast.matchAst: ast.matchAst:
@ -285,7 +354,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
{a: 3, b: 5} {a: 3, b: 5}
ast.matchAst: ast.matchAst:
@ -300,7 +369,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
[1, 2, 3] [1, 2, 3]
ast.matchAst: ast.matchAst:
@ -312,7 +381,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
1..3 1..3
ast.matchAst: ast.matchAst:
@ -328,7 +397,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
if cond1: expr1 elif cond2: expr2 else: expr3 if cond1: expr1 elif cond2: expr2 else: expr3
ast.matchAst: ast.matchAst:
@ -343,7 +412,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
## This is a comment ## This is a comment
## This is part of the first comment ## This is part of the first comment
stmt1 stmt1
@ -357,12 +426,12 @@ static:
): ):
echo "ok" echo "ok"
else: else:
echo "NOT OK!!!" echo "warning!"
echo ast.treeRepr echo ast.treeRepr
echo "TEST causes no fail, because of a regression in Nim." echo "TEST causes no fail, because of a regression in Nim."
scope: scope:
let ast = quote do: let ast = myquote:
{.emit: "#include <stdio.h>".} {.emit: "#include <stdio.h>".}
ast.matchAst: ast.matchAst:
@ -375,7 +444,7 @@ static:
echo "ok" echo "ok"
scope: scope:
let ast = quote do: let ast = myquote:
{.pragma: cdeclRename, cdecl.} {.pragma: cdeclRename, cdecl.}
ast.matchAst: ast.matchAst:
@ -391,7 +460,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
if cond1: if cond1:
stmt1 stmt1
elif cond2: elif cond2:
@ -413,7 +482,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
x = 42 x = 42
ast.matchAst: ast.matchAst:
@ -423,7 +492,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
stmt1 stmt1
stmt2 stmt2
stmt3 stmt3
@ -439,7 +508,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
case expr1 case expr1
of expr2, expr3..expr4: of expr2, expr3..expr4:
stmt1 stmt1
@ -464,7 +533,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
while expr1: while expr1:
stmt1 stmt1
@ -477,7 +546,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
for ident1, ident2 in expr1: for ident1, ident2 in expr1:
stmt1 stmt1
@ -490,7 +559,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
try: try:
stmt1 stmt1
except e1, e2: except e1, e2:
@ -517,7 +586,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
return expr1 return expr1
ast.matchAst: ast.matchAst:
@ -528,7 +597,7 @@ static:
## Continue statement ## Continue statement
scope: scope:
let ast = quote do: let ast = myquote:
continue continue
ast.matchAst: ast.matchAst:
@ -539,7 +608,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
break otherLocation break otherLocation
ast.matchAst: ast.matchAst:
@ -550,10 +619,12 @@ static:
scope: scope:
let ast = quote do: template blockStatement {.dirty.} =
block name: block name:
discard discard
let ast = getAst(blockStatement())
ast.matchAst: ast.matchAst:
of nnkBlockStmt(ident"name", nnkStmtList): of nnkBlockStmt(ident"name", nnkStmtList):
echo "ok" echo "ok"
@ -562,7 +633,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
asm """some asm""" asm """some asm"""
ast.matchAst: ast.matchAst:
@ -576,7 +647,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
import math import math
ast.matchAst: ast.matchAst:
@ -585,7 +656,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
import math except pow import math except pow
ast.matchAst: ast.matchAst:
@ -594,7 +665,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
import strutils as su import strutils as su
ast.matchAst: ast.matchAst:
@ -611,7 +682,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
from math import pow from math import pow
ast.matchAst: ast.matchAst:
@ -622,7 +693,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
export unsigned export unsigned
ast.matchAst: ast.matchAst:
@ -631,7 +702,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
export math except pow # we're going to implement our own exponentiation export math except pow # we're going to implement our own exponentiation
ast.matchAst: ast.matchAst:
@ -642,7 +713,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
include blocks include blocks
ast.matchAst: ast.matchAst:
@ -653,7 +724,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
var a = 3 var a = 3
ast.matchAst: ast.matchAst:
@ -670,7 +741,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
let a = 3 let a = 3
ast.matchAst: ast.matchAst:
@ -687,7 +758,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
const a = 3 const a = 3
ast.matchAst: ast.matchAst:
@ -704,7 +775,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
type A = int type A = int
ast.matchAst: ast.matchAst:
@ -719,7 +790,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
type MyInt = distinct int type MyInt = distinct int
ast.peelOff({nnkTypeSection}).matchAst: ast.peelOff({nnkTypeSection}).matchAst:
@ -735,7 +806,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
type A[T] = expr1 type A[T] = expr1
ast.matchAst: ast.matchAst:
@ -757,7 +828,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
type IO = object of RootObj type IO = object of RootObj
ast.peelOff(nnkTypeSection).matchAst: ast.peelOff(nnkTypeSection).matchAst:
@ -840,7 +911,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
type X = enum type X = enum
First First
@ -853,7 +924,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
type Con = concept x,y,z type Con = concept x,y,z
(x & y & z) is string (x & y & z) is string
@ -865,7 +936,7 @@ static:
scope: scope:
let astX = quote do: let astX = myquote:
type type
A[T: static[int]] = object A[T: static[int]] = object
@ -880,7 +951,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
type MyProc[T] = proc(x: T) type MyProc[T] = proc(x: T)
ast.peelOff({nnkStmtList, nnkTypeSection}).matchAst(err): ast.peelOff({nnkStmtList, nnkTypeSection}).matchAst(err):
@ -952,7 +1023,7 @@ static:
proc hello*[T: SomeInteger](x: int = 3, y: float32): int {.inline.} = discard proc hello*[T: SomeInteger](x: int = 3, y: float32): int {.inline.} = discard
scope: scope:
var ast = quote do: var ast = myquote:
proc foobar(a, b: int): void proc foobar(a, b: int): void
ast = ast[3] ast = ast[3]
@ -971,7 +1042,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
proc hello(): var int proc hello(): var int
ast[3].matchAst: # subAst ast[3].matchAst: # subAst
@ -986,7 +1057,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
iterator nonsense[T](x: seq[T]): float {.closure.} = iterator nonsense[T](x: seq[T]): float {.closure.} =
discard discard
@ -998,7 +1069,7 @@ static:
scope: scope:
let ast = quote do: let ast = myquote:
converter toBool(x: float): bool converter toBool(x: float): bool
ast.matchAst: ast.matchAst:
@ -1008,7 +1079,7 @@ static:
## Template declaration ## Template declaration
scope: scope:
let ast = quote do: let ast = myquote:
template optOpt{expr1}(a: int): int template optOpt{expr1}(a: int): int
ast.matchAst: ast.matchAst:

View file

@ -0,0 +1,44 @@
discard """
output: '''0
0.0'''
"""
# bug #11494
import macros
macro staticForEach(arr: untyped, body: untyped): untyped =
result = newNimNode(nnkStmtList)
arr.expectKind(nnkBracket)
for n in arr:
let b = copyNimTree(body)
result.add quote do:
block:
type it {.inject.} = `n`
`b`
template forEveryMatchingEntity*() =
staticForEach([int, string, float]):
var a: it
echo a
forEveryMatchingEntity()
# bug #11483
proc main =
template first(body) =
template second: var int =
var o: int
var i = addr(o)
i[]
body
first:
second = 5
second = 6
main()

View file

@ -8,6 +8,7 @@ output: '''
1 1
2 2
3 3
wth
''' '''
""" """
# bug #1915 # bug #1915
@ -130,3 +131,17 @@ template test() =
doAssert(foo.len == 3) doAssert(foo.len == 3)
test() test()
# regression found in PMunch's parser generator
proc namedcall(arg: string) =
discard
macro m(): untyped =
result = quote do:
(proc (arg: string) =
namedcall(arg = arg)
echo arg)
let meh = m()
meh("wth")

View file

@ -0,0 +1,13 @@
discard """
errormsg: "redefinition of 'a`gensym"
line: 9
"""
# bug #10180
proc f() =
template t() =
var a = 1
var a = 2
echo a
t()
f()