diff options
-rw-r--r-- | changelog.md | 1 | ||||
-rw-r--r-- | compiler/commands.nim | 2 | ||||
-rw-r--r-- | compiler/evaltempl.nim | 10 | ||||
-rw-r--r-- | compiler/lookups.nim | 3 | ||||
-rw-r--r-- | compiler/options.nim | 1 | ||||
-rw-r--r-- | compiler/procfind.nim | 45 | ||||
-rw-r--r-- | compiler/sem.nim | 3 | ||||
-rw-r--r-- | compiler/semdata.nim | 2 | ||||
-rw-r--r-- | compiler/semexprs.nim | 13 | ||||
-rw-r--r-- | compiler/semstmts.nim | 6 | ||||
-rw-r--r-- | compiler/semtempl.nim | 27 | ||||
-rw-r--r-- | compiler/types.nim | 2 | ||||
-rw-r--r-- | compiler/vm.nim | 5 | ||||
-rw-r--r-- | compiler/vmdef.nim | 1 | ||||
-rw-r--r-- | testament/important_packages.nim | 4 | ||||
-rw-r--r-- | tests/macros/tmacros_issues.nim | 114 | ||||
-rw-r--r-- | tests/template/tparams_gensymed.nim | 126 |
17 files changed, 270 insertions, 95 deletions
diff --git a/changelog.md b/changelog.md index d9eaa5823..517919de9 100644 --- a/changelog.md +++ b/changelog.md @@ -264,6 +264,7 @@ proc mydiv(a, b): int {.raises: [].} = - Removed the `--oldNewlines` switch. - Removed the `--laxStrings` switch for mutating the internal zero terminator on strings. - Removed the `--oldast` switch. +- Removed the `--oldgensym` switch - `$getType(untyped)` is now "untyped" instead of "expr", `$getType(typed)` is now "typed" instead of "stmt". - Sink inference is now disabled per default and has to enabled explicitly via diff --git a/compiler/commands.nim b/compiler/commands.nim index 504550045..b67cd461f 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -871,8 +871,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "expandarc": expectArg(conf, switch, arg, pass, info) conf.arcToExpand[arg] = "T" - of "oldgensym": - processOnOffSwitchG(conf, {optNimV019}, arg, pass, info) of "useversion": expectArg(conf, switch, arg, pass, info) case arg diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 2a01a7911..c1024e2fe 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -21,6 +21,7 @@ type # new symbol config: ConfigRef ic: IdentCache + instID: int proc copyNode(ctx: TemplCtx, a, b: PNode): PNode = result = copyNode(a) @@ -53,8 +54,8 @@ 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) - if sfGenSym in s.flags and optNimV019 notin c.config.globalOptions: - result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $x.id), + if sfGenSym in s.flags: + result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $c.instID), if c.instLines: actual.info else: templ.info) else: result.add newSymNode(x, if c.instLines: actual.info else: templ.info) @@ -166,7 +167,7 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode = proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; conf: ConfigRef; - ic: IdentCache; + ic: IdentCache; instID: ref int; fromHlo=false): PNode = inc(conf.evalTemplateCounter) if conf.evalTemplateCounter > evalTemplateLimit: @@ -181,6 +182,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; ctx.config = conf ctx.ic = ic initIdTable(ctx.mapping) + ctx.instID = instID[] let body = tmpl.getBody #echo "instantion of ", renderTree(body, {renderIds}) @@ -203,3 +205,5 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; #if ctx.debugActive: # echo "instantion of ", renderTree(result, {renderIds}) dec(conf.evalTemplateCounter) + # The instID must be unique for every template instantiation, so we increment it here + inc instID[] diff --git a/compiler/lookups.nim b/compiler/lookups.nim index fe0e6fe7c..a3a2bf49c 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -316,8 +316,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = fixSpelling(n, ident, searchInScopes) errorUndeclaredIdentifier(c, n.info, ident.s) result = errorSym(c, n) - elif checkAmbiguity in flags and result != nil and - contains(c.ambiguousSymbols, result.id): + elif checkAmbiguity in flags and result != nil and result.id in c.ambiguousSymbols: errorUseQualifier(c, n.info, result) of nkSym: result = n.sym diff --git a/compiler/options.nim b/compiler/options.nim index 7beabe5e8..b5fb546ef 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -89,7 +89,6 @@ type # please make sure we have under 32 options # implementation optOwnedRefs # active if the Nim compiler knows about 'owned'. optMultiMethods - optNimV019 optBenchmarkVM # Enables cpuTime() in the VM optProduceAsm # produce assembler code optPanics # turn panics (sysFatal) into a process termination diff --git a/compiler/procfind.nim b/compiler/procfind.nim index f7dc6c379..1d897758a 100644 --- a/compiler/procfind.nim +++ b/compiler/procfind.nim @@ -28,40 +28,7 @@ proc equalGenericParams(procA, procB: PNode): bool = if not exprStructuralEquivalent(a.ast, b.ast): return result = true -proc searchForProcOld*(c: PContext, scope: PScope, fn: PSym): PSym = - # Searches for a forward declaration or a "twin" symbol of fn - # in the symbol table. If the parameter lists are exactly - # the same the sym in the symbol table is returned, else nil. - var it: TIdentIter - result = initIdentIter(it, scope.symbols, fn.name) - if isGenericRoutine(fn): - # we simply check the AST; this is imprecise but nearly the best what - # can be done; this doesn't work either though as type constraints are - # not kept in the AST .. - while result != nil: - if result.kind == fn.kind and isGenericRoutine(result): - let genR = result.ast[genericParamsPos] - let genF = fn.ast[genericParamsPos] - if exprStructuralEquivalent(genR, genF) and - exprStructuralEquivalent(result.ast[paramsPos], - fn.ast[paramsPos]) and - equalGenericParams(genR, genF): - return - result = nextIdentIter(it, scope.symbols) - else: - while result != nil: - if result.kind == fn.kind and not isGenericRoutine(result): - case equalParams(result.typ.n, fn.typ.n) - of paramsEqual: - return - of paramsIncompatible: - localError(c.config, fn.info, "overloaded '$1' leads to ambiguous calls" % fn.name.s) - return - of paramsNotEqual: - discard - result = nextIdentIter(it, scope.symbols) - -proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym = +proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym = const flags = {ExactGenericParams, ExactTypeDescValues, ExactConstraints, IgnoreCC} var it: TIdentIter @@ -83,16 +50,6 @@ proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym = discard result = nextIdentIter(it, scope.symbols) -proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym = - result = searchForProcNew(c, scope, fn) - when false: - let old = searchForProcOld(c, scope, fn) - if old != result: - echo "Mismatch in searchForProc: ", fn.info - debug fn.typ - debug if result != nil: result.typ else: nil - debug if old != nil: old.typ else: nil - when false: proc paramsFitBorrow(child, parent: PNode): bool = result = false diff --git a/compiler/sem.nim b/compiler/sem.nim index 1f98b4f87..a07f62ca0 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -472,7 +472,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, #if c.evalContext == nil: # c.evalContext = c.createEvalContext(emStatic) - result = evalMacroCall(c.module, c.graph, n, nOrig, sym) + result = evalMacroCall(c.module, c.graph, c.templInstCounter, n, nOrig, sym) if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, sym, flags) if c.config.macrosToExpand.hasKey(sym.name.s): @@ -521,6 +521,7 @@ proc myOpen(graph: ModuleGraph; module: PSym): PPassContext {.nosinks.} = c.semTypeNode = semTypeNode c.instTypeBoundOp = sigmatch.instTypeBoundOp c.hasUnresolvedArgs = hasUnresolvedArgs + c.templInstCounter = new int pushProcCon(c, module) pushOwner(c, c.module) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 8680fcac3..5bd6c4cf2 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -35,7 +35,6 @@ type inTryStmt*: int # whether we are in a try statement; works also # in standalone ``except`` and ``finally`` next*: PProcCon # used for stacking procedure contexts - wasForwarded*: bool # whether the current proc has a separate header mappingExists*: bool mapping*: TIdTable caseContext*: seq[tuple[n: PNode, idx: int]] @@ -85,6 +84,7 @@ type # this is used so that generic instantiations # can access private object fields instCounter*: int # to prevent endless instantiations + templInstCounter*: ref int # gives every template instantiation a unique id ambiguousSymbols*: IntSet # ids of all ambiguous symbols (cannot # store this info in the syms themselves!) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index fd9203f34..cdd0f61dd 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -33,7 +33,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, c.cache, efFromHlo in flags) + result = evalTemplate(n, s, getCurrOwner(c), c.config, c.cache, c.templInstCounter, efFromHlo in flags) if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags) popInfoContext(c.config) @@ -1203,17 +1203,6 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = elif sfGenSym in s.flags: # the owner should have been set by now by addParamOrResult internalAssert c.config, s.owner != nil - if c.p.wasForwarded: - # gensym'ed parameters that nevertheless have been forward declared - # need a special fixup: - let realParam = c.p.owner.typ.n[s.position+1] - internalAssert c.config, realParam.kind == nkSym and realParam.sym.kind == skParam - return newSymNode(c.p.owner.typ.n[s.position+1].sym, n.info) - elif c.p.owner.kind == skMacro: - # gensym'ed macro parameters need a similar hack (see bug #1944): - var u = searchInScopes(c, s.name) - internalAssert c.config, u != nil and u.kind == skParam and u.owner == s.owner - return newSymNode(u, n.info) result = newSymNode(s, n.info) of skVar, skLet, skResult, skForVar: if s.magic == mNimvm: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index c88ff3609..68f0cee64 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1898,6 +1898,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, incl(s.typ.flags, tfNoSideEffect) var proto: PSym = if isAnon: nil else: searchForProc(c, oldScope, s) + if proto == nil and sfForward in s.flags: + #This is a definition that shares its sym with its forward declaration (generated by a macro), + #if the symbol is also gensymmed we won't find it with searchForProc, so we check here + proto = s if proto == nil: if s.kind == skIterator: if s.typ.callConv != ccClosure: @@ -1943,7 +1947,6 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, addGenericParamListToScope(c, proto.ast[genericParamsPos]) addParams(c, proto.typ.n, proto.kind) proto.info = s.info # more accurate line information - s.typ = proto.typ proto.options = s.options s = proto n[genericParamsPos] = proto.ast[genericParamsPos] @@ -1983,7 +1986,6 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if n[genericParamsPos].kind == nkEmpty or usePseudoGenerics: if not usePseudoGenerics and s.magic == mNone: paramsTypeCheck(c, s.typ) - c.p.wasForwarded = proto != nil maybeAddResult(c, s, n) # semantic checking also needed with importc in case used in VM s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos])) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index c9cd82833..e771e17c4 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -145,9 +145,6 @@ proc getIdentNode(c: var TemplCtx, n: PNode): PNode = illFormedAst(n, c.c.config) result = n -template oldCheck(cx: TemplCtx; cond: bool): bool = - (optNimV019 notin cx.c.config.globalOptions or cond) - proc isTemplParam(c: TemplCtx, n: PNode): bool {.inline.} = result = n.kind == nkSym and n.sym.kind == skParam and n.sym.owner == c.owner and sfTemplateParam in n.sym.flags @@ -215,21 +212,7 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = closeScope(c) let ident = getIdentNode(c, n) if not isTemplParam(c, ident): - # fix #2670, consider: - # - # when b: - # var a = "hi" - # else: - # var a = 5 - # echo a - # - # We need to ensure that both 'a' produce the same gensym'ed symbol. - # So we need only check the *current* scope. - let s = localSearchInScope(c.c, considerQuotedIdent(c.c, ident)) - if s != nil and s.owner == c.owner and sfGenSym in s.flags: - onUse(n.info, s) - replaceIdentBySym(c.c, n, newSymNode(s, n.info)) - elif n.kind != nkSym: + if n.kind != nkSym: let local = newGenSym(k, ident, c) addPrelimDecl(c.c, local) styleCheckDef(c.c.config, n.info, local) @@ -560,16 +543,16 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = if n.kind == nkDotExpr: result = n result[0] = semTemplBody(c, n[0]) - if optNimV019 notin c.c.config.globalOptions: inc c.noGenSym + inc c.noGenSym result[1] = semTemplBody(c, n[1]) - if optNimV019 notin c.c.config.globalOptions: dec c.noGenSym + dec c.noGenSym else: result = semTemplBodySons(c, n) of nkExprColonExpr, nkExprEqExpr: if n.len == 2: - if optNimV019 notin c.c.config.globalOptions: inc c.noGenSym + inc c.noGenSym result[0] = semTemplBody(c, n[0]) - if optNimV019 notin c.c.config.globalOptions: dec c.noGenSym + dec c.noGenSym result[1] = semTemplBody(c, n[1]) else: result = semTemplBodySons(c, n) diff --git a/compiler/types.nim b/compiler/types.nim index b0581e96a..94fe8fd54 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -936,7 +936,7 @@ proc equalParams(a, b: PNode): TParamsEquality = discard of paramsIncompatible: result = paramsIncompatible - if (m.name.id != n.name.id): + if m.name.id != n.name.id: # BUGFIX return paramsNotEqual # paramsIncompatible; # continue traversal! If not equal, we can return immediately; else diff --git a/compiler/vm.nim b/compiler/vm.nim index 1edcace82..bb217c4db 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1233,7 +1233,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, c.cache) + var a = evalTemplate(macroCall, prc, genSymOwner, c.config, c.cache, c.templInstCounter) if a.kind == nkStmtList and a.len == 1: a = a[0] a.recSetFlagIsRef ensureKind(rkNode) @@ -2232,7 +2232,7 @@ proc errorNode(owner: PSym, n: PNode): PNode = result.typ = newType(tyError, owner) result.typ.flags.incl tfCheckedForDestructor -proc evalMacroCall*(module: PSym; g: ModuleGraph; +proc evalMacroCall*(module: PSym; g: ModuleGraph; templInstCounter: ref int; n, nOrig: PNode, sym: PSym): PNode = if g.config.errorCounter > 0: return errorNode(module, n) @@ -2253,6 +2253,7 @@ proc evalMacroCall*(module: PSym; g: ModuleGraph; c.mode = emStaticStmt c.comesFromHeuristic.line = 0'u16 c.callsite = nOrig + c.templInstCounter = templInstCounter let start = genProc(c, sym) var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil) diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 9228ce5eb..59ffe9581 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -262,6 +262,7 @@ type graph*: ModuleGraph oldErrorCount*: int profiler*: Profiler + templInstCounter*: ref int # gives every template instantiation a unique ID, needed here for getAst PStackFrame* = ref TStackFrame TStackFrame* = object diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 8fa7c99a9..a4187f64c 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -12,7 +12,7 @@ var packages2*: seq[tuple[name, cmd: string; hasDeps: bool; url: string, useHead # pkg1 "alea", true pkg1 "argparse" pkg1 "arraymancer", true, "nim c tests/tests_cpu.nim" -pkg1 "ast_pattern_matching", false, "nim c -r --oldgensym:on tests/test1.nim" +#pkg1 "ast_pattern_matching", false, "nim c -r --oldgensym:on tests/test1.nim" pkg1 "awk", true pkg1 "bigints" pkg1 "binaryheap", false, "nim c -r binaryheap.nim" @@ -95,7 +95,7 @@ pkg2 "optionsutils" pkg2 "ormin", true, "nim c -o:orminn ormin.nim" pkg2 "parsetoml" pkg2 "patty" -pkg2 "plotly", true, "nim c --oldgensym:on examples/all.nim" +pkg2 "plotly", true, "nim c examples/all.nim" pkg2 "pnm" pkg2 "polypbren" pkg2 "prologue", true, "nim c -d:release -r tests/test_compile/test_compile.nim" diff --git a/tests/macros/tmacros_issues.nim b/tests/macros/tmacros_issues.nim index 255a98ba9..882c0735d 100644 --- a/tests/macros/tmacros_issues.nim +++ b/tests/macros/tmacros_issues.nim @@ -35,6 +35,10 @@ test foo1 foo2 foo3 +true +false +true +false ''' """ @@ -289,3 +293,113 @@ proc main() = 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 + diff --git a/tests/template/tparams_gensymed.nim b/tests/template/tparams_gensymed.nim index a35d878d5..cfd354a74 100644 --- a/tests/template/tparams_gensymed.nim +++ b/tests/template/tparams_gensymed.nim @@ -195,3 +195,129 @@ mystate_machine: state_push(S1) echo state_current() state_pop() + +# bug #15075 +block: #Doesn't work + template genGenTempl: untyped = + proc loop(locals: int) + proc loop(locals: int) = discard + genGenTempl() + let pool = loop + +block: #Doesn't work + macro genGenMacro: untyped = + quote do: + proc loop(locals: int) + proc loop(locals: int) = discard + genGenMacro() + let pool = loop + +block: #This works + proc loop(locals: int) + proc loop(locals: int) = discard + let pool = loop + +#Now somewhat recursive: +type Cont = ref object of RootObj + fn*: proc(c: Cont): Cont {.nimcall.} + +block: #Doesn't work + template genGenTempl(): untyped = + proc loop(locals: Cont): Cont + proc loop(locals: Cont): Cont = + return Cont(fn: loop) + proc doServer(): Cont = + return Cont(fn: loop) + genGenTempl() + discard doServer() + +block: #Doesn't work + macro genGenMacro(): untyped = + quote: + proc loop(locals: Cont): Cont + proc loop(locals: Cont): Cont = + return Cont(fn: loop) + proc doServer(): Cont = + return Cont(fn: loop) + genGenMacro() + discard doServer() + +block: #This works + proc loop(locals: Cont): Cont + proc loop(locals: Cont): Cont = + return Cont(fn: loop) + proc doServer(): Cont = + return Cont(fn: loop) + discard doServer() + +#And fully recursive: +block: #Doesn't work + template genGenTempl: untyped = + proc loop(locals: int) + proc loop(locals: int) = loop(locals) + genGenTempl() + let pool = loop + +block: #Doesn't work + macro genGenMacro: untyped = + quote do: + proc loop(locals: int) + proc loop(locals: int) = loop(locals) + genGenMacro() + let pool = loop + +block: #This works + proc loop(locals: int) + proc loop(locals: int) = loop(locals) + let pool = loop + +block: + template genAndCallLoop: untyped = + proc loop() {.gensym.} + proc loop() {.gensym.} = + discard + loop() + genAndCallLoop + +block: #Fully recursive and gensymmed: + template genGenTempl: untyped = + proc loop(locals: int) {.gensym.} + proc loop(locals: int) {.gensym.} = loop(locals) + let pool = loop + genGenTempl() + +block: #Make sure gensymmed symbol doesn't overwrite the forward decl + proc loop() + proc loop() = discard + template genAndCallLoop: untyped = + proc loop() {.gensym.} = + discard + loop() + genAndCallLoop() + +template genLoopDecl: untyped = + proc loop() +template genLoopDef: untyped = + proc loop() = discard +block: + genLoopDecl + genLoopDef + loop() +block: + proc loop() + genLoopDef + loop() +block: + genLoopDecl + proc loop() = discard + loop() + +block: #Gensymmed sym sharing forward decl + macro genGenMacro: untyped = + let sym = genSym(nskProc, "loop") + nnkStmtList.newTree( + newProc(sym, body = newEmptyNode()), + newCall(sym), + newProc(sym, body = newStmtList()), + ) + genGenMacro |