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, 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]

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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,

View file

@ -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) =

View file

@ -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)

View file

@ -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:

View file

@ -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):

View file

@ -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)):

View file

@ -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

View file

@ -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)*

View file

@ -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

View file

@ -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
~~~~~~~~~~~~ ~~~~~~~~~~~~

View file

@ -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

View file

@ -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

View file

@ -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()

View file

@ -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

View file

@ -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