allows access to .compileTime vars at runtime (#12128)
This commit is contained in:
parent
58bcf6cd46
commit
a5e2db2ac5
5 changed files with 52 additions and 14 deletions
|
|
@ -2533,6 +2533,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||||
of skVar, skForVar, skResult, skLet:
|
of skVar, skForVar, skResult, skLet:
|
||||||
if {sfGlobal, sfThread} * sym.flags != {}:
|
if {sfGlobal, sfThread} * sym.flags != {}:
|
||||||
genVarPrototype(p.module, n)
|
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:
|
if sym.loc.r == nil or sym.loc.t == nil:
|
||||||
#echo "FAILED FOR PRCO ", p.prc.name.s
|
#echo "FAILED FOR PRCO ", p.prc.name.s
|
||||||
#echo renderTree(p.prc.ast, {renderIds})
|
#echo renderTree(p.prc.ast, {renderIds})
|
||||||
|
|
|
||||||
|
|
@ -271,19 +271,16 @@ proc genGotoVar(p: BProc; value: PNode) =
|
||||||
else:
|
else:
|
||||||
lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])
|
lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])
|
||||||
|
|
||||||
proc genSingleVar(p: BProc, a: PNode) =
|
proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
|
||||||
let vn = a.sons[0]
|
|
||||||
let v = vn.sym
|
|
||||||
if sfCompileTime in v.flags: return
|
|
||||||
if sfGoto in v.flags:
|
if sfGoto in v.flags:
|
||||||
# translate 'var state {.goto.} = X' into 'goto LX':
|
# translate 'var state {.goto.} = X' into 'goto LX':
|
||||||
genGotoVar(p, a.sons[2])
|
genGotoVar(p, value)
|
||||||
return
|
return
|
||||||
var targetProc = p
|
var targetProc = p
|
||||||
var traverseProc: Rope
|
var traverseProc: Rope
|
||||||
if sfGlobal in v.flags:
|
if sfGlobal in v.flags:
|
||||||
if v.flags * {sfImportc, sfExportc} == {sfImportc} and
|
if v.flags * {sfImportc, sfExportc} == {sfImportc} and
|
||||||
a.sons[2].kind == nkEmpty and
|
value.kind == nkEmpty and
|
||||||
v.loc.flags * {lfHeader, lfNoDecl} != {}:
|
v.loc.flags * {lfHeader, lfNoDecl} != {}:
|
||||||
return
|
return
|
||||||
if sfPure in v.flags:
|
if sfPure in v.flags:
|
||||||
|
|
@ -313,14 +310,13 @@ proc genSingleVar(p: BProc, a: PNode) =
|
||||||
if traverseProc != nil and not p.hcrOn:
|
if traverseProc != nil and not p.hcrOn:
|
||||||
registerTraverseProc(p, v, traverseProc)
|
registerTraverseProc(p, v, traverseProc)
|
||||||
else:
|
else:
|
||||||
let value = a.sons[2]
|
|
||||||
let imm = isAssignedImmediately(p.config, value)
|
let imm = isAssignedImmediately(p.config, value)
|
||||||
if imm and p.module.compileToCpp and p.splitDecls == 0 and
|
if imm and p.module.compileToCpp and p.splitDecls == 0 and
|
||||||
not containsHiddenPointer(v.typ):
|
not containsHiddenPointer(v.typ):
|
||||||
# C++ really doesn't like things like 'Foo f; f = x' as that invokes a
|
# C++ really doesn't like things like 'Foo f; f = x' as that invokes a
|
||||||
# parameterless constructor followed by an assignment operator. So we
|
# parameterless constructor followed by an assignment operator. So we
|
||||||
# generate better code here: 'Foo f = x;'
|
# generate better code here: 'Foo f = x;'
|
||||||
genLineDir(p, a)
|
genLineDir(p, vn)
|
||||||
let decl = localVarDecl(p, vn)
|
let decl = localVarDecl(p, vn)
|
||||||
var tmp: TLoc
|
var tmp: TLoc
|
||||||
if value.kind in nkCallKinds and value[0].kind == nkSym and
|
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",
|
lineCg(targetProc, cpsStmts, "if (hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1))$N",
|
||||||
[v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
|
[v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
|
||||||
startBlock(targetProc)
|
startBlock(targetProc)
|
||||||
if a.sons[2].kind != nkEmpty:
|
if value.kind != nkEmpty:
|
||||||
genLineDir(targetProc, a)
|
genLineDir(targetProc, vn)
|
||||||
loadInto(targetProc, a.sons[0], a.sons[2], v.loc)
|
loadInto(targetProc, vn, value, v.loc)
|
||||||
if forHcr:
|
if forHcr:
|
||||||
endBlock(targetProc)
|
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) =
|
proc genClosureVar(p: BProc, a: PNode) =
|
||||||
var immediateAsgn = a.sons[2].kind != nkEmpty
|
var immediateAsgn = a.sons[2].kind != nkEmpty
|
||||||
var v: TLoc
|
var v: TLoc
|
||||||
|
|
|
||||||
|
|
@ -839,7 +839,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||||
of wCompileTime:
|
of wCompileTime:
|
||||||
noVal(c, it)
|
noVal(c, it)
|
||||||
incl(sym.flags, sfCompileTime)
|
incl(sym.flags, sfCompileTime)
|
||||||
incl(sym.loc.flags, lfNoDecl)
|
#incl(sym.loc.flags, lfNoDecl)
|
||||||
of wGlobal:
|
of wGlobal:
|
||||||
noVal(c, it)
|
noVal(c, it)
|
||||||
incl(sym.flags, sfGlobal)
|
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}:
|
elif comesFromPush and whichKeyword(ident) in {wTags, wRaises}:
|
||||||
discard "ignore the .push pragma; it doesn't apply"
|
discard "ignore the .push pragma; it doesn't apply"
|
||||||
else:
|
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}):
|
skField, skProc, skFunc, skConverter, skMethod, skType}):
|
||||||
n.sons[i] = semCustomPragma(c, it)
|
n.sons[i] = semCustomPragma(c, it)
|
||||||
elif sym != nil:
|
elif sym != nil:
|
||||||
|
|
|
||||||
|
|
@ -5928,6 +5928,31 @@ Is the same as:
|
||||||
proc astHelper(n: NimNode): NimNode {.compileTime.} =
|
proc astHelper(n: NimNode): NimNode {.compileTime.} =
|
||||||
result = n
|
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
|
noReturn pragma
|
||||||
---------------
|
---------------
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,10 @@ discard """
|
||||||
3
|
3
|
||||||
4:2
|
4:2
|
||||||
Got Hi
|
Got Hi
|
||||||
Got Hey'''
|
Got Hey
|
||||||
|
a
|
||||||
|
b
|
||||||
|
c'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# bug #404
|
# bug #404
|
||||||
|
|
@ -47,3 +50,9 @@ addStuff("Hey"): echo "Hey"
|
||||||
addStuff("Hi"): echo "Hi"
|
addStuff("Hi"): echo "Hi"
|
||||||
dump()
|
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]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue