first steps for cleaner static/const distinction
This commit is contained in:
parent
1d8ff40f56
commit
c25ffbf262
19 changed files with 193 additions and 66 deletions
|
|
@ -396,6 +396,36 @@ type
|
||||||
mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError,
|
mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError,
|
||||||
mInstantiationInfo, mGetTypeInfo
|
mInstantiationInfo, mGetTypeInfo
|
||||||
|
|
||||||
|
# things that we can evaluate safely at compile time, even if not asked for it:
|
||||||
|
const
|
||||||
|
ctfeWhitelist* = {mNone, mUnaryLt, mSucc,
|
||||||
|
mPred, mInc, mDec, mOrd, mLengthOpenArray,
|
||||||
|
mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr,
|
||||||
|
mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64,
|
||||||
|
mDivI64, mModI64, mAddF64, mSubF64, mMulF64, mDivF64,
|
||||||
|
mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI,
|
||||||
|
mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinI64, mMaxI64,
|
||||||
|
mMinF64, mMaxF64, mAddU, mSubU, mMulU,
|
||||||
|
mDivU, mModU, mAddU64, mSubU64, mMulU64, mDivU64, mModU64, mEqI, mLeI,
|
||||||
|
mLtI,
|
||||||
|
mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64,
|
||||||
|
mLeU, mLtU, mLeU64, mLtU64,
|
||||||
|
mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef,
|
||||||
|
mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI,
|
||||||
|
mUnaryMinusI64, mAbsI, mAbsI64, mNot,
|
||||||
|
mUnaryPlusI, mBitnotI, mUnaryPlusI64,
|
||||||
|
mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64,
|
||||||
|
mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32,
|
||||||
|
mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr,
|
||||||
|
mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr,
|
||||||
|
mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet,
|
||||||
|
mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mConArrArr, mConArrT,
|
||||||
|
mConTArr, mConTT, mSlice,
|
||||||
|
mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
||||||
|
mInRange, mInSet, mRepr,
|
||||||
|
mRand,
|
||||||
|
mCopyStr, mCopyStrLast}
|
||||||
|
|
||||||
type
|
type
|
||||||
PNode* = ref TNode
|
PNode* = ref TNode
|
||||||
TNodeSeq* = seq[PNode]
|
TNodeSeq* = seq[PNode]
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ proc genVarTuple(p: BProc, n: PNode) =
|
||||||
var t = tup.t
|
var t = tup.t
|
||||||
for i in countup(0, L-3):
|
for i in countup(0, L-3):
|
||||||
var v = n.sons[i].sym
|
var v = n.sons[i].sym
|
||||||
|
if sfCompileTime in v.flags: continue
|
||||||
if sfGlobal in v.flags and v.kind != skForVar:
|
if sfGlobal in v.flags and v.kind != skForVar:
|
||||||
assignGlobalVar(p, v)
|
assignGlobalVar(p, v)
|
||||||
genObjectInit(p, cpsInit, v.typ, v.loc, true)
|
genObjectInit(p, cpsInit, v.typ, v.loc, true)
|
||||||
|
|
@ -46,6 +47,7 @@ proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} =
|
||||||
|
|
||||||
proc genSingleVar(p: BProc, a: PNode) =
|
proc genSingleVar(p: BProc, a: PNode) =
|
||||||
var v = a.sons[0].sym
|
var v = a.sons[0].sym
|
||||||
|
if sfCompileTime in v.flags: return
|
||||||
var immediateAsgn = a.sons[2].kind != nkEmpty
|
var immediateAsgn = a.sons[2].kind != nkEmpty
|
||||||
if sfGlobal in v.flags and v.kind != skForVar:
|
if sfGlobal in v.flags and v.kind != skForVar:
|
||||||
assignGlobalVar(p, v)
|
assignGlobalVar(p, v)
|
||||||
|
|
|
||||||
|
|
@ -183,6 +183,7 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
|
||||||
of "threads": result = contains(gGlobalOptions, optThreads)
|
of "threads": result = contains(gGlobalOptions, optThreads)
|
||||||
of "taintmode": result = contains(gGlobalOptions, optTaintMode)
|
of "taintmode": result = contains(gGlobalOptions, optTaintMode)
|
||||||
of "tlsemulation": result = contains(gGlobalOptions, optTlsEmulation)
|
of "tlsemulation": result = contains(gGlobalOptions, optTlsEmulation)
|
||||||
|
of "implicitstatic": result = contains(gOptions, optImplicitStatic)
|
||||||
else: InvalidCmdLineOption(passCmd1, switch, info)
|
else: InvalidCmdLineOption(passCmd1, switch, info)
|
||||||
|
|
||||||
proc processPath(path: string): string =
|
proc processPath(path: string): string =
|
||||||
|
|
@ -312,6 +313,8 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
|
||||||
of "threads": ProcessOnOffSwitchG({optThreads}, arg, pass, info)
|
of "threads": ProcessOnOffSwitchG({optThreads}, arg, pass, info)
|
||||||
of "tlsemulation": ProcessOnOffSwitchG({optTlsEmulation}, arg, pass, info)
|
of "tlsemulation": ProcessOnOffSwitchG({optTlsEmulation}, arg, pass, info)
|
||||||
of "taintmode": ProcessOnOffSwitchG({optTaintMode}, arg, pass, info)
|
of "taintmode": ProcessOnOffSwitchG({optTaintMode}, arg, pass, info)
|
||||||
|
of "implicitstatic":
|
||||||
|
ProcessOnOffSwitch({optImplicitStatic}, arg, pass, info)
|
||||||
of "opt":
|
of "opt":
|
||||||
expectArg(switch, arg, pass, info)
|
expectArg(switch, arg, pass, info)
|
||||||
case arg.normalize
|
case arg.normalize
|
||||||
|
|
|
||||||
|
|
@ -263,7 +263,8 @@ proc evalVar(c: PEvalContext, n: PNode): PNode =
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
var a = n.sons[i]
|
var a = n.sons[i]
|
||||||
if a.kind == nkCommentStmt: continue
|
if a.kind == nkCommentStmt: continue
|
||||||
assert(a.kind == nkIdentDefs)
|
if a.kind != nkIdentDefs: return raiseCannotEval(c, n.info)
|
||||||
|
# XXX var (x, y) = z support?
|
||||||
#assert(a.sons[0].kind == nkSym) can happen for transformed vars
|
#assert(a.sons[0].kind == nkSym) can happen for transformed vars
|
||||||
if a.sons[2].kind != nkEmpty:
|
if a.sons[2].kind != nkEmpty:
|
||||||
result = evalAux(c, a.sons[2], {})
|
result = evalAux(c, a.sons[2], {})
|
||||||
|
|
@ -311,17 +312,18 @@ proc evalCall(c: PEvalContext, n: PNode): PNode =
|
||||||
if isSpecial(result): return
|
if isSpecial(result): return
|
||||||
prc = result
|
prc = result
|
||||||
# bind the actual params to the local parameter of a new binding
|
# bind the actual params to the local parameter of a new binding
|
||||||
if prc.kind == nkSym:
|
if prc.kind != nkSym: InternalError(n.info, "evalCall " & n.renderTree)
|
||||||
d.prc = prc.sym
|
d.prc = prc.sym
|
||||||
if prc.sym.kind notin {skProc, skConverter}:
|
if prc.sym.kind notin {skProc, skConverter, skMacro}:
|
||||||
InternalError(n.info, "evalCall")
|
InternalError(n.info, "evalCall")
|
||||||
|
|
||||||
for i in countup(1, sonsLen(n) - 1):
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
result = evalAux(c, n.sons[i], {})
|
result = evalAux(c, n.sons[i], {})
|
||||||
if isSpecial(result): return
|
if isSpecial(result): return
|
||||||
d.params[i] = result
|
d.params[i] = result
|
||||||
if n.typ != nil: d.params[0] = getNullValue(n.typ, n.info)
|
if n.typ != nil: d.params[0] = getNullValue(n.typ, n.info)
|
||||||
pushStackFrame(c, d)
|
pushStackFrame(c, d)
|
||||||
result = evalAux(c, prc, {})
|
result = evalAux(c, prc.sym.getBody, {})
|
||||||
if result.kind == nkExceptBranch: return
|
if result.kind == nkExceptBranch: return
|
||||||
if n.typ != nil: result = d.params[0]
|
if n.typ != nil: result = d.params[0]
|
||||||
popStackFrame(c)
|
popStackFrame(c)
|
||||||
|
|
@ -489,7 +491,8 @@ proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
|
||||||
var s = n.sym
|
var s = n.sym
|
||||||
case s.kind
|
case s.kind
|
||||||
of skProc, skConverter, skMacro:
|
of skProc, skConverter, skMacro:
|
||||||
result = s.getBody
|
result = n
|
||||||
|
#result = s.getBody
|
||||||
of skVar, skLet, skForVar, skTemp, skResult:
|
of skVar, skLet, skForVar, skTemp, skResult:
|
||||||
if sfGlobal notin s.flags:
|
if sfGlobal notin s.flags:
|
||||||
result = evalVariable(c.tos, s, flags)
|
result = evalVariable(c.tos, s, flags)
|
||||||
|
|
@ -501,7 +504,7 @@ proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
|
||||||
of skConst: result = s.ast
|
of skConst: result = s.ast
|
||||||
of skEnumField: result = newIntNodeT(s.position, n)
|
of skEnumField: result = newIntNodeT(s.position, n)
|
||||||
else: result = nil
|
else: result = nil
|
||||||
if result == nil or sfImportc in s.flags:
|
if result == nil or {sfImportc, sfForward} * s.flags != {}:
|
||||||
result = raiseCannotEval(c, n.info)
|
result = raiseCannotEval(c, n.info)
|
||||||
|
|
||||||
proc evalIncDec(c: PEvalContext, n: PNode, sign: biggestInt): PNode =
|
proc evalIncDec(c: PEvalContext, n: PNode, sign: biggestInt): PNode =
|
||||||
|
|
@ -532,10 +535,13 @@ proc evalEcho(c: PEvalContext, n: PNode): PNode =
|
||||||
result = emptyNode
|
result = emptyNode
|
||||||
|
|
||||||
proc evalExit(c: PEvalContext, n: PNode): PNode =
|
proc evalExit(c: PEvalContext, n: PNode): PNode =
|
||||||
result = evalAux(c, n.sons[1], {})
|
if c.mode in {emRepl, emStatic}:
|
||||||
if isSpecial(result): return
|
result = evalAux(c, n.sons[1], {})
|
||||||
Message(n.info, hintQuitCalled)
|
if isSpecial(result): return
|
||||||
quit(int(getOrdValue(result)))
|
Message(n.info, hintQuitCalled)
|
||||||
|
quit(int(getOrdValue(result)))
|
||||||
|
else:
|
||||||
|
result = raiseCannotEval(c, n.info)
|
||||||
|
|
||||||
proc evalOr(c: PEvalContext, n: PNode): PNode =
|
proc evalOr(c: PEvalContext, n: PNode): PNode =
|
||||||
result = evalAux(c, n.sons[1], {})
|
result = evalAux(c, n.sons[1], {})
|
||||||
|
|
@ -636,19 +642,22 @@ proc evalConvCStrToStr(c: PEvalContext, n: PNode): PNode =
|
||||||
result.typ = n.typ
|
result.typ = n.typ
|
||||||
|
|
||||||
proc evalRaise(c: PEvalContext, n: PNode): PNode =
|
proc evalRaise(c: PEvalContext, n: PNode): PNode =
|
||||||
if n.sons[0].kind != nkEmpty:
|
if c.mode in {emRepl, emStatic}:
|
||||||
result = evalAux(c, n.sons[0], {})
|
if n.sons[0].kind != nkEmpty:
|
||||||
if isSpecial(result): return
|
result = evalAux(c, n.sons[0], {})
|
||||||
var a = result
|
if isSpecial(result): return
|
||||||
result = newNodeIT(nkExceptBranch, n.info, a.typ)
|
var a = result
|
||||||
addSon(result, a)
|
result = newNodeIT(nkExceptBranch, n.info, a.typ)
|
||||||
c.lastException = result
|
addSon(result, a)
|
||||||
elif c.lastException != nil:
|
c.lastException = result
|
||||||
result = c.lastException
|
elif c.lastException != nil:
|
||||||
else:
|
result = c.lastException
|
||||||
stackTrace(c, n, errExceptionAlreadyHandled)
|
else:
|
||||||
result = newNodeIT(nkExceptBranch, n.info, nil)
|
stackTrace(c, n, errExceptionAlreadyHandled)
|
||||||
addSon(result, ast.emptyNode)
|
result = newNodeIT(nkExceptBranch, n.info, nil)
|
||||||
|
addSon(result, ast.emptyNode)
|
||||||
|
else:
|
||||||
|
result = raiseCannotEval(c, n.info)
|
||||||
|
|
||||||
proc evalReturn(c: PEvalContext, n: PNode): PNode =
|
proc evalReturn(c: PEvalContext, n: PNode): PNode =
|
||||||
if n.sons[0].kind != nkEmpty:
|
if n.sons[0].kind != nkEmpty:
|
||||||
|
|
@ -1266,28 +1275,37 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
|
||||||
InternalError(n.info, "evalAux: returned nil " & $n.kind)
|
InternalError(n.info, "evalAux: returned nil " & $n.kind)
|
||||||
inc(gNestedEvals)
|
inc(gNestedEvals)
|
||||||
|
|
||||||
proc eval*(c: PEvalContext, n: PNode): PNode =
|
proc tryEval(c: PEvalContext, n: PNode): PNode =
|
||||||
## eval never returns nil! This simplifies the code a lot and
|
|
||||||
## makes it faster too.
|
|
||||||
var n = transform(c.module, n)
|
var n = transform(c.module, n)
|
||||||
gWhileCounter = evalMaxIterations
|
gWhileCounter = evalMaxIterations
|
||||||
gNestedEvals = evalMaxRecDepth
|
gNestedEvals = evalMaxRecDepth
|
||||||
result = evalAux(c, n, {})
|
result = evalAux(c, n, {})
|
||||||
|
|
||||||
|
proc eval*(c: PEvalContext, n: PNode): PNode =
|
||||||
|
## eval never returns nil! This simplifies the code a lot and
|
||||||
|
## makes it faster too.
|
||||||
|
result = tryEval(c, n)
|
||||||
if result.kind == nkExceptBranch:
|
if result.kind == nkExceptBranch:
|
||||||
if sonsLen(result) >= 1:
|
if sonsLen(result) >= 1:
|
||||||
stackTrace(c, n, errUnhandledExceptionX, typeToString(result.typ))
|
stackTrace(c, n, errUnhandledExceptionX, typeToString(result.typ))
|
||||||
else:
|
else:
|
||||||
stackTrace(c, n, errCannotInterpretNodeX, renderTree(n))
|
stackTrace(c, n, errCannotInterpretNodeX, renderTree(n))
|
||||||
|
|
||||||
proc evalConstExpr*(module: PSym, e: PNode): PNode =
|
proc evalConstExprAux(module: PSym, e: PNode, mode: TEvalMode): PNode =
|
||||||
var p = newEvalContext(module, "", emConst)
|
var p = newEvalContext(module, "", mode)
|
||||||
var s = newStackFrame()
|
var s = newStackFrame()
|
||||||
s.call = e
|
s.call = e
|
||||||
pushStackFrame(p, s)
|
pushStackFrame(p, s)
|
||||||
result = eval(p, e)
|
result = tryEval(p, e)
|
||||||
if result != nil and result.kind == nkExceptBranch: result = nil
|
if result != nil and result.kind == nkExceptBranch: result = nil
|
||||||
popStackFrame(p)
|
popStackFrame(p)
|
||||||
|
|
||||||
|
proc evalConstExpr*(module: PSym, e: PNode): PNode =
|
||||||
|
result = evalConstExprAux(module, e, emConst)
|
||||||
|
|
||||||
|
proc evalStaticExpr*(module: PSym, e: PNode): PNode =
|
||||||
|
result = evalConstExprAux(module, e, emStatic)
|
||||||
|
|
||||||
proc evalMacroCall*(c: PEvalContext, n: PNode, sym: PSym): PNode =
|
proc evalMacroCall*(c: PEvalContext, n: PNode, sym: PSym): PNode =
|
||||||
# XXX GlobalError() is ugly here, but I don't know a better solution for now
|
# XXX GlobalError() is ugly here, but I don't know a better solution for now
|
||||||
inc(evalTemplateCounter)
|
inc(evalTemplateCounter)
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,9 @@ type # please make sure we have under 32 options
|
||||||
optEndb, # embedded debugger
|
optEndb, # embedded debugger
|
||||||
optByRef, # use pass by ref for objects
|
optByRef, # use pass by ref for objects
|
||||||
# (for interfacing with C)
|
# (for interfacing with C)
|
||||||
optProfiler # profiler turned on
|
optProfiler, # profiler turned on
|
||||||
|
optImplicitStatic # optimization: implicit at compile time
|
||||||
|
# evaluation
|
||||||
TOptions* = set[TOption]
|
TOptions* = set[TOption]
|
||||||
TGlobalOption* = enum # **keep binary compatible**
|
TGlobalOption* = enum # **keep binary compatible**
|
||||||
gloptNone, optForceFullMake, optBoehmGC, optRefcGC, optDeadCodeElim,
|
gloptNone, optForceFullMake, optBoehmGC, optRefcGC, optDeadCodeElim,
|
||||||
|
|
|
||||||
|
|
@ -277,6 +277,7 @@ proc processOption(c: PContext, n: PNode) =
|
||||||
excl(gOptions, optOptimizeSpeed)
|
excl(gOptions, optOptimizeSpeed)
|
||||||
excl(gOptions, optOptimizeSize)
|
excl(gOptions, optOptimizeSize)
|
||||||
else: LocalError(n.info, errNoneSpeedOrSizeExpected)
|
else: LocalError(n.info, errNoneSpeedOrSizeExpected)
|
||||||
|
of wImplicitStatic: OnOff(c, n, {optImplicitStatic})
|
||||||
else: LocalError(n.info, errOptionExpected)
|
else: LocalError(n.info, errOptionExpected)
|
||||||
|
|
||||||
proc processPush(c: PContext, n: PNode, start: int) =
|
proc processPush(c: PContext, n: PNode, start: int) =
|
||||||
|
|
|
||||||
|
|
@ -85,13 +85,6 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
|
||||||
result = evalConstExpr(c.module, e)
|
result = evalConstExpr(c.module, e)
|
||||||
if result == nil or result.kind == nkEmpty:
|
if result == nil or result.kind == nkEmpty:
|
||||||
GlobalError(n.info, errConstExprExpected)
|
GlobalError(n.info, errConstExprExpected)
|
||||||
when false:
|
|
||||||
result = semExprWithType(c, n)
|
|
||||||
if result == nil:
|
|
||||||
GlobalError(n.info, errConstExprExpected)
|
|
||||||
return
|
|
||||||
result = getConstExpr(c.module, result)
|
|
||||||
if result == nil: GlobalError(n.info, errConstExprExpected)
|
|
||||||
|
|
||||||
proc semAndEvalConstExpr(c: PContext, n: PNode): PNode =
|
proc semAndEvalConstExpr(c: PContext, n: PNode): PNode =
|
||||||
result = semConstExpr(c, n)
|
result = semConstExpr(c, n)
|
||||||
|
|
|
||||||
|
|
@ -475,6 +475,39 @@ proc expectStringArg(c: PContext, n: PNode, i: int): PNode =
|
||||||
|
|
||||||
include semmagic
|
include semmagic
|
||||||
|
|
||||||
|
proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
||||||
|
result = n
|
||||||
|
if n.kind notin nkCallKinds or n.sons[0].kind != nkSym: return
|
||||||
|
var callee = n.sons[0].sym
|
||||||
|
|
||||||
|
if {sfNoSideEffect, sfCompileTime} * callee.flags != {} and
|
||||||
|
{sfForward, sfImportc} * callee.flags == {}:
|
||||||
|
if sfCompileTime notin callee.flags and
|
||||||
|
optImplicitCompileTime notin gOptions: return
|
||||||
|
|
||||||
|
if callee.magic notin ctfeWhitelist: return
|
||||||
|
if callee.kind notin {skProc, skConverter} or callee.isGenericRoutine:
|
||||||
|
return
|
||||||
|
|
||||||
|
if n.typ != nil and not typeAllowed(n.typ, skConst): return
|
||||||
|
|
||||||
|
var call = newNodeIT(nkCall, n.info, n.typ)
|
||||||
|
call.add(n.sons[0])
|
||||||
|
for i in 1 .. < n.len:
|
||||||
|
let a = getConstExpr(c.module, n.sons[i])
|
||||||
|
if a == nil: return n
|
||||||
|
call.add(a)
|
||||||
|
#echo "NOW evaluating at compile time: ", call.renderTree
|
||||||
|
if sfCompileTime in callee.flags:
|
||||||
|
result = evalStaticExpr(c.module, call)
|
||||||
|
if result.isNil:
|
||||||
|
LocalError(n.info, errCannotInterpretNodeX, renderTree(call))
|
||||||
|
else:
|
||||||
|
result = evalConstExpr(c.module, call)
|
||||||
|
if result.isNil: result = n
|
||||||
|
#if result != n:
|
||||||
|
# echo "SUCCESS evaluated at compile time: ", call.renderTree
|
||||||
|
|
||||||
proc semDirectCallAnalyseEffects(c: PContext, n: PNode,
|
proc semDirectCallAnalyseEffects(c: PContext, n: PNode,
|
||||||
flags: TExprFlags): PNode =
|
flags: TExprFlags): PNode =
|
||||||
if efWantIterator in flags:
|
if efWantIterator in flags:
|
||||||
|
|
@ -490,7 +523,7 @@ proc semDirectCallAnalyseEffects(c: PContext, n: PNode,
|
||||||
var callee = result.sons[0].sym
|
var callee = result.sons[0].sym
|
||||||
if (callee.kind == skIterator) and (callee.id == c.p.owner.id):
|
if (callee.kind == skIterator) and (callee.id == c.p.owner.id):
|
||||||
GlobalError(n.info, errRecursiveDependencyX, callee.name.s)
|
GlobalError(n.info, errRecursiveDependencyX, callee.name.s)
|
||||||
if sfNoSideEffect notin callee.flags:
|
if sfNoSideEffect notin callee.flags:
|
||||||
if {sfImportc, sfSideEffect} * callee.flags != {}:
|
if {sfImportc, sfSideEffect} * callee.flags != {}:
|
||||||
incl(c.p.owner.flags, sfSideEffect)
|
incl(c.p.owner.flags, sfSideEffect)
|
||||||
|
|
||||||
|
|
@ -544,6 +577,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
analyseIfAddressTakenInCall(c, result)
|
analyseIfAddressTakenInCall(c, result)
|
||||||
if result.sons[0].kind == nkSym and result.sons[0].sym.magic != mNone:
|
if result.sons[0].kind == nkSym and result.sons[0].sym.magic != mNone:
|
||||||
result = magicsAfterOverloadResolution(c, result, flags)
|
result = magicsAfterOverloadResolution(c, result, flags)
|
||||||
|
result = evalAtCompileTime(c, result)
|
||||||
|
|
||||||
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
# this seems to be a hotspot in the compiler!
|
# this seems to be a hotspot in the compiler!
|
||||||
|
|
@ -556,6 +590,7 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
analyseIfAddressTakenInCall(c, result)
|
analyseIfAddressTakenInCall(c, result)
|
||||||
if result.sons[0].sym.magic != mNone:
|
if result.sons[0].sym.magic != mNone:
|
||||||
result = magicsAfterOverloadResolution(c, result, flags)
|
result = magicsAfterOverloadResolution(c, result, flags)
|
||||||
|
result = evalAtCompileTime(c, result)
|
||||||
|
|
||||||
proc buildStringify(c: PContext, arg: PNode): PNode =
|
proc buildStringify(c: PContext, arg: PNode): PNode =
|
||||||
if arg.typ != nil and skipTypes(arg.typ, abstractInst).kind == tyString:
|
if arg.typ != nil and skipTypes(arg.typ, abstractInst).kind == tyString:
|
||||||
|
|
|
||||||
|
|
@ -827,7 +827,8 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool =
|
||||||
if ContainsOrIncl(marker, typ.id): return
|
if ContainsOrIncl(marker, typ.id): return
|
||||||
var t = skipTypes(typ, abstractInst)
|
var t = skipTypes(typ, abstractInst)
|
||||||
case t.kind
|
case t.kind
|
||||||
of tyVar:
|
of tyVar:
|
||||||
|
if kind == skConst: return false
|
||||||
var t2 = skipTypes(t.sons[0], abstractInst)
|
var t2 = skipTypes(t.sons[0], abstractInst)
|
||||||
case t2.kind
|
case t2.kind
|
||||||
of tyVar:
|
of tyVar:
|
||||||
|
|
@ -866,6 +867,7 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool =
|
||||||
result = t.sons[1].kind == tyEmpty or
|
result = t.sons[1].kind == tyEmpty or
|
||||||
typeAllowedAux(marker, t.sons[1], skVar)
|
typeAllowedAux(marker, t.sons[1], skVar)
|
||||||
of tyPtr, tyRef:
|
of tyPtr, tyRef:
|
||||||
|
if kind == skConst: return false
|
||||||
result = typeAllowedAux(marker, t.sons[0], skVar)
|
result = typeAllowedAux(marker, t.sons[0], skVar)
|
||||||
of tyArrayConstr, tyTuple, tySet, tyConst, tyMutable, tyIter, tyProxy:
|
of tyArrayConstr, tyTuple, tySet, tyConst, tyMutable, tyIter, tyProxy:
|
||||||
for i in countup(0, sonsLen(t) - 1):
|
for i in countup(0, sonsLen(t) - 1):
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,8 @@ type
|
||||||
wInclude, wIs, wIsnot, wIterator, wLambda, wLet,
|
wInclude, wIs, wIsnot, wIterator, wLambda, wLet,
|
||||||
wMacro, wMethod, wMod, wNil,
|
wMacro, wMethod, wMod, wNil,
|
||||||
wNot, wNotin, wObject, wOf, wOr, wOut, wProc, wPtr, wRaise, wRef, wReturn,
|
wNot, wNotin, wObject, wOf, wOr, wOut, wProc, wPtr, wRaise, wRef, wReturn,
|
||||||
wShl, wShr, wTemplate, wTry, wTuple, wType, wVar, wWhen, wWhile, wWith,
|
wShl, wShr, wStatic, wTemplate, wTry, wTuple, wType, wVar,
|
||||||
wWithout, wXor, wYield,
|
wWhen, wWhile, wWith, wWithout, wXor, wYield,
|
||||||
|
|
||||||
wColon, wColonColon, wEquals, wDot, wDotDot,
|
wColon, wColonColon, wEquals, wDot, wDotDot,
|
||||||
wStar, wMinus,
|
wStar, wMinus,
|
||||||
|
|
@ -57,7 +57,8 @@ type
|
||||||
wFieldChecks,
|
wFieldChecks,
|
||||||
wWatchPoint, wSubsChar,
|
wWatchPoint, wSubsChar,
|
||||||
wAcyclic, wShallow, wUnroll, wLinearScanEnd,
|
wAcyclic, wShallow, wUnroll, wLinearScanEnd,
|
||||||
wWrite, wPutEnv, wPrependEnv, wAppendEnv, wThreadVar, wEmit, wNoStackFrame
|
wWrite, wPutEnv, wPrependEnv, wAppendEnv, wThreadVar, wEmit, wNoStackFrame,
|
||||||
|
wImplicitStatic
|
||||||
|
|
||||||
TSpecialWords* = set[TSpecialWord]
|
TSpecialWords* = set[TSpecialWord]
|
||||||
|
|
||||||
|
|
@ -75,8 +76,9 @@ const
|
||||||
"import", "in", "include", "is", "isnot", "iterator",
|
"import", "in", "include", "is", "isnot", "iterator",
|
||||||
"lambda", "let",
|
"lambda", "let",
|
||||||
"macro", "method", "mod", "nil", "not", "notin", "object", "of", "or",
|
"macro", "method", "mod", "nil", "not", "notin", "object", "of", "or",
|
||||||
"out", "proc", "ptr", "raise", "ref", "return", "shl", "shr", "template",
|
"out", "proc", "ptr", "raise", "ref", "return", "shl", "shr", "static",
|
||||||
"try", "tuple", "type", "var", "when", "while", "with", "without", "xor",
|
"template", "try", "tuple", "type", "var",
|
||||||
|
"when", "while", "with", "without", "xor",
|
||||||
"yield",
|
"yield",
|
||||||
|
|
||||||
":", "::", "=", ".", "..",
|
":", "::", "=", ".", "..",
|
||||||
|
|
@ -105,7 +107,7 @@ const
|
||||||
"watchpoint",
|
"watchpoint",
|
||||||
"subschar", "acyclic", "shallow", "unroll", "linearscanend",
|
"subschar", "acyclic", "shallow", "unroll", "linearscanend",
|
||||||
"write", "putenv", "prependenv", "appendenv", "threadvar", "emit",
|
"write", "putenv", "prependenv", "appendenv", "threadvar", "emit",
|
||||||
"nostackframe"]
|
"nostackframe", "implicitstatic"]
|
||||||
|
|
||||||
proc findStr*(a: openarray[string], s: string): int =
|
proc findStr*(a: openarray[string], s: string): int =
|
||||||
for i in countup(low(a), high(a)):
|
for i in countup(low(a), high(a)):
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ Advanced options:
|
||||||
--tlsEmulation:on|off turn thread local storage emulation on|off
|
--tlsEmulation:on|off turn thread local storage emulation on|off
|
||||||
--taintMode:on|off turn taint mode on|off
|
--taintMode:on|off turn taint mode on|off
|
||||||
--symbolFiles:on|off turn symbol files on|off (experimental)
|
--symbolFiles:on|off turn symbol files on|off (experimental)
|
||||||
|
--implicitStatic:on|off turn implicit compile time evaluation on|off
|
||||||
--skipCfg do not read the general configuration file
|
--skipCfg do not read the general configuration file
|
||||||
--skipUserCfg do not read the user's configuration file
|
--skipUserCfg do not read the user's configuration file
|
||||||
--skipParentCfg do not read the parent dirs' configuration files
|
--skipParentCfg do not read the parent dirs' configuration files
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ indexExpr ::= expr
|
||||||
|
|
||||||
castExpr ::= 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')'
|
castExpr ::= 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')'
|
||||||
addrExpr ::= 'addr' '(' optInd expr optPar ')'
|
addrExpr ::= 'addr' '(' optInd expr optPar ')'
|
||||||
|
staticExpr ::= 'static' '(' optInd expr optPar ')'
|
||||||
symbol ::= '`' (KEYWORD | IDENT | operator | '(' ')' | '[' ']' | '{' '}'
|
symbol ::= '`' (KEYWORD | IDENT | operator | '(' ')' | '[' ']' | '{' '}'
|
||||||
| '=' | literal)+ '`'
|
| '=' | literal)+ '`'
|
||||||
| IDENT
|
| IDENT
|
||||||
|
|
@ -37,7 +38,7 @@ primarySuffix ::= '.' optInd symbol [generalizedLit]
|
||||||
| '{' optInd [indexExpr (comma indexExpr)* [comma]] optPar '}'
|
| '{' optInd [indexExpr (comma indexExpr)* [comma]] optPar '}'
|
||||||
|
|
||||||
primary ::= primaryPrefix* (symbol [generalizedLit] |
|
primary ::= primaryPrefix* (symbol [generalizedLit] |
|
||||||
constructor | castExpr | addrExpr)
|
constructor | castExpr | addrExpr | staticExpr)
|
||||||
primarySuffix*
|
primarySuffix*
|
||||||
|
|
||||||
generalizedLit ::= GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT
|
generalizedLit ::= GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT
|
||||||
|
|
@ -97,7 +98,7 @@ simpleStmt ::= returnStmt
|
||||||
| includeStmt
|
| includeStmt
|
||||||
| exprStmt
|
| exprStmt
|
||||||
complexStmt ::= ifStmt | whileStmt | caseStmt | tryStmt | forStmt
|
complexStmt ::= ifStmt | whileStmt | caseStmt | tryStmt | forStmt
|
||||||
| blockStmt | asmStmt
|
| blockStmt | staticStmt | asmStmt
|
||||||
| procDecl | iteratorDecl | macroDecl | templateDecl | methodDecl
|
| procDecl | iteratorDecl | macroDecl | templateDecl | methodDecl
|
||||||
| constSection | letSection | varSection
|
| constSection | letSection | varSection
|
||||||
| typeSection | whenStmt | bindStmt
|
| typeSection | whenStmt | bindStmt
|
||||||
|
|
@ -131,6 +132,7 @@ tryStmt ::= 'try' ':' stmt
|
||||||
['finally' ':' stmt]
|
['finally' ':' stmt]
|
||||||
asmStmt ::= 'asm' [pragma] (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
|
asmStmt ::= 'asm' [pragma] (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
|
||||||
blockStmt ::= 'block' [symbol] ':' stmt
|
blockStmt ::= 'block' [symbol] ':' stmt
|
||||||
|
staticStmt ::= 'static' ':' stmt
|
||||||
filename ::= symbol | STR_LIT | RSTR_LIT | TRIPLESTR_LIT
|
filename ::= symbol | STR_LIT | RSTR_LIT | TRIPLESTR_LIT
|
||||||
importStmt ::= 'import' filename (comma filename)*
|
importStmt ::= 'import' filename (comma filename)*
|
||||||
includeStmt ::= 'include' filename (comma filename)*
|
includeStmt ::= 'include' filename (comma filename)*
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ nil not notin
|
||||||
object of or out
|
object of or out
|
||||||
proc ptr
|
proc ptr
|
||||||
raise ref return
|
raise ref return
|
||||||
shl shr
|
shl shr static
|
||||||
template try tuple type
|
template try tuple type
|
||||||
var
|
var
|
||||||
when while with without
|
when while with without
|
||||||
|
|
|
||||||
|
|
@ -1640,6 +1640,30 @@ Constants cannot be of type ``ptr``, ``ref``, ``var`` or ``object``, nor can
|
||||||
they contain such a type.
|
they contain such a type.
|
||||||
|
|
||||||
|
|
||||||
|
Static statement/expression
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Syntax::
|
||||||
|
staticExpr ::= 'static' '(' optInd expr optPar ')'
|
||||||
|
staticStmt ::= 'static' ':' stmt
|
||||||
|
|
||||||
|
A `static`:idx: statement/expression can be used to enforce compile
|
||||||
|
time evaluation explicitely. Enforced compile time evaluation can even evaluate
|
||||||
|
code that has side effects:
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
|
||||||
|
static:
|
||||||
|
echo "echo at compile time"
|
||||||
|
|
||||||
|
It's a static error if the compiler cannot perform the evaluation at compile time.
|
||||||
|
|
||||||
|
The current implementation poses some restrictions for compile time
|
||||||
|
evaluation: Code which contains ``cast`` or makes use of the foreign function
|
||||||
|
interface cannot be evaluated at compile time. Later versions of Nimrod will
|
||||||
|
support the FFI at compile time.
|
||||||
|
|
||||||
|
|
||||||
If statement
|
If statement
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,8 @@ type
|
||||||
## represents a Nimrod *symbol* in the compiler; a *symbol* is a looked-up
|
## represents a Nimrod *symbol* in the compiler; a *symbol* is a looked-up
|
||||||
## *ident*.
|
## *ident*.
|
||||||
|
|
||||||
PNimrodNode* = expr
|
TNimrodNode {.final.} = object
|
||||||
|
PNimrodNode* = ref TNimrodNode
|
||||||
## represents a Nimrod AST node. Macros operate on this type.
|
## represents a Nimrod AST node. Macros operate on this type.
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
|
||||||
|
|
@ -2213,12 +2213,12 @@ proc shallow*(s: var string) {.noSideEffect, inline.} =
|
||||||
var s = cast[PGenericSeq](s)
|
var s = cast[PGenericSeq](s)
|
||||||
s.reserved = s.reserved or seqShallowFlag
|
s.reserved = s.reserved or seqShallowFlag
|
||||||
|
|
||||||
template static*(e: expr): expr =
|
#template static*(e: expr): expr =
|
||||||
## evaluates a given expression `e` at compile-time
|
# ## evaluates a given expression `e` at compile-time
|
||||||
## even if it has side effects
|
# ## even if it has side effects
|
||||||
block:
|
# block:
|
||||||
const res = e
|
# const res = e
|
||||||
res
|
# res
|
||||||
|
|
||||||
template eval*(blk: stmt): stmt =
|
template eval*(blk: stmt): stmt =
|
||||||
## executes a block of code at compile time just as if it was a macro
|
## executes a block of code at compile time just as if it was a macro
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,14 @@ import macros
|
||||||
|
|
||||||
# Test compile-time state in same module
|
# Test compile-time state in same module
|
||||||
|
|
||||||
var gid = 3
|
var gid {.compileTime.} = 3
|
||||||
|
|
||||||
macro genId(invokation: expr): expr =
|
macro genId(invokation: expr): expr =
|
||||||
result = newIntLitNode(gid)
|
result = newIntLitNode(gid)
|
||||||
inc gid
|
inc gid
|
||||||
|
|
||||||
proc Id1(): int = return genId()
|
proc Id1(): int {.compileTime.} = return genId()
|
||||||
proc Id2(): int = return genId()
|
proc Id2(): int {.compileTime.} = return genId()
|
||||||
|
|
||||||
echo Id1(), " ", Id2()
|
echo Id1(), " ", Id2()
|
||||||
|
|
||||||
|
|
|
||||||
8
todo.txt
8
todo.txt
|
|
@ -1,7 +1,9 @@
|
||||||
version 0.9.0
|
version 0.9.0
|
||||||
=============
|
=============
|
||||||
|
|
||||||
- implement 'static' vs. 'const'
|
- implement 'static' vs. 'const';
|
||||||
|
clear separation between tyExpr and PNimrodNode
|
||||||
|
|
||||||
- ``=`` should be overloadable; requires specialization for ``=``
|
- ``=`` should be overloadable; requires specialization for ``=``
|
||||||
- fix remaining generics bugs
|
- fix remaining generics bugs
|
||||||
- fix remaining closure bugs:
|
- fix remaining closure bugs:
|
||||||
|
|
@ -24,7 +26,6 @@ version 0.9.0
|
||||||
loop hoisting
|
loop hoisting
|
||||||
- we need to support iteration of 2 different data structures in parallel
|
- we need to support iteration of 2 different data structures in parallel
|
||||||
- make exceptions compatible with C++ exceptions
|
- make exceptions compatible with C++ exceptions
|
||||||
- 'const' objects including case objects
|
|
||||||
- change how comments are part of the AST
|
- change how comments are part of the AST
|
||||||
- optional indentation for 'case' statement; hm, keep in mind other syntax
|
- optional indentation for 'case' statement; hm, keep in mind other syntax
|
||||||
changes that people want; may turn out to be a bad idea
|
changes that people want; may turn out to be a bad idea
|
||||||
|
|
@ -77,6 +78,7 @@ version 0.9.XX
|
||||||
- make pegs support a compile-time option and make c2nim use regexes instead
|
- make pegs support a compile-time option and make c2nim use regexes instead
|
||||||
per default?
|
per default?
|
||||||
- fix implicit generic routines
|
- fix implicit generic routines
|
||||||
|
- 'const' objects including case objects
|
||||||
- improve docgen to use the semantic pass
|
- improve docgen to use the semantic pass
|
||||||
- 'export' feature (requires improved docgen)
|
- 'export' feature (requires improved docgen)
|
||||||
- think about ``{:}.toTable[int, string]()``
|
- think about ``{:}.toTable[int, string]()``
|
||||||
|
|
@ -102,7 +104,7 @@ version 0.9.XX
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
- wrappers for poppler; libharu
|
- wrappers for mongodb; poppler; libharu
|
||||||
- suffix trees
|
- suffix trees
|
||||||
- locale support; i18n module
|
- locale support; i18n module
|
||||||
- bignums
|
- bignums
|
||||||
|
|
|
||||||
15
web/news.txt
15
web/news.txt
|
|
@ -19,8 +19,6 @@ Library Additions
|
||||||
|
|
||||||
- Added ``system.shallow`` that can be used to speed up string and sequence
|
- Added ``system.shallow`` that can be used to speed up string and sequence
|
||||||
assignments.
|
assignments.
|
||||||
- Added ``system.static`` that can force compile-time evaluation of certain
|
|
||||||
expressions.
|
|
||||||
- Added ``system.eval`` that can execute an anonymous block of code at
|
- Added ``system.eval`` that can execute an anonymous block of code at
|
||||||
compile time as if was a macro.
|
compile time as if was a macro.
|
||||||
- Added ``macros.emit`` that can emit an arbitrary computed string as nimrod
|
- Added ``macros.emit`` that can emit an arbitrary computed string as nimrod
|
||||||
|
|
@ -34,11 +32,22 @@ Changes affecting backwards compatibility
|
||||||
The ``system``, ``os``, ``osproc`` and ``memfiles`` modules use the wide
|
The ``system``, ``os``, ``osproc`` and ``memfiles`` modules use the wide
|
||||||
string versions of the WinAPI. Use the ``-d:useWinAnsi`` switch to revert
|
string versions of the WinAPI. Use the ``-d:useWinAnsi`` switch to revert
|
||||||
back to the old behaviour which uses the Ansi string versions.
|
back to the old behaviour which uses the Ansi string versions.
|
||||||
|
- ``static`` is now a keyword.
|
||||||
|
|
||||||
|
|
||||||
Compiler Additions
|
Compiler Additions
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
- Win64 is now an officially supported target.
|
- Win64 is now an officially supported target.
|
||||||
|
- The compiler can detect and evaluate calls that can be evaluated at compile
|
||||||
|
time for optimization purposes with the ``--implicitStatic`` command line
|
||||||
|
option or pragma.
|
||||||
|
|
||||||
|
|
||||||
|
Language Additions
|
||||||
|
------------------
|
||||||
|
|
||||||
|
- Added explicit ``static`` sections for enforced compile time evaluation.
|
||||||
|
|
||||||
|
|
||||||
2012-02-09 Version 0.8.14 released
|
2012-02-09 Version 0.8.14 released
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue