first steps for cleaner static/const distinction

This commit is contained in:
Araq 2012-03-13 02:01:56 +01:00
commit c25ffbf262
19 changed files with 193 additions and 66 deletions

View file

@ -396,6 +396,36 @@ type
mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError,
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
PNode* = ref TNode
TNodeSeq* = seq[PNode]

View file

@ -22,6 +22,7 @@ proc genVarTuple(p: BProc, n: PNode) =
var t = tup.t
for i in countup(0, L-3):
var v = n.sons[i].sym
if sfCompileTime in v.flags: continue
if sfGlobal in v.flags and v.kind != skForVar:
assignGlobalVar(p, v)
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) =
var v = a.sons[0].sym
if sfCompileTime in v.flags: return
var immediateAsgn = a.sons[2].kind != nkEmpty
if sfGlobal in v.flags and v.kind != skForVar:
assignGlobalVar(p, v)

View file

@ -183,6 +183,7 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
of "threads": result = contains(gGlobalOptions, optThreads)
of "taintmode": result = contains(gGlobalOptions, optTaintMode)
of "tlsemulation": result = contains(gGlobalOptions, optTlsEmulation)
of "implicitstatic": result = contains(gOptions, optImplicitStatic)
else: InvalidCmdLineOption(passCmd1, switch, info)
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 "tlsemulation": ProcessOnOffSwitchG({optTlsEmulation}, arg, pass, info)
of "taintmode": ProcessOnOffSwitchG({optTaintMode}, arg, pass, info)
of "implicitstatic":
ProcessOnOffSwitch({optImplicitStatic}, arg, pass, info)
of "opt":
expectArg(switch, arg, pass, info)
case arg.normalize

View file

@ -263,7 +263,8 @@ proc evalVar(c: PEvalContext, n: PNode): PNode =
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
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
if a.sons[2].kind != nkEmpty:
result = evalAux(c, a.sons[2], {})
@ -311,17 +312,18 @@ proc evalCall(c: PEvalContext, n: PNode): PNode =
if isSpecial(result): return
prc = result
# bind the actual params to the local parameter of a new binding
if prc.kind == nkSym:
d.prc = prc.sym
if prc.sym.kind notin {skProc, skConverter}:
InternalError(n.info, "evalCall")
if prc.kind != nkSym: InternalError(n.info, "evalCall " & n.renderTree)
d.prc = prc.sym
if prc.sym.kind notin {skProc, skConverter, skMacro}:
InternalError(n.info, "evalCall")
for i in countup(1, sonsLen(n) - 1):
result = evalAux(c, n.sons[i], {})
if isSpecial(result): return
d.params[i] = result
if n.typ != nil: d.params[0] = getNullValue(n.typ, n.info)
pushStackFrame(c, d)
result = evalAux(c, prc, {})
result = evalAux(c, prc.sym.getBody, {})
if result.kind == nkExceptBranch: return
if n.typ != nil: result = d.params[0]
popStackFrame(c)
@ -489,7 +491,8 @@ proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
var s = n.sym
case s.kind
of skProc, skConverter, skMacro:
result = s.getBody
result = n
#result = s.getBody
of skVar, skLet, skForVar, skTemp, skResult:
if sfGlobal notin 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 skEnumField: result = newIntNodeT(s.position, n)
else: result = nil
if result == nil or sfImportc in s.flags:
if result == nil or {sfImportc, sfForward} * s.flags != {}:
result = raiseCannotEval(c, n.info)
proc evalIncDec(c: PEvalContext, n: PNode, sign: biggestInt): PNode =
@ -532,10 +535,13 @@ proc evalEcho(c: PEvalContext, n: PNode): PNode =
result = emptyNode
proc evalExit(c: PEvalContext, n: PNode): PNode =
result = evalAux(c, n.sons[1], {})
if isSpecial(result): return
Message(n.info, hintQuitCalled)
quit(int(getOrdValue(result)))
if c.mode in {emRepl, emStatic}:
result = evalAux(c, n.sons[1], {})
if isSpecial(result): return
Message(n.info, hintQuitCalled)
quit(int(getOrdValue(result)))
else:
result = raiseCannotEval(c, n.info)
proc evalOr(c: PEvalContext, n: PNode): PNode =
result = evalAux(c, n.sons[1], {})
@ -636,19 +642,22 @@ proc evalConvCStrToStr(c: PEvalContext, n: PNode): PNode =
result.typ = n.typ
proc evalRaise(c: PEvalContext, n: PNode): PNode =
if n.sons[0].kind != nkEmpty:
result = evalAux(c, n.sons[0], {})
if isSpecial(result): return
var a = result
result = newNodeIT(nkExceptBranch, n.info, a.typ)
addSon(result, a)
c.lastException = result
elif c.lastException != nil:
result = c.lastException
else:
stackTrace(c, n, errExceptionAlreadyHandled)
result = newNodeIT(nkExceptBranch, n.info, nil)
addSon(result, ast.emptyNode)
if c.mode in {emRepl, emStatic}:
if n.sons[0].kind != nkEmpty:
result = evalAux(c, n.sons[0], {})
if isSpecial(result): return
var a = result
result = newNodeIT(nkExceptBranch, n.info, a.typ)
addSon(result, a)
c.lastException = result
elif c.lastException != nil:
result = c.lastException
else:
stackTrace(c, n, errExceptionAlreadyHandled)
result = newNodeIT(nkExceptBranch, n.info, nil)
addSon(result, ast.emptyNode)
else:
result = raiseCannotEval(c, n.info)
proc evalReturn(c: PEvalContext, n: PNode): PNode =
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)
inc(gNestedEvals)
proc eval*(c: PEvalContext, n: PNode): PNode =
## eval never returns nil! This simplifies the code a lot and
## makes it faster too.
proc tryEval(c: PEvalContext, n: PNode): PNode =
var n = transform(c.module, n)
gWhileCounter = evalMaxIterations
gNestedEvals = evalMaxRecDepth
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 sonsLen(result) >= 1:
stackTrace(c, n, errUnhandledExceptionX, typeToString(result.typ))
else:
stackTrace(c, n, errCannotInterpretNodeX, renderTree(n))
proc evalConstExpr*(module: PSym, e: PNode): PNode =
var p = newEvalContext(module, "", emConst)
proc evalConstExprAux(module: PSym, e: PNode, mode: TEvalMode): PNode =
var p = newEvalContext(module, "", mode)
var s = newStackFrame()
s.call = e
pushStackFrame(p, s)
result = eval(p, e)
result = tryEval(p, e)
if result != nil and result.kind == nkExceptBranch: result = nil
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 =
# XXX GlobalError() is ugly here, but I don't know a better solution for now
inc(evalTemplateCounter)

View file

@ -25,7 +25,9 @@ type # please make sure we have under 32 options
optEndb, # embedded debugger
optByRef, # use pass by ref for objects
# (for interfacing with C)
optProfiler # profiler turned on
optProfiler, # profiler turned on
optImplicitStatic # optimization: implicit at compile time
# evaluation
TOptions* = set[TOption]
TGlobalOption* = enum # **keep binary compatible**
gloptNone, optForceFullMake, optBoehmGC, optRefcGC, optDeadCodeElim,

View file

@ -277,6 +277,7 @@ proc processOption(c: PContext, n: PNode) =
excl(gOptions, optOptimizeSpeed)
excl(gOptions, optOptimizeSize)
else: LocalError(n.info, errNoneSpeedOrSizeExpected)
of wImplicitStatic: OnOff(c, n, {optImplicitStatic})
else: LocalError(n.info, errOptionExpected)
proc processPush(c: PContext, n: PNode, start: int) =

View file

@ -85,13 +85,6 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
result = evalConstExpr(c.module, e)
if result == nil or result.kind == nkEmpty:
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 =
result = semConstExpr(c, n)

View file

