diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 0902e2e19e..253c67b246 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2533,6 +2533,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of skVar, skForVar, skResult, skLet: if {sfGlobal, sfThread} * sym.flags != {}: genVarPrototype(p.module, n) + if sfCompileTime in sym.flags: + genSingleVar(p, sym, n, astdef(sym)) + if sym.loc.r == nil or sym.loc.t == nil: #echo "FAILED FOR PRCO ", p.prc.name.s #echo renderTree(p.prc.ast, {renderIds}) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index ffeeb0db94..29e245ef73 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -271,19 +271,16 @@ proc genGotoVar(p: BProc; value: PNode) = else: lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope]) -proc genSingleVar(p: BProc, a: PNode) = - let vn = a.sons[0] - let v = vn.sym - if sfCompileTime in v.flags: return +proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = if sfGoto in v.flags: # translate 'var state {.goto.} = X' into 'goto LX': - genGotoVar(p, a.sons[2]) + genGotoVar(p, value) return var targetProc = p var traverseProc: Rope if sfGlobal in v.flags: if v.flags * {sfImportc, sfExportc} == {sfImportc} and - a.sons[2].kind == nkEmpty and + value.kind == nkEmpty and v.loc.flags * {lfHeader, lfNoDecl} != {}: return if sfPure in v.flags: @@ -313,14 +310,13 @@ proc genSingleVar(p: BProc, a: PNode) = if traverseProc != nil and not p.hcrOn: registerTraverseProc(p, v, traverseProc) else: - let value = a.sons[2] let imm = isAssignedImmediately(p.config, value) if imm and p.module.compileToCpp and p.splitDecls == 0 and not containsHiddenPointer(v.typ): # C++ really doesn't like things like 'Foo f; f = x' as that invokes a # parameterless constructor followed by an assignment operator. So we # generate better code here: 'Foo f = x;' - genLineDir(p, a) + genLineDir(p, vn) let decl = localVarDecl(p, vn) var tmp: TLoc if value.kind in nkCallKinds and value[0].kind == nkSym and @@ -363,12 +359,17 @@ proc genSingleVar(p: BProc, a: PNode) = lineCg(targetProc, cpsStmts, "if (hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1))$N", [v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc]) startBlock(targetProc) - if a.sons[2].kind != nkEmpty: - genLineDir(targetProc, a) - loadInto(targetProc, a.sons[0], a.sons[2], v.loc) + if value.kind != nkEmpty: + genLineDir(targetProc, vn) + loadInto(targetProc, vn, value, v.loc) if forHcr: endBlock(targetProc) +proc genSingleVar(p: BProc, a: PNode) = + let v = a[0].sym + if sfCompileTime in v.flags: return + genSingleVar(p, v, a[0], a.sons[2]) + proc genClosureVar(p: BProc, a: PNode) = var immediateAsgn = a.sons[2].kind != nkEmpty var v: TLoc diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 3de12cfa9e..6c70e4bad5 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -839,7 +839,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wCompileTime: noVal(c, it) incl(sym.flags, sfCompileTime) - incl(sym.loc.flags, lfNoDecl) + #incl(sym.loc.flags, lfNoDecl) of wGlobal: noVal(c, it) incl(sym.flags, sfGlobal) @@ -1131,7 +1131,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, elif comesFromPush and whichKeyword(ident) in {wTags, wRaises}: discard "ignore the .push pragma; it doesn't apply" else: - if sym == nil or (sym != nil and sym.kind in {skVar, skLet, skParam, + if sym == nil or (sym.kind in {skVar, skLet, skParam, skField, skProc, skFunc, skConverter, skMethod, skType}): n.sons[i] = semCustomPragma(c, it) elif sym != nil: diff --git a/doc/manual.rst b/doc/manual.rst index a8326d94d5..3c194b1d2e 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -5928,6 +5928,31 @@ Is the same as: proc astHelper(n: NimNode): NimNode {.compileTime.} = result = n +``compileTime`` variables are available at runtime too. This simplifies certain +idioms where variables are filled at compile-time (for example, lookup tables) +but accessed at runtime: + +.. code-block:: nim + :test: "nim c -r $1" + + import macros + + var nameToProc {.compileTime.}: seq[(string, proc (): string {.nimcall.})] + + macro registerProc(p: untyped): untyped = + result = newTree(nnkStmtList, p) + + let procName = p[0] + let procNameAsStr = $p[0] + result.add quote do: + nameToProc.add((`procNameAsStr`, `procName`)) + + proc foo: string {.registerProc.} = "foo" + proc bar: string {.registerProc.} = "bar" + proc baz: string {.registerProc.} = "baz" + + doAssert nameToProc[2][1]() == "baz" + noReturn pragma --------------- diff --git a/tests/vm/tcompiletimetable.nim b/tests/vm/tcompiletimetable.nim index e78c06536c..ece2ddfe90 100644 --- a/tests/vm/tcompiletimetable.nim +++ b/tests/vm/tcompiletimetable.nim @@ -3,7 +3,10 @@ discard """ 3 4:2 Got Hi -Got Hey''' +Got Hey +a +b +c''' """ # bug #404 @@ -47,3 +50,9 @@ addStuff("Hey"): echo "Hey" addStuff("Hi"): echo "Hi" dump() +# ensure .compileTime vars can be used at runtime: +import macros + +var xzzzz {.compileTime.}: array[3, string] = ["a", "b", "c"] + +for i in 0..high(xzzzz): echo xzzzz[i]