first steps for cleaner static/const distinction

This commit is contained in:
Araq
2012-03-13 02:01:56 +01:00
parent 1d8ff40f56
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)):

View File

@@ -51,6 +51,7 @@ Advanced options:
--tlsEmulation:on|off turn thread local storage emulation on|off
--taintMode:on|off turn taint mode on|off
--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
--skipUserCfg do not read the user's configuration file
--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 ')'
addrExpr ::= 'addr' '(' optInd expr optPar ')'
staticExpr ::= 'static' '(' optInd expr optPar ')'
symbol ::= '`' (KEYWORD | IDENT | operator | '(' ')' | '[' ']' | '{' '}'
| '=' | literal)+ '`'
| IDENT
@@ -37,7 +38,7 @@ primarySuffix ::= '.' optInd symbol [generalizedLit]
| '{' optInd [indexExpr (comma indexExpr)* [comma]] optPar '}'
primary ::= primaryPrefix* (symbol [generalizedLit] |
constructor | castExpr | addrExpr)
constructor | castExpr | addrExpr | staticExpr)
primarySuffix*
generalizedLit ::= GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT
@@ -97,7 +98,7 @@ simpleStmt ::= returnStmt
| includeStmt
| exprStmt
complexStmt ::= ifStmt | whileStmt | caseStmt | tryStmt | forStmt
| blockStmt | asmStmt
| blockStmt | staticStmt | asmStmt
| procDecl | iteratorDecl | macroDecl | templateDecl | methodDecl
| constSection | letSection | varSection
| typeSection | whenStmt | bindStmt
@@ -131,6 +132,7 @@ tryStmt ::= 'try' ':' stmt
['finally' ':' stmt]
asmStmt ::= 'asm' [pragma] (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
blockStmt ::= 'block' [symbol] ':' stmt
staticStmt ::= 'static' ':' stmt
filename ::= symbol | STR_LIT | RSTR_LIT | TRIPLESTR_LIT
importStmt ::= 'import' filename (comma filename)*
includeStmt ::= 'include' filename (comma filename)*

View File

@@ -12,7 +12,7 @@ nil not notin
object of or out
proc ptr
raise ref return
shl shr
shl shr static
template try tuple type
var
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.
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
~~~~~~~~~~~~

View File

@@ -87,7 +87,8 @@ type
## represents a Nimrod *symbol* in the compiler; a *symbol* is a looked-up
## *ident*.
PNimrodNode* = expr
TNimrodNode {.final.} = object
PNimrodNode* = ref TNimrodNode
## represents a Nimrod AST node. Macros operate on this type.
const

View File

@@ -2213,12 +2213,12 @@ proc shallow*(s: var string) {.noSideEffect, inline.} =
var s = cast[PGenericSeq](s)
s.reserved = s.reserved or seqShallowFlag
template static*(e: expr): expr =
## evaluates a given expression `e` at compile-time
## even if it has side effects
block:
const res = e
res
#template static*(e: expr): expr =
# ## evaluates a given expression `e` at compile-time
# ## even if it has side effects
# block:
# const res = e
# res
template eval*(blk: stmt): stmt =
## 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
var gid = 3
var gid {.compileTime.} = 3
macro genId(invokation: expr): expr =
result = newIntLitNode(gid)
inc gid
proc Id1(): int = return genId()
proc Id2(): int = return genId()
proc Id1(): int {.compileTime.} = return genId()
proc Id2(): int {.compileTime.} = return genId()
echo Id1(), " ", Id2()

View File

@@ -1,7 +1,9 @@
version 0.9.0
=============
- implement 'static' vs. 'const'
- implement 'static' vs. 'const';
clear separation between tyExpr and PNimrodNode
- ``=`` should be overloadable; requires specialization for ``=``
- fix remaining generics bugs
- fix remaining closure bugs:
@@ -24,7 +26,6 @@ version 0.9.0
loop hoisting
- we need to support iteration of 2 different data structures in parallel
- make exceptions compatible with C++ exceptions
- 'const' objects including case objects
- change how comments are part of the AST
- optional indentation for 'case' statement; hm, keep in mind other syntax
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
per default?
- fix implicit generic routines
- 'const' objects including case objects
- improve docgen to use the semantic pass
- 'export' feature (requires improved docgen)
- think about ``{:}.toTable[int, string]()``
@@ -102,7 +104,7 @@ version 0.9.XX
Library
-------
- wrappers for poppler; libharu
- wrappers for mongodb; poppler; libharu
- suffix trees
- locale support; i18n module
- bignums

View File

@@ -19,8 +19,6 @@ Library Additions
- Added ``system.shallow`` that can be used to speed up string and sequence
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
compile time as if was a macro.
- 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
string versions of the WinAPI. Use the ``-d:useWinAnsi`` switch to revert
back to the old behaviour which uses the Ansi string versions.
- ``static`` is now a keyword.
Compiler Additions
------------------
- 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