@ -475,6 +475,39 @@ proc expectStringArg(c: PContext, n: PNode, i: int): PNode =
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,
flags: TExprFlags): PNode =
if efWantIterator in flags:
@ -490,7 +523,7 @@ proc semDirectCallAnalyseEffects(c: PContext, n: PNode,
var callee = result.sons[0].sym
if (callee.kind == skIterator) and (callee.id == c.p.owner.id):
GlobalError(n.info, errRecursiveDependencyX, callee.name.s)
if sfNoSideEffect notin callee.flags:
if sfNoSideEffect notin callee.flags:
if {sfImportc, sfSideEffect} * callee.flags != {}:
incl(c.p.owner.flags, sfSideEffect)
@ -544,6 +577,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
analyseIfAddressTakenInCall(c, result)
if result.sons[0].kind == nkSym and result.sons[0].sym.magic != mNone:
result = magicsAfterOverloadResolution(c, result, flags)
result = evalAtCompileTime(c, result)
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
# 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)
if result.sons[0].sym.magic != mNone:
result = magicsAfterOverloadResolution(c, result, flags)
result = evalAtCompileTime(c, result)
proc buildStringify(c: PContext, arg: PNode): PNode =
if arg.typ != nil and skipTypes(arg.typ, abstractInst).kind == tyString:

View file

@ -827,7 +827,8 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool =
if ContainsOrIncl(marker, typ.id): return
var t = skipTypes(typ, abstractInst)
case t.kind
of tyVar:
of tyVar:
if kind == skConst: return false
var t2 = skipTypes(t.sons[0], abstractInst)
case t2.kind
of tyVar:
@ -866,6 +867,7 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool =
result = t.sons[1].kind == tyEmpty or
typeAllowedAux(marker, t.sons[1], skVar)
of tyPtr, tyRef:
if kind == skConst: return false
result = typeAllowedAux(marker, t.sons[0], skVar)
of tyArrayConstr, tyTuple, tySet, tyConst, tyMutable, tyIter, tyProxy:
for i in countup(0, sonsLen(t) - 1):

View file

@ -30,8 +30,8 @@ type
wInclude, wIs, wIsnot, wIterator, wLambda, wLet,
wMacro, wMethod, wMod, wNil,
wNot, wNotin, wObject, wOf, wOr, wOut, wProc, wPtr, wRaise, wRef, wReturn,
wShl, wShr, wTemplate, wTry, wTuple, wType, wVar, wWhen, wWhile, wWith,
wWithout, wXor, wYield,
wShl, wShr, wStatic, wTemplate, wTry, wTuple, wType, wVar,
wWhen, wWhile, wWith, wWithout, wXor, wYield,
wColon, wColonColon, wEquals, wDot, wDotDot,
wStar, wMinus,
@ -57,7 +57,8 @@ type
wFieldChecks,
wWatchPoint, wSubsChar,
wAcyclic, wShallow, wUnroll, wLinearScanEnd,
wWrite, wPutEnv, wPrependEnv, wAppendEnv, wThreadVar, wEmit, wNoStackFrame
wWrite, wPutEnv, wPrependEnv, wAppendEnv, wThreadVar, wEmit, wNoStackFrame,
wImplicitStatic
TSpecialWords* = set[TSpecialWord]
@ -75,8 +76,9 @@ const
"import", "in", "include", "is", "isnot", "iterator",
"lambda", "let",
"macro", "method", "mod", "nil", "not", "notin", "object", "of", "or",
"out", "proc", "ptr", "raise", "ref", "return", "shl", "shr", "template",
"try", "tuple", "type", "var", "when", "while", "with", "without", "xor",
"out", "proc", "ptr", "raise", "ref", "return", "shl", "shr", "static",
"template", "try", "tuple", "type", "var",
"when", "while", "with", "without", "xor",
"yield",
":", "::", "=", ".", "..",
@ -105,7 +107,7 @@ const
"watchpoint",
"subschar", "acyclic", "shallow", "unroll", "linearscanend",
"write", "putenv", "prependenv", "appendenv", "threadvar", "emit",
"nostackframe"]
"nostackframe", "implicitstatic"]
proc findStr*(a: openarray[string], s: string): int =
for i in countup(low(a), high(a)):