diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2019-08-23 16:15:02 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-08-23 16:15:02 +0200 |
commit | b07694cd90ab7c6eb4660971ddb818b461d4eed8 (patch) | |
tree | a158993297748d4c55b6e069a6636eefcacd9865 | |
parent | f28a47ea7b9c579b172653c15dc2cc054adf599a (diff) | |
download | Nim-b07694cd90ab7c6eb4660971ddb818b461d4eed8.tar.gz |
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
-rw-r--r-- | changelog.md | 7 | ||||
-rw-r--r-- | compiler/commands.nim | 9 | ||||
-rw-r--r-- | compiler/evaltempl.nim | 14 | ||||
-rw-r--r-- | compiler/main.nim | 2 | ||||
-rw-r--r-- | compiler/options.nim | 1 | ||||
-rw-r--r-- | compiler/semexprs.nim | 3 | ||||
-rw-r--r-- | compiler/semtempl.nim | 62 | ||||
-rw-r--r-- | compiler/suggest.nim | 6 | ||||
-rw-r--r-- | compiler/vm.nim | 2 | ||||
-rw-r--r-- | doc/advopt.txt | 2 | ||||
-rw-r--r-- | doc/manual.rst | 39 | ||||
-rw-r--r-- | lib/core/locks.nim | 4 | ||||
-rw-r--r-- | lib/system/alloc.nim | 12 | ||||
-rw-r--r-- | testament/important_packages.nim | 8 | ||||
-rw-r--r-- | tests/astspec/tastspec.nim | 219 | ||||
-rw-r--r-- | tests/template/tmore_regressions.nim | 44 | ||||
-rw-r--r-- | tests/template/tparams_gensymed.nim | 15 | ||||
-rw-r--r-- | tests/template/tredefinition.nim | 13 |
18 files changed, 347 insertions, 115 deletions
diff --git a/changelog.md b/changelog.md index 41d371b60..f29a4c69d 100644 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,13 @@ to UTF-8. Use the new switch `-d:nimDontSetUtf8CodePage` to disable this 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 diff --git a/compiler/commands.nim b/compiler/commands.nim index 3874ea38f..662df9c84 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -788,6 +788,15 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "expandmacro": expectArg(conf, switch, arg, pass, info) 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 "": conf.projectName = "-" else: diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index d3d3e5f77..d941f6c46 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -10,7 +10,7 @@ ## Template evaluation engine. Now hygienic. import - strutils, options, ast, astalgo, msgs, renderer, lineinfos + strutils, options, ast, astalgo, msgs, renderer, lineinfos, idents type TemplCtx = object @@ -20,6 +20,7 @@ type mapping: TIdTable # every gensym'ed symbol needs to be mapped to some # new symbol config: ConfigRef + ic: IdentCache proc copyNode(ctx: TemplCtx, a, b: PNode): PNode = result = copyNode(a) @@ -52,7 +53,11 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = #if x.kind == skParam and x.owner.kind == skModule: # internalAssert c.config, false idTablePut(c.mapping, s, x) - result.add newSymNode(x, if c.instLines: actual.info else: templ.info) + 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) else: result.add copyNode(c, templ, actual) of nkNone..nkIdent, nkType..nkNilLit: # atom @@ -160,7 +165,9 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode = result.typ = res.typ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; - conf: ConfigRef; fromHlo=false): PNode = + conf: ConfigRef; + ic: IdentCache; + fromHlo=false): PNode = inc(conf.evalTemplateCounter) if conf.evalTemplateCounter > evalTemplateLimit: globalError(conf, n.info, errTemplateInstantiationTooNested) @@ -172,6 +179,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; ctx.owner = tmpl ctx.genSymOwner = genSymOwner ctx.config = conf + ctx.ic = ic initIdTable(ctx.mapping) let body = tmpl.getBody diff --git a/compiler/main.nim b/compiler/main.nim index 8cd8a52d4..877b82dd9 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -18,7 +18,7 @@ import sem, idents, passes, extccomp, cgen, json, nversion, platform, nimconf, passaux, depends, vm, idgen, - parser, modules, + modules, modulegraphs, tables, rod, lineinfos, pathutils when not defined(leanCompiler): diff --git a/compiler/options.nim b/compiler/options.nim index 75eec4756..52ecd61bc 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -84,6 +84,7 @@ type # please make sure we have under 32 options optDynlibOverrideAll optNimV2 optMultiMethods + optNimV019 TGlobalOptions* = set[TGlobalOption] diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 8048111cd..57d9aae4b 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -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 # context when called from an another template 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) popInfoContext(c.config) @@ -1236,6 +1236,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = result = newSymNode(s, n.info) else: let info = getCallLineInfo(n) + #if efInCall notin flags: markUsed(c, info, s) onUse(info, s) result = newSymNode(s, info) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index ddc0667e4..907d2174e 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -47,7 +47,8 @@ type TSymChoiceRule = enum 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 a: PSym 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: # (s.kind notin routineKinds or s.magic != mNone): # for instance 'nextTry' is both in tables.nim and astalgo.nim ... - result = newSymNode(s, info) - markUsed(c, info, s) - onUse(info, s) + if not isField or sfGenSym notin s.flags: + result = newSymNode(s, info) + markUsed(c, info, s) + onUse(info, s) + else: + result = n else: # semantic checking requires a type; ``fitNode`` deals with it # appropriately @@ -74,7 +78,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode = result = newNodeIT(kind, info, newTypeS(tyNone, c)) a = initOverloadIter(o, c, n) while a != nil: - if a.kind != skModule: + if a.kind != skModule and (not isField or sfGenSym notin s.flags): incl(a.flags, sfUsed) addSon(result, newSymNode(a, info)) onUse(info, a) @@ -119,6 +123,7 @@ type owner: PSym cursorInBody: bool # only for nimsuggest scopeN: int + noGenSym: int template withBracketExpr(ctx, x, body: untyped) = body @@ -228,7 +233,7 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = else: 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) # we do not call onUse here, as the identifier is not really # 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. result = n of OverloadableSyms: - result = symChoice(c, n, s, scOpen) + result = symChoice(c, n, s, scOpen, isField) of skGenericParam: - result = newSymNodeTypeDesc(s, n.info) + if isField: result = n + else: result = newSymNodeTypeDesc(s, n.info) of skParam: result = n of skType: - result = newSymNodeTypeDesc(s, n.info) + if isField: result = n + else: result = newSymNodeTypeDesc(s, n.info) else: - result = newSymNode(s, n.info) + if isField: result = n + else: result = newSymNode(s, n.info) proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode = result = n @@ -322,22 +330,23 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = if n.ident.id in c.toInject: return n let s = qualifiedLookUp(c.c, n, {}) 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) result = newSymNode(s, n.info) onUse(n.info, s) 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): - result = symChoice(c.c, n, s, scForceOpen) - elif s.owner == c.owner and sfGenSym in s.flags: + result = symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0) + elif s.owner == c.owner and sfGenSym in s.flags and c.noGenSym == 0: # template tmp[T](x: var seq[T]) = # var yz: T incl(s.flags, sfUsed) result = newSymNode(s, n.info) onUse(n.info, s) else: - result = semTemplSymbol(c.c, n, s) + result = semTemplSymbol(c.c, n, s, c.noGenSym > 0) of nkBind: result = semTemplBody(c, n.sons[0]) of nkBindStmt: @@ -524,12 +533,27 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = onUse(n.info, s) return newSymNode(s, n.info) 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): - 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) - result = semTemplBodySons(c, n) + 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: + result = semTemplBodySons(c, n) else: result = semTemplBodySons(c, n) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index dc01916d1..9680bc846 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -261,15 +261,15 @@ proc getQuality(s: PSym): range[0..100] = if exp.kind in {tyUntyped, tyTyped, tyGenericParam, tyAnything}: return 50 return 100 -template wholeSymTab(cond, section: untyped) = +template wholeSymTab(cond, section: untyped) {.dirty.} = var isLocal = true var scopeN = 0 for scope in walkScopes(c.currentScope): if scope == c.topLevelScope: isLocal = false dec scopeN for item in scope.symbols: - let it {.inject.} = item - var pm {.inject.}: PrefixMatch + let it = item + var pm: PrefixMatch if cond: outputs.add(symToSuggest(c.config, it, isLocal = isLocal, section, info, getQuality(it), pm, c.inTypeContext > 0, scopeN)) diff --git a/compiler/vm.nim b/compiler/vm.nim index 75f6cc1c3..31dec418f 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1137,7 +1137,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let node = regs[rb+i].regToNode node.info = c.debug[pc] 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] a.recSetFlagIsRef ensureKind(rkNode) diff --git a/doc/advopt.txt b/doc/advopt.txt index f5359c0c0..e3d7f018c 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -122,7 +122,7 @@ Advanced options: enable experimental language feature --legacy:$2 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 and that uses a shared heap via -d:useMalloc --profiler:on|off enable profiling; requires `import nimprof`, and diff --git a/doc/manual.rst b/doc/manual.rst index e1fb2fa59..7dad44423 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -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 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 diff --git a/lib/core/locks.nim b/lib/core/locks.nim index d6d579ba0..0143957ce 100644 --- a/lib/core/locks.nim +++ b/lib/core/locks.nim @@ -60,11 +60,11 @@ template withLock*(a: Lock, body: untyped) = ## Acquires the given lock, executes the statements in body and ## releases the lock after the statements finish executing. mixin acquire, release - a.acquire() + acquire(a) {.locks: [a].}: try: body finally: - a.release() + release(a) {.pop.} diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 9c47d9de9..efbd95089 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -984,7 +984,7 @@ when defined(nimTypeNames): # ---------------------- thread memory region ------------------------------- -template instantiateForRegion(allocator: untyped) = +template instantiateForRegion(allocator: untyped) {.dirty.} = {.push stackTrace: off.} when defined(fulldebug): @@ -1006,8 +1006,8 @@ template instantiateForRegion(allocator: untyped) = proc dealloc(p: pointer) = dealloc(allocator, p) - proc realloc(p: pointer, newsize: Natural): pointer = - result = realloc(allocator, p, newsize) + proc realloc(p: pointer, newSize: Natural): pointer = + result = realloc(allocator, p, newSize) when false: proc countFreeMem(): int = @@ -1054,13 +1054,13 @@ template instantiateForRegion(allocator: untyped) = else: dealloc(p) - proc reallocShared(p: pointer, newsize: Natural): pointer = + proc reallocShared(p: pointer, newSize: Natural): pointer = when hasThreadSupport: acquireSys(heapLock) - result = realloc(sharedHeap, p, newsize) + result = realloc(sharedHeap, p, newSize) releaseSys(heapLock) else: - result = realloc(p, newsize) + result = realloc(p, newSize) when hasThreadSupport: template sharedMemStatsShared(v: int) = diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 93aeb1bbc..a96310ee5 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -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]] = @[] -pkg "argparse" +#pkg "argparse" 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 "blscurve", "", true pkg "bncurve", "", true @@ -61,7 +61,7 @@ pkg "npeg" pkg "ormin", "nim c -o:orminn ormin.nim", true pkg "parsetoml" 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 "regex", "nim c src/regex", true 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 "stint", "nim c -o:stintt -r stint.nim" 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 "unicodedb" pkg "unicodeplus", "", true diff --git a/tests/astspec/tastspec.nim b/tests/astspec/tastspec.nim index 82c32f130..f9e35804d 100644 --- a/tests/astspec/tastspec.nim +++ b/tests/astspec/tastspec.nim @@ -6,6 +6,74 @@ action: compile 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 = arg.expectKind nnkStmtListExpr arg[0].expectKind(nnkVarSection) @@ -49,34 +117,35 @@ static: echo "OK" - testPattern nnkIntLit(intVal = 42) , 42 - testPattern nnkInt8Lit(intVal = 42) , 42'i8 - testPattern nnkInt16Lit(intVal = 42) , 42'i16 - testPattern nnkInt32Lit(intVal = 42) , 42'i32 - testPattern nnkInt64Lit(intVal = 42) , 42'i64 - testPattern nnkUInt8Lit(intVal = 42) , 42'u8 - testPattern nnkUInt16Lit(intVal = 42) , 42'u16 - testPattern nnkUInt32Lit(intVal = 42) , 42'u32 - testPattern nnkUInt64Lit(intVal = 42) , 42'u64 - #testPattern nnkFloat64Lit(floatVal = 42.0) , 42.0 - testPattern nnkFloat32Lit(floatVal = 42.0) , 42.0'f32 - #testPattern nnkFloat64Lit(floatVal = 42.0) , 42.0'f64 - testPattern nnkStrLit(strVal = "abc") , "abc" - testPattern nnkRStrLit(strVal = "abc") , r"abc" - testPattern nnkTripleStrLit(strVal = "abc") , """abc""" - testPattern nnkCharLit(intVal = 32) , ' ' - testPattern nnkNilLit() , nil - testPattern nnkIdent(strVal = "myIdentifier") , myIdentifier - - testPatternFail nnkInt8Lit(intVal = 42) , 42'i16 - testPatternFail nnkInt16Lit(intVal = 42) , 42'i8 + testPattern nnkIntLit(intVal = 42), 42 + testPattern nnkInt8Lit(intVal = 42), 42'i8 + testPattern nnkInt16Lit(intVal = 42), 42'i16 + testPattern nnkInt32Lit(intVal = 42), 42'i32 + testPattern nnkInt64Lit(intVal = 42), 42'i64 + testPattern nnkUInt8Lit(intVal = 42), 42'u8 + testPattern nnkUInt16Lit(intVal = 42), 42'u16 + testPattern nnkUInt32Lit(intVal = 42), 42'u32 + testPattern nnkUInt64Lit(intVal = 42), 42'u64 + #testPattern nnkFloat64Lit(floatVal = 42.0), 42.0 + testPattern nnkFloat32Lit(floatVal = 42.0), 42.0'f32 + #testPattern nnkFloat64Lit(floatVal = 42.0), 42.0'f64 + testPattern nnkStrLit(strVal = "abc"), "abc" + testPattern nnkRStrLit(strVal = "abc"), r"abc" + testPattern nnkTripleStrLit(strVal = "abc"), """abc""" + testPattern nnkCharLit(intVal = 32), ' ' + testPattern nnkNilLit(), nil + testPattern nnkIdent(strVal = "myIdentifier"), myIdentifier + + 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. macro scope(arg: untyped): untyped = let procSym = genSym(nskProc) result = quote do: - proc `procSym`(): void {.compileTime.} = + proc `procSym`() {.compileTime.} = `arg` `procSym`() @@ -85,7 +154,7 @@ static: ## Command call scope: - let ast = quote do: + let ast = myquote: echo "abc", "xyz" ast.matchAst: @@ -95,7 +164,7 @@ static: ## Call with ``()`` scope: - let ast = quote do: + let ast = myquote: echo("abc", "xyz") ast.matchAst: @@ -140,7 +209,7 @@ static: scope: - let ast = quote do: + let ast = myquote: ? "xyz" ast.matchAst(err): @@ -155,7 +224,7 @@ static: scope: - let ast = quote do: + let ast = myquote: proc identifier* ast[0].matchAst(err): @@ -185,7 +254,7 @@ static: ## Call with raw string literal scope: - let ast = quote do: + let ast = myquote: echo"abc" @@ -230,7 +299,7 @@ static: scope: - let ast = quote do: + let ast = myquote: cast[T](x) ast.matchAst: @@ -242,7 +311,7 @@ static: scope: - let ast = quote do: + let ast = myquote: x.y ast.matchAst: @@ -264,7 +333,7 @@ static: scope: - let ast = quote do: + let ast = myquote: (1, 2, (3)) ast.matchAst: @@ -276,7 +345,7 @@ static: scope: - let ast = quote do: + let ast = myquote: {1, 2, 3} ast.matchAst: @@ -285,7 +354,7 @@ static: scope: - let ast = quote do: + let ast = myquote: {a: 3, b: 5} ast.matchAst: @@ -300,7 +369,7 @@ static: scope: - let ast = quote do: + let ast = myquote: [1, 2, 3] ast.matchAst: @@ -312,7 +381,7 @@ static: scope: - let ast = quote do: + let ast = myquote: 1..3 ast.matchAst: @@ -328,7 +397,7 @@ static: scope: - let ast = quote do: + let ast = myquote: if cond1: expr1 elif cond2: expr2 else: expr3 ast.matchAst: @@ -343,7 +412,7 @@ static: scope: - let ast = quote do: + let ast = myquote: ## This is a comment ## This is part of the first comment stmt1 @@ -357,12 +426,12 @@ static: ): echo "ok" else: - echo "NOT OK!!!" + echo "warning!" echo ast.treeRepr echo "TEST causes no fail, because of a regression in Nim." scope: - let ast = quote do: + let ast = myquote: {.emit: "#include <stdio.h>".} ast.matchAst: @@ -375,7 +444,7 @@ static: echo "ok" scope: - let ast = quote do: + let ast = myquote: {.pragma: cdeclRename, cdecl.} ast.matchAst: @@ -391,7 +460,7 @@ static: scope: - let ast = quote do: + let ast = myquote: if cond1: stmt1 elif cond2: @@ -413,7 +482,7 @@ static: scope: - let ast = quote do: + let ast = myquote: x = 42 ast.matchAst: @@ -423,7 +492,7 @@ static: scope: - let ast = quote do: + let ast = myquote: stmt1 stmt2 stmt3 @@ -439,7 +508,7 @@ static: scope: - let ast = quote do: + let ast = myquote: case expr1 of expr2, expr3..expr4: stmt1 @@ -464,7 +533,7 @@ static: scope: - let ast = quote do: + let ast = myquote: while expr1: stmt1 @@ -477,7 +546,7 @@ static: scope: - let ast = quote do: + let ast = myquote: for ident1, ident2 in expr1: stmt1 @@ -490,7 +559,7 @@ static: scope: - let ast = quote do: + let ast = myquote: try: stmt1 except e1, e2: @@ -517,7 +586,7 @@ static: scope: - let ast = quote do: + let ast = myquote: return expr1 ast.matchAst: @@ -528,7 +597,7 @@ static: ## Continue statement scope: - let ast = quote do: + let ast = myquote: continue ast.matchAst: @@ -539,7 +608,7 @@ static: scope: - let ast = quote do: + let ast = myquote: break otherLocation ast.matchAst: @@ -550,10 +619,12 @@ static: scope: - let ast = quote do: + template blockStatement {.dirty.} = block name: discard + let ast = getAst(blockStatement()) + ast.matchAst: of nnkBlockStmt(ident"name", nnkStmtList): echo "ok" @@ -562,7 +633,7 @@ static: scope: - let ast = quote do: + let ast = myquote: asm """some asm""" ast.matchAst: @@ -576,7 +647,7 @@ static: scope: - let ast = quote do: + let ast = myquote: import math ast.matchAst: @@ -585,7 +656,7 @@ static: scope: - let ast = quote do: + let ast = myquote: import math except pow ast.matchAst: @@ -594,7 +665,7 @@ static: scope: - let ast = quote do: + let ast = myquote: import strutils as su ast.matchAst: @@ -611,7 +682,7 @@ static: scope: - let ast = quote do: + let ast = myquote: from math import pow ast.matchAst: @@ -622,7 +693,7 @@ static: scope: - let ast = quote do: + let ast = myquote: export unsigned ast.matchAst: @@ -631,7 +702,7 @@ static: scope: - let ast = quote do: + let ast = myquote: export math except pow # we're going to implement our own exponentiation ast.matchAst: @@ -642,7 +713,7 @@ static: scope: - let ast = quote do: + let ast = myquote: include blocks ast.matchAst: @@ -653,7 +724,7 @@ static: scope: - let ast = quote do: + let ast = myquote: var a = 3 ast.matchAst: @@ -670,7 +741,7 @@ static: scope: - let ast = quote do: + let ast = myquote: let a = 3 ast.matchAst: @@ -687,7 +758,7 @@ static: scope: - let ast = quote do: + let ast = myquote: const a = 3 ast.matchAst: @@ -704,7 +775,7 @@ static: scope: - let ast = quote do: + let ast = myquote: type A = int ast.matchAst: @@ -719,7 +790,7 @@ static: scope: - let ast = quote do: + let ast = myquote: type MyInt = distinct int ast.peelOff({nnkTypeSection}).matchAst: @@ -735,7 +806,7 @@ static: scope: - let ast = quote do: + let ast = myquote: type A[T] = expr1 ast.matchAst: @@ -757,7 +828,7 @@ static: scope: - let ast = quote do: + let ast = myquote: type IO = object of RootObj ast.peelOff(nnkTypeSection).matchAst: @@ -840,7 +911,7 @@ static: scope: - let ast = quote do: + let ast = myquote: type X = enum First @@ -853,7 +924,7 @@ static: scope: - let ast = quote do: + let ast = myquote: type Con = concept x,y,z (x & y & z) is string @@ -865,7 +936,7 @@ static: scope: - let astX = quote do: + let astX = myquote: type A[T: static[int]] = object @@ -880,7 +951,7 @@ static: scope: - let ast = quote do: + let ast = myquote: type MyProc[T] = proc(x: T) ast.peelOff({nnkStmtList, nnkTypeSection}).matchAst(err): @@ -952,7 +1023,7 @@ static: proc hello*[T: SomeInteger](x: int = 3, y: float32): int {.inline.} = discard scope: - var ast = quote do: + var ast = myquote: proc foobar(a, b: int): void ast = ast[3] @@ -971,7 +1042,7 @@ static: scope: - let ast = quote do: + let ast = myquote: proc hello(): var int ast[3].matchAst: # subAst @@ -986,7 +1057,7 @@ static: scope: - let ast = quote do: + let ast = myquote: iterator nonsense[T](x: seq[T]): float {.closure.} = discard @@ -998,7 +1069,7 @@ static: scope: - let ast = quote do: + let ast = myquote: converter toBool(x: float): bool ast.matchAst: @@ -1008,7 +1079,7 @@ static: ## Template declaration scope: - let ast = quote do: + let ast = myquote: template optOpt{expr1}(a: int): int ast.matchAst: diff --git a/tests/template/tmore_regressions.nim b/tests/template/tmore_regressions.nim new file mode 100644 index 000000000..8b4b5fa4c --- /dev/null +++ b/tests/template/tmore_regressions.nim @@ -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() + diff --git a/tests/template/tparams_gensymed.nim b/tests/template/tparams_gensymed.nim index b19ed7afc..f7a02efa0 100644 --- a/tests/template/tparams_gensymed.nim +++ b/tests/template/tparams_gensymed.nim @@ -8,6 +8,7 @@ output: ''' 1 2 3 +wth ''' """ # bug #1915 @@ -130,3 +131,17 @@ template test() = doAssert(foo.len == 3) 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") diff --git a/tests/template/tredefinition.nim b/tests/template/tredefinition.nim new file mode 100644 index 000000000..8efc5ae2f --- /dev/null +++ b/tests/template/tredefinition.nim @@ -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() |