diff options
author | Dominik Picheta <dominikpicheta@gmail.com> | 2015-10-27 23:36:00 +0100 |
---|---|---|
committer | Dominik Picheta <dominikpicheta@gmail.com> | 2015-10-27 23:36:00 +0100 |
commit | 3892969af4a3c6f509b553f07b2a7f37aa8de967 (patch) | |
tree | f4ac158a72b3f520848a479ee4faa8dd68792738 /compiler | |
parent | c7eaa8ae034fc22fcb91770f94e08f7c1ebb9963 (diff) | |
parent | d9415fd5cebdc44385cdd092f2c49060c09e2631 (diff) | |
download | Nim-3892969af4a3c6f509b553f07b2a7f37aa8de967.tar.gz |
Merge branch 'devel'
Diffstat (limited to 'compiler')
107 files changed, 5047 insertions, 3199 deletions
diff --git a/compiler/aliases.nim b/compiler/aliases.nim index 3f3d45ff7..4b592ee60 100644 --- a/compiler/aliases.nim +++ b/compiler/aliases.nim @@ -11,7 +11,7 @@ import ast, astalgo, types, trees, intsets, msgs - + type TAnalysisResult* = enum arNo, arMaybe, arYes @@ -21,42 +21,42 @@ proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult proc isPartOfAux(n: PNode, b: PType, marker: var IntSet): TAnalysisResult = result = arNo case n.kind - of nkRecList: - for i in countup(0, sonsLen(n) - 1): + of nkRecList: + for i in countup(0, sonsLen(n) - 1): result = isPartOfAux(n.sons[i], b, marker) if result == arYes: return of nkRecCase: assert(n.sons[0].kind == nkSym) result = isPartOfAux(n.sons[0], b, marker) if result == arYes: return - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): case n.sons[i].kind - of nkOfBranch, nkElse: + of nkOfBranch, nkElse: result = isPartOfAux(lastSon(n.sons[i]), b, marker) if result == arYes: return else: internalError("isPartOfAux(record case branch)") of nkSym: result = isPartOfAux(n.sym.typ, b, marker) else: internalError(n.info, "isPartOfAux()") - -proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult = + +proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult = result = arNo - if a == nil or b == nil: return - if containsOrIncl(marker, a.id): return + if a == nil or b == nil: return + if containsOrIncl(marker, a.id): return if compareTypes(a, b, dcEqIgnoreDistinct): return arYes case a.kind - of tyObject: + of tyObject: result = isPartOfAux(a.sons[0], b, marker) if result == arNo: result = isPartOfAux(a.n, b, marker) of tyGenericInst, tyDistinct: result = isPartOfAux(lastSon(a), b, marker) - of tyArray, tyArrayConstr, tySet, tyTuple: - for i in countup(0, sonsLen(a) - 1): + of tyArray, tyArrayConstr, tySet, tyTuple: + for i in countup(0, sonsLen(a) - 1): result = isPartOfAux(a.sons[i], b, marker) - if result == arYes: return + if result == arYes: return else: discard -proc isPartOf(a, b: PType): TAnalysisResult = +proc isPartOf(a, b: PType): TAnalysisResult = ## checks iff 'a' can be part of 'b'. Iterates over VALUE types! var marker = initIntSet() # watch out: parameters reversed because I'm too lazy to change the code... @@ -70,14 +70,14 @@ proc isPartOf*(a, b: PNode): TAnalysisResult = ## type. Since however type analysis is more expensive, we perform it only ## if necessary. ## - ## cases: + ## cases: ## ## YES-cases: ## x <| x # for general trees ## x[] <| x ## x[i] <| x ## x.f <| x - ## + ## ## NO-cases: ## x !<| y # depending on type and symbol kind ## x[constA] !<| x[constB] @@ -88,16 +88,16 @@ proc isPartOf*(a, b: PNode): TAnalysisResult = ## ## x[] ?<| y[] iff compatible type ## - ## + ## ## x[] ?<| y depending on type - ## + ## if a.kind == b.kind: case a.kind of nkSym: const varKinds = {skVar, skTemp, skProc} # same symbol: aliasing: if a.sym.id == b.sym.id: result = arYes - elif a.sym.kind in varKinds or b.sym.kind in varKinds: + elif a.sym.kind in varKinds or b.sym.kind in varKinds: # actually, a param could alias a var but we know that cannot happen # here. XXX make this more generic result = arNo @@ -110,11 +110,11 @@ proc isPartOf*(a, b: PNode): TAnalysisResult = if len(a) >= 2 and len(b) >= 2: # array accesses: if result == arYes and isDeepConstExpr(a[1]) and isDeepConstExpr(b[1]): - # we know it's the same array and we have 2 constant indexes; - # if they are + # we know it's the same array and we have 2 constant indexes; + # if they are var x = if a[1].kind == nkHiddenStdConv: a[1][1] else: a[1] var y = if b[1].kind == nkHiddenStdConv: b[1][1] else: b[1] - + if sameValue(x, y): result = arYes else: result = arNo # else: maybe and no are accurate @@ -122,7 +122,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult = # pointer derefs: if result != arYes: if isPartOf(a.typ, b.typ) != arNo: result = arMaybe - + of nkDotExpr: result = isPartOf(a[0], b[0]) if result != arNo: @@ -135,7 +135,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult = # weaken because of indirection: if result != arYes: if isPartOf(a.typ, b.typ) != arNo: result = arMaybe - + of nkHiddenStdConv, nkHiddenSubConv, nkConv: result = isPartOf(a[1], b[1]) of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: @@ -144,26 +144,26 @@ proc isPartOf*(a, b: PNode): TAnalysisResult = # Calls return a new location, so a default of ``arNo`` is fine. else: # go down recursively; this is quite demanding: - const + const Ix0Kinds = {nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv, - nkCheckedFieldExpr} + nkCheckedFieldExpr, nkHiddenAddr} Ix1Kinds = {nkHiddenStdConv, nkHiddenSubConv, nkConv} DerefKinds = {nkHiddenDeref, nkDerefExpr} case b.kind of Ix0Kinds: # a* !<| b.f iff a* !<| b result = isPartOf(a, b[0]) - + of DerefKinds: - # a* !<| b[] iff + # a* !<| b[] iff if isPartOf(a.typ, b.typ) != arNo: result = isPartOf(a, b[0]) if result == arNo: result = arMaybe - + of Ix1Kinds: # a* !<| T(b) iff a* !<| b result = isPartOf(a, b[1]) - + of nkSym: # b is an atom, so we have to check a: case a.kind @@ -172,7 +172,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult = result = isPartOf(a[0], b) of Ix1Kinds: result = isPartOf(a[1], b) - + of DerefKinds: if isPartOf(a.typ, b.typ) != arNo: result = isPartOf(a[0], b) diff --git a/compiler/ast.nim b/compiler/ast.nim index 3798410e8..25958f580 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -10,7 +10,7 @@ # abstract syntax tree + symbol table import - msgs, hashes, nversion, options, strutils, crc, ropes, idents, lists, + msgs, hashes, nversion, options, strutils, securehash, ropes, idents, lists, intsets, idgen type @@ -291,12 +291,13 @@ const sfNoForward* = sfRegister # forward declarations are not required (per module) - sfNoRoot* = sfBorrow # a local variable is provably no root so it doesn't - # require RC ops sfCompileToCpp* = sfInfixCall # compile the module as C++ code sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code sfExperimental* = sfOverriden # module uses the .experimental switch sfGoto* = sfOverriden # var is used for 'goto' code generation + sfWrittenTo* = sfBorrow # param is assigned to + sfEscapes* = sfProcvar # param escapes + sfBase* = sfDiscriminant const # getting ready for the future expr/stmt merge @@ -423,6 +424,7 @@ type # but unfortunately it has measurable impact for compilation # efficiency nfTransf, # node has been transformed + nfNoRewrite # node should not be transformed anymore nfSem # node has been checked for semantics nfLL # node has gone through lambda lifting nfDotField # the call can use a dot operator @@ -475,6 +477,8 @@ type # wildcard type. tfHasAsgn # type has overloaded assignment operator tfBorrowDot # distinct type borrows '.' + tfTriggersCompileTime # uses the NimNode type which make the proc + # implicitly '.compiletime' TTypeFlags* = set[TTypeFlag] @@ -521,6 +525,12 @@ const tfUnion* = tfNoSideEffect tfGcSafe* = tfThread tfObjHasKids* = tfEnumHasHoles + tfOldSchoolExprStmt* = tfVarargs # for now used to distinguish \ + # 'varargs[expr]' from 'varargs[untyped]'. Eventually 'expr' will be + # deprecated and this mess can be cleaned up. + tfVoid* = tfVarargs # for historical reasons we conflated 'void' with + # 'empty' ('@[]' has the type 'seq[empty]'). + tfReturnsNew* = tfInheritable skError* = skUnknown # type flags that are essential for type equality: @@ -529,40 +539,52 @@ const type TMagic* = enum # symbols that require compiler magic: mNone, - mDefined, mDefinedInScope, mCompiles, + mDefined, mDefinedInScope, mCompiles, mArrGet, mArrPut, mAsgn, mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof, mPlugin, mEcho, mShallowCopy, mSlurp, mStaticExec, mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst, - mUnaryLt, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray, - mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq, + mUnaryLt, mInc, mDec, mOrd, + mNew, mNewFinalize, mNewSeq, + mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq, + mXLenStr, mXLenSeq, mIncl, mExcl, mCard, mChr, mGCref, mGCunref, - - mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64, - mDivI64, mModI64, mSucc, mPred, + mAddI, mSubI, mMulI, mDivI, mModI, + mSucc, mPred, mAddF64, mSubF64, mMulF64, mDivF64, - - mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI, - mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, - mMinF64, mMaxF64, mAddU, mSubU, mMulU, - mDivU, mModU, mEqI, mLeI, - mLtI, - mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64, - mLeU, mLtU, mLeU64, mLtU64, - mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef, - mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mEqProc, mUnaryMinusI, - mUnaryMinusI64, mAbsI, mAbsI64, mNot, + mShrI, mShlI, mBitandI, mBitorI, mBitxorI, + mMinI, mMaxI, + mMinF64, mMaxF64, + mAddU, mSubU, mMulU, mDivU, mModU, + mEqI, mLeI, mLtI, + mEqF64, mLeF64, mLtF64, + mLeU, mLtU, + mLeU64, mLtU64, + mEqEnum, mLeEnum, mLtEnum, + mEqCh, mLeCh, mLtCh, + mEqB, mLeB, mLtB, + mEqRef, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, + mXor, mEqProc, + mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI, - 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, mSlice, + 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, mSlice, mDotDot, # this one is only necessary to give nice compile time warnings mFields, mFieldPairs, mOmpParFor, mAppendStrCh, mAppendStrStr, mAppendSeqElem, - mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, + mInRange, mInSet, mRepr, mExit, + mSetLengthStr, mSetLengthSeq, mIsPartOf, mAstToStr, mParallel, mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mNewString, mNewStringOfCap, mParseBiggestFloat, @@ -584,42 +606,53 @@ type mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo, mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr, mNBindSym, mLocals, mNCallSite, - mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError, - mInstantiationInfo, mGetTypeInfo, mNGenSym + mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, + mNHint, mNWarning, mNError, + mInstantiationInfo, mGetTypeInfo, mNGenSym, + mNimvm # 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, mXLenStr, mXLenSeq, + mArrGet, mArrPut, mAsgn, 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, - mMinF64, mMaxF64, mAddU, mSubU, mMulU, - mDivU, mModU, 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, - 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, - mAppendStrCh, mAppendStrStr, mAppendSeqElem, + mAddI, mSubI, mMulI, mDivI, mModI, + mAddF64, mSubF64, mMulF64, mDivF64, + mShrI, mShlI, mBitandI, mBitorI, mBitxorI, + mMinI, mMaxI, + mMinF64, mMaxF64, + mAddU, mSubU, mMulU, mDivU, mModU, + mEqI, mLeI, mLtI, + 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, mNot, mUnaryPlusI, mBitnotI, + 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, mAppendStrCh, mAppendStrStr, mAppendSeqElem, mInRange, mInSet, mRepr, mCopyStr, mCopyStrLast} # magics that require special semantic checking and # thus cannot be overloaded (also documented in the spec!): SpecialSemMagics* = { mDefined, mDefinedInScope, mCompiles, mLow, mHigh, mSizeOf, mIs, mOf, - mEcho, mShallowCopy, mExpandToAst, mParallel, mSpawn, mAstToStr} + mShallowCopy, mExpandToAst, mParallel, mSpawn, mAstToStr} type PNode* = ref TNode @@ -679,6 +712,7 @@ type lfSingleUse # no location yet and will only be used once TStorageLoc* = enum OnUnknown, # location is unknown (stack, heap or static) + OnStatic, # in a static section OnStack, # location is on hardware stack OnHeap # location is on heap or global # (reference counting needed) @@ -704,6 +738,8 @@ type name*: Rope path*: PNode # can be a string literal! + CompilesId* = int ## id that is used for the caching logic within + ## ``system.compiles``. See the seminst module. TInstantiation* = object sym*: PSym concreteTypes*: seq[PType] @@ -711,6 +747,7 @@ type # needed in caas mode for purging the cache # XXX: it's possible to switch to a # simple ref count here + compilesId*: CompilesId PInstantiation* = ref TInstantiation @@ -747,6 +784,7 @@ type tab*: TStrTable # interface table for modules of skLet, skVar, skField, skForVar: guard*: PSym + bitsize*: int else: nil magic*: TMagic typ*: PType @@ -842,7 +880,7 @@ type data*: TIdNodePairSeq TNodePair* = object - h*: THash # because it is expensive to compute! + h*: Hash # because it is expensive to compute! key*: PNode val*: int @@ -947,6 +985,9 @@ proc add*(father, son: PNode) = proc `[]`*(n: PNode, i: int): PNode {.inline.} = result = n.sons[i] +template `-|`*(b, s: expr): expr = + (if b >= 0: b else: s.len + b) + # son access operators with support for negative indices template `{}`*(n: PNode, i: int): expr = n[i -| n] template `{}=`*(n: PNode, i: int, s: PNode): stmt = @@ -1000,7 +1041,8 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym, result.id = getID() when debugIds: registerId(result) - #if result.id < 2000: + #if result.id == 93289: + # writeStacktrace() # MessageOut(name.s & " has id: " & toString(result.id)) var emptyNode* = newNode(nkEmpty) @@ -1198,23 +1240,11 @@ proc newSons*(father: PType, length: int) = else: setLen(father.sons, length) -proc sonsLen*(n: PType): int = - if isNil(n.sons): result = 0 - else: result = len(n.sons) - -proc len*(n: PType): int = - if isNil(n.sons): result = 0 - else: result = len(n.sons) - -proc sonsLen*(n: PNode): int = - if isNil(n.sons): result = 0 - else: result = len(n.sons) - -proc lastSon*(n: PNode): PNode = - result = n.sons[sonsLen(n) - 1] - -proc lastSon*(n: PType): PType = - result = n.sons[sonsLen(n) - 1] +proc sonsLen*(n: PType): int = n.sons.len +proc len*(n: PType): int = n.sons.len +proc sonsLen*(n: PNode): int = n.sons.len +proc lastSon*(n: PNode): PNode = n.sons[^1] +proc lastSon*(n: PType): PType = n.sons[^1] proc assignType*(dest, src: PType) = dest.kind = src.kind @@ -1356,8 +1386,11 @@ proc propagateToOwner*(owner, elem: PType) = o2.flags.incl tfHasAsgn owner.flags.incl tfHasAsgn + if tfTriggersCompileTime in elem.flags: + owner.flags.incl tfTriggersCompileTime + if owner.kind notin {tyProc, tyGenericInst, tyGenericBody, - tyGenericInvocation}: + tyGenericInvocation, tyPtr}: let elemB = elem.skipTypes({tyGenericInst}) if elemB.isGCedMem or tfHasGCedMem in elemB.flags: # for simplicity, we propagate this flag even to generics. We then @@ -1496,6 +1529,9 @@ proc getFloat*(a: PNode): BiggestFloat = proc getStr*(a: PNode): string = case a.kind of nkStrLit..nkTripleStrLit: result = a.strVal + of nkNilLit: + # let's hope this fixes more problems than it creates: + result = nil else: internalError(a.info, "getStr") result = "" @@ -1553,6 +1589,14 @@ proc makeStmtList*(n: PNode): PNode = result = newNodeI(nkStmtList, n.info) result.add n +proc skipStmtList*(n: PNode): PNode = + if n.kind in {nkStmtList, nkStmtListExpr}: + for i in 0 .. n.len-2: + if n[i].kind notin {nkEmpty, nkCommentStmt}: return n + result = n.lastSon + else: + result = n + proc createMagic*(name: string, m: TMagic): PSym = result = newSym(skProc, getIdent(name), nil, unknownLineInfo()) result.magic = m @@ -1560,3 +1604,10 @@ proc createMagic*(name: string, m: TMagic): PSym = let opNot* = createMagic("not", mNot) opContains* = createMagic("contains", mInSet) + +when false: + proc containsNil*(n: PNode): bool = + # only for debugging + if n.isNil: return true + for i in 0 ..< n.safeLen: + if n[i].containsNil: return true diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 1707718d7..3ba43b4c5 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -14,7 +14,7 @@ import ast, hashes, intsets, strutils, options, msgs, ropes, idents, rodutils -proc hashNode*(p: RootRef): THash +proc hashNode*(p: RootRef): Hash proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope # Convert a tree into its YAML representation; this is used by the # YAML code generator and it is invaluable for debugging purposes. @@ -49,7 +49,7 @@ proc strTableGet*(t: TStrTable, name: PIdent): PSym type TTabIter*{.final.} = object # consider all fields here private - h*: THash # current hash + h*: Hash # current hash proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym @@ -65,7 +65,7 @@ proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym type TIdentIter*{.final.} = object # iterator over all syms with same identifier - h*: THash # current hash + h*: Hash # current hash name*: PIdent @@ -94,7 +94,7 @@ proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym proc lookupInRecord*(n: PNode, field: PIdent): PSym proc getModule*(s: PSym): PSym proc mustRehash*(length, counter: int): bool -proc nextTry*(h, maxHash: THash): THash {.inline.} +proc nextTry*(h, maxHash: Hash): Hash {.inline.} # ------------- table[int, int] --------------------------------------------- const @@ -196,7 +196,7 @@ proc getSymFromList(list: PNode, ident: PIdent, start: int = 0): PSym = else: internalError(list.info, "getSymFromList") result = nil -proc hashNode(p: RootRef): THash = +proc hashNode(p: RootRef): Hash = result = hash(cast[pointer](p)) proc mustRehash(length, counter: int): bool = @@ -452,7 +452,7 @@ proc debug(n: PSym) = elif n.kind == skUnknown: msgWriteln("skUnknown") else: - #writeln(stdout, $symToYaml(n, 0, 1)) + #writeLine(stdout, $symToYaml(n, 0, 1)) msgWriteln("$1_$2: $3, $4, $5, $6" % [ n.name.s, $n.id, $flagsToStr(n.flags), $flagsToStr(n.loc.flags), $lineInfoToStr(n.info), $n.kind]) @@ -466,7 +466,7 @@ proc debug(n: PNode) = const EmptySeq = @[] -proc nextTry(h, maxHash: THash): THash = +proc nextTry(h, maxHash: Hash): Hash = result = ((5 * h) + 1) and maxHash # For any initial h in range(maxHash), repeating that maxHash times # generates each int in range(maxHash) exactly once (see any text on @@ -474,7 +474,7 @@ proc nextTry(h, maxHash: THash): THash = proc objectSetContains(t: TObjectSet, obj: RootRef): bool = # returns true whether n is in t - var h: THash = hashNode(obj) and high(t.data) # start with real hash value + var h: Hash = hashNode(obj) and high(t.data) # start with real hash value while t.data[h] != nil: if t.data[h] == obj: return true @@ -482,7 +482,7 @@ proc objectSetContains(t: TObjectSet, obj: RootRef): bool = result = false proc objectSetRawInsert(data: var TObjectSeq, obj: RootRef) = - var h: THash = hashNode(obj) and high(data) + var h: Hash = hashNode(obj) and high(data) while data[h] != nil: assert(data[h] != obj) h = nextTry(h, high(data)) @@ -503,7 +503,7 @@ proc objectSetIncl(t: var TObjectSet, obj: RootRef) = proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool = # returns true if obj is already in the string table: - var h: THash = hashNode(obj) and high(t.data) + var h: Hash = hashNode(obj) and high(t.data) while true: var it = t.data[h] if it == nil: break @@ -520,7 +520,7 @@ proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool = result = false proc tableRawGet(t: TTable, key: RootRef): int = - var h: THash = hashNode(key) and high(t.data) # start with real hash value + var h: Hash = hashNode(key) and high(t.data) # start with real hash value while t.data[h].key != nil: if t.data[h].key == key: return h @@ -529,7 +529,7 @@ proc tableRawGet(t: TTable, key: RootRef): int = proc tableSearch(t: TTable, key, closure: RootRef, comparator: TCmpProc): RootRef = - var h: THash = hashNode(key) and high(t.data) # start with real hash value + var h: Hash = hashNode(key) and high(t.data) # start with real hash value while t.data[h].key != nil: if t.data[h].key == key: if comparator(t.data[h].val, closure): @@ -544,7 +544,7 @@ proc tableGet(t: TTable, key: RootRef): RootRef = else: result = nil proc tableRawInsert(data: var TPairSeq, key, val: RootRef) = - var h: THash = hashNode(key) and high(data) + var h: Hash = hashNode(key) and high(data) while data[h].key != nil: assert(data[h].key != key) h = nextTry(h, high(data)) @@ -569,7 +569,7 @@ proc tablePut(t: var TTable, key, val: RootRef) = inc(t.counter) proc strTableContains(t: TStrTable, n: PSym): bool = - var h: THash = n.name.h and high(t.data) # start with real hash value + var h: Hash = n.name.h and high(t.data) # start with real hash value while t.data[h] != nil: if (t.data[h] == n): return true @@ -577,7 +577,7 @@ proc strTableContains(t: TStrTable, n: PSym): bool = result = false proc strTableRawInsert(data: var TSymSeq, n: PSym) = - var h: THash = n.name.h and high(data) + var h: Hash = n.name.h and high(data) if sfImmediate notin n.flags: # fast path: while data[h] != nil: @@ -606,7 +606,7 @@ proc strTableRawInsert(data: var TSymSeq, n: PSym) = proc symTabReplaceRaw(data: var TSymSeq, prevSym: PSym, newSym: PSym) = assert prevSym.name.h == newSym.name.h - var h: THash = prevSym.name.h and high(data) + var h: Hash = prevSym.name.h and high(data) while data[h] != nil: if data[h] == prevSym: data[h] = newSym @@ -640,7 +640,7 @@ proc strTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} = # It is essential that `n` is written nevertheless! # This way the newest redefinition is picked by the semantic analyses! assert n.name != nil - var h: THash = n.name.h and high(t.data) + var h: Hash = n.name.h and high(t.data) var replaceSlot = -1 while true: var it = t.data[h] @@ -666,7 +666,7 @@ proc strTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} = result = false proc strTableGet(t: TStrTable, name: PIdent): PSym = - var h: THash = name.h and high(t.data) + var h: Hash = name.h and high(t.data) while true: result = t.data[h] if result == nil: break @@ -694,7 +694,7 @@ proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym = proc nextIdentExcluding*(ti: var TIdentIter, tab: TStrTable, excluding: IntSet): PSym = - var h: THash = ti.h and high(tab.data) + var h: Hash = ti.h and high(tab.data) var start = h result = tab.data[h] while result != nil: @@ -743,7 +743,7 @@ proc hasEmptySlot(data: TIdPairSeq): bool = result = false proc idTableRawGet(t: TIdTable, key: int): int = - var h: THash + var h: Hash h = key and high(t.data) # start with real hash value while t.data[h].key != nil: if t.data[h].key.id == key: @@ -772,7 +772,7 @@ iterator pairs*(t: TIdTable): tuple[key: int, value: RootRef] = yield (t.data[i].key.id, t.data[i].val) proc idTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: RootRef) = - var h: THash + var h: Hash h = key.id and high(data) while data[h].key != nil: assert(data[h].key.id != key.id) @@ -805,7 +805,7 @@ iterator idTablePairs*(t: TIdTable): tuple[key: PIdObj, val: RootRef] = if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val) proc idNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int = - var h: THash + var h: Hash h = key.id and high(t.data) # start with real hash value while t.data[h].key != nil: if t.data[h].key.id == key.id: @@ -824,7 +824,7 @@ proc idNodeTableGetLazy*(t: TIdNodeTable, key: PIdObj): PNode = result = idNodeTableGet(t, key) proc idNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) = - var h: THash + var h: Hash h = key.id and high(data) while data[h].key != nil: assert(data[h].key.id != key.id) @@ -863,7 +863,7 @@ proc initIITable(x: var TIITable) = for i in countup(0, StartSize - 1): x.data[i].key = InvalidKey proc iiTableRawGet(t: TIITable, key: int): int = - var h: THash + var h: Hash h = key and high(t.data) # start with real hash value while t.data[h].key != InvalidKey: if t.data[h].key == key: return h @@ -876,7 +876,7 @@ proc iiTableGet(t: TIITable, key: int): int = else: result = InvalidKey proc iiTableRawInsert(data: var TIIPairSeq, key, val: int) = - var h: THash + var h: Hash h = key and high(data) while data[h].key != InvalidKey: assert(data[h].key != key) diff --git a/compiler/bitsets.nim b/compiler/bitsets.nim index a2324f4e2..5454ef5e7 100644 --- a/compiler/bitsets.nim +++ b/compiler/bitsets.nim @@ -10,12 +10,12 @@ # this unit handles Nim sets; it implements bit sets # the code here should be reused in the Nim standard library -type +type TBitSet* = seq[int8] # we use byte here to avoid issues with # cross-compiling; uint would be more efficient # however -const +const ElemSize* = sizeof(int8) * 8 proc bitSetInit*(b: var TBitSet, length: int) @@ -30,42 +30,42 @@ proc bitSetEquals*(x, y: TBitSet): bool proc bitSetContains*(x, y: TBitSet): bool # implementation -proc bitSetIn(x: TBitSet, e: BiggestInt): bool = +proc bitSetIn(x: TBitSet, e: BiggestInt): bool = result = (x[int(e div ElemSize)] and toU8(int(1 shl (e mod ElemSize)))) != toU8(0) -proc bitSetIncl(x: var TBitSet, elem: BiggestInt) = +proc bitSetIncl(x: var TBitSet, elem: BiggestInt) = assert(elem >= 0) x[int(elem div ElemSize)] = x[int(elem div ElemSize)] or toU8(int(1 shl (elem mod ElemSize))) -proc bitSetExcl(x: var TBitSet, elem: BiggestInt) = +proc bitSetExcl(x: var TBitSet, elem: BiggestInt) = x[int(elem div ElemSize)] = x[int(elem div ElemSize)] and not toU8(int(1 shl (elem mod ElemSize))) -proc bitSetInit(b: var TBitSet, length: int) = +proc bitSetInit(b: var TBitSet, length: int) = newSeq(b, length) -proc bitSetUnion(x: var TBitSet, y: TBitSet) = +proc bitSetUnion(x: var TBitSet, y: TBitSet) = for i in countup(0, high(x)): x[i] = x[i] or y[i] - -proc bitSetDiff(x: var TBitSet, y: TBitSet) = + +proc bitSetDiff(x: var TBitSet, y: TBitSet) = for i in countup(0, high(x)): x[i] = x[i] and not y[i] - -proc bitSetSymDiff(x: var TBitSet, y: TBitSet) = + +proc bitSetSymDiff(x: var TBitSet, y: TBitSet) = for i in countup(0, high(x)): x[i] = x[i] xor y[i] - -proc bitSetIntersect(x: var TBitSet, y: TBitSet) = + +proc bitSetIntersect(x: var TBitSet, y: TBitSet) = for i in countup(0, high(x)): x[i] = x[i] and y[i] - -proc bitSetEquals(x, y: TBitSet): bool = - for i in countup(0, high(x)): - if x[i] != y[i]: + +proc bitSetEquals(x, y: TBitSet): bool = + for i in countup(0, high(x)): + if x[i] != y[i]: return false result = true -proc bitSetContains(x, y: TBitSet): bool = - for i in countup(0, high(x)): - if (x[i] and not y[i]) != int8(0): +proc bitSetContains(x, y: TBitSet): bool = + for i in countup(0, high(x)): + if (x[i] and not y[i]) != int8(0): return false result = true diff --git a/compiler/canonicalizer.nim b/compiler/canonicalizer.nim index 6fcc57a91..dc6445035 100644 --- a/compiler/canonicalizer.nim +++ b/compiler/canonicalizer.nim @@ -30,39 +30,39 @@ type # # This is a good compromise between correctness and brevity. ;-) -const +const cb64 = [ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", - "O", "P", "Q", "R", "S", "T" "U", "V", "W", "X", "Y", "Z", - "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", - "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", + "O", "P", "Q", "R", "S", "T" "U", "V", "W", "X", "Y", "Z", + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", + "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", - "_A", "_B"] - -proc toBase64a(s: cstring, len: int): string = - ## encodes `s` into base64 representation. After `lineLen` characters, a - ## `newline` is added. - result = newStringOfCap(((len + 2) div 3) * 4) - var i = 0 - while i < s.len - 2: - let a = ord(s[i]) - let b = ord(s[i+1]) - let c = ord(s[i+2]) - result.add cb64[a shr 2] - result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)] - result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)] - result.add cb64[c and 0x3F] - inc(i, 3) - if i < s.len-1: - let a = ord(s[i]) - let b = ord(s[i+1]) - result.add cb64[a shr 2] - result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)] - result.add cb64[((b and 0x0F) shl 2)] - elif i < s.len: - let a = ord(s[i]) - result.add cb64[a shr 2] - result.add cb64[(a and 3) shl 4] + "_A", "_B"] + +proc toBase64a(s: cstring, len: int): string = + ## encodes `s` into base64 representation. After `lineLen` characters, a + ## `newline` is added. + result = newStringOfCap(((len + 2) div 3) * 4) + var i = 0 + while i < s.len - 2: + let a = ord(s[i]) + let b = ord(s[i+1]) + let c = ord(s[i+2]) + result.add cb64[a shr 2] + result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)] + result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)] + result.add cb64[c and 0x3F] + inc(i, 3) + if i < s.len-1: + let a = ord(s[i]) + let b = ord(s[i+1]) + result.add cb64[a shr 2] + result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)] + result.add cb64[((b and 0x0F) shl 2)] + elif i < s.len: + let a = ord(s[i]) + result.add cb64[a shr 2] + result.add cb64[(a and 3) shl 4] proc toBase64a(u: TUid): string = toBase64a(cast[cstring](u), sizeof(u)) @@ -73,7 +73,7 @@ proc hashSym(c: var MD5Context, s: PSym) = c &= ":anon" else: var it = s.owner - while it != nil: + while it != nil: hashSym(c, it) c &= "." it = s.owner @@ -106,18 +106,18 @@ proc hashTree(c: var MD5Context, n: PNode) = proc hashType(c: var MD5Context, t: PType) = # modelled after 'typeToString' - if t == nil: + if t == nil: c &= "\254" return var k = t.kind md5Update(c, cast[cstring](addr(k)), 1) - + if t.sym != nil and sfAnon notin t.sym.flags: # t.n for literals, but not for e.g. objects! if t.kind in {tyFloat, tyInt}: c.hashNode(t.n) c.hashSym(t.sym) - + case t.kind of tyGenericBody, tyGenericInst, tyGenericInvocation: for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvocation)): @@ -135,10 +135,10 @@ proc hashType(c: var MD5Context, t: PType) = of tyArrayConstr: c.hashTree(t.sons[0].n) c.hashType(t.sons[1]) - of tyTuple: + of tyTuple: if t.n != nil: assert(sonsLen(t.n) == sonsLen(t)) - for i in countup(0, sonsLen(t.n) - 1): + for i in countup(0, sonsLen(t.n) - 1): assert(t.n.sons[i].kind == nkSym) c &= t.n.sons[i].sym.name.s c &= ":" @@ -184,18 +184,18 @@ proc pushSym(w: PRodWriter, s: PSym) = if iiTableGet(w.index.tab, s.id) == InvalidKey: w.sstack.add(s) -proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, - result: var string) = - if n == nil: +proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, + result: var string) = + if n == nil: # nil nodes have to be stored too: result.add("()") return result.add('(') - encodeVInt(ord(n.kind), result) + encodeVInt(ord(n.kind), result) # we do not write comments for now # Line information takes easily 20% or more of the filesize! Therefore we # omit line information if it is the same as the father's line information: - if fInfo.fileIndex != n.info.fileIndex: + if fInfo.fileIndex != n.info.fileIndex: result.add('?') encodeVInt(n.info.col, result) result.add(',') @@ -211,7 +211,7 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, result.add('?') encodeVInt(n.info.col, result) var f = n.flags * PersistentNodeFlags - if f != {}: + if f != {}: result.add('$') encodeVInt(cast[int32](f), result) if n.typ != nil: @@ -219,16 +219,16 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, encodeVInt(n.typ.id, result) pushType(w, n.typ) case n.kind - of nkCharLit..nkInt64Lit: + of nkCharLit..nkInt64Lit: if n.intVal != 0: result.add('!') encodeVBiggestInt(n.intVal, result) - of nkFloatLit..nkFloat64Lit: - if n.floatVal != 0.0: + of nkFloatLit..nkFloat64Lit: + if n.floatVal != 0.0: result.add('!') encodeStr($n.floatVal, result) of nkStrLit..nkTripleStrLit: - if n.strVal != "": + if n.strVal != "": result.add('!') encodeStr(n.strVal, result) of nkIdent: @@ -239,7 +239,7 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, encodeVInt(n.sym.id, result) pushSym(w, n.sym) else: - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): encodeNode(w, n.info, n.sons[i], result) add(result, ')') @@ -268,9 +268,9 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) = setLen(result, oldLen) else: add(result, '>') - -proc encodeType(w: PRodWriter, t: PType, result: var string) = - if t == nil: + +proc encodeType(w: PRodWriter, t: PType, result: var string) = + if t == nil: # nil nodes have to be stored too: result.add("[]") return @@ -282,38 +282,38 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) = encodeVInt(ord(t.kind), result) add(result, '+') encodeVInt(t.id, result) - if t.n != nil: + if t.n != nil: encodeNode(w, unknownLineInfo(), t.n, result) - if t.flags != {}: + if t.flags != {}: add(result, '$') encodeVInt(cast[int32](t.flags), result) - if t.callConv != low(t.callConv): + if t.callConv != low(t.callConv): add(result, '?') encodeVInt(ord(t.callConv), result) - if t.owner != nil: + if t.owner != nil: add(result, '*') encodeVInt(t.owner.id, result) pushSym(w, t.owner) - if t.sym != nil: + if t.sym != nil: add(result, '&') encodeVInt(t.sym.id, result) pushSym(w, t.sym) - if t.size != - 1: + if t.size != - 1: add(result, '/') encodeVBiggestInt(t.size, result) - if t.align != 2: + if t.align != 2: add(result, '=') encodeVInt(t.align, result) encodeLoc(w, t.loc, result) - for i in countup(0, sonsLen(t) - 1): - if t.sons[i] == nil: + for i in countup(0, sonsLen(t) - 1): + if t.sons[i] == nil: add(result, "^()") - else: - add(result, '^') + else: + add(result, '^') encodeVInt(t.sons[i].id, result) pushType(w, t.sons[i]) -proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) = +proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) = add(result, '|') encodeVInt(ord(lib.kind), result) add(result, '|') @@ -352,10 +352,10 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) = if s.magic != mNone: result.add('@') encodeVInt(ord(s.magic), result) - if s.options != w.options: + if s.options != w.options: result.add('!') encodeVInt(cast[int32](s.options), result) - if s.position != 0: + if s.position != 0: result.add('%') encodeVInt(s.position, result) if s.offset != - 1: @@ -383,7 +383,7 @@ proc createDb() = fullpath varchar(256) not null, interfHash varchar(256) not null, fullHash varchar(256) not null, - + created timestamp not null default (DATETIME('now')), );""") @@ -397,7 +397,7 @@ proc createDb() = foreign key (module) references module(id) );""") - + db.exec(sql""" create table if not exists Type( id integer primary key, diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 2dacc25e9..86ecc9db8 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -159,6 +159,17 @@ proc genArgNoParam(p: BProc, n: PNode): Rope = initLocExprSingleUse(p, n, a) result = rdLoc(a) +template genParamLoop(params) {.dirty.} = + if i < sonsLen(typ): + assert(typ.n.sons[i].kind == nkSym) + let paramType = typ.n.sons[i] + if not paramType.typ.isCompileTimeOnly: + if params != nil: add(params, ~", ") + add(params, genArg(p, ri.sons[i], paramType.sym, ri)) + else: + if params != nil: add(params, ~", ") + add(params, genArgNoParam(p, ri.sons[i])) + proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) = var op: TLoc # this is a hotspot in the compiler @@ -170,13 +181,7 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) = assert(sonsLen(typ) == sonsLen(typ.n)) var length = sonsLen(ri) for i in countup(1, length - 1): - if ri.sons[i].typ.isCompileTimeOnly: continue - if params != nil: add(params, ~", ") - if i < sonsLen(typ): - assert(typ.n.sons[i].kind == nkSym) - add(params, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri)) - else: - add(params, genArgNoParam(p, ri.sons[i])) + genParamLoop(params) fixupCall(p, le, ri, d, op.r, params) proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = @@ -198,13 +203,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = var length = sonsLen(ri) for i in countup(1, length - 1): assert(sonsLen(typ) == sonsLen(typ.n)) - if ri.sons[i].typ.isCompileTimeOnly: continue - if i < sonsLen(typ): - assert(typ.n.sons[i].kind == nkSym) - add(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri)) - else: - add(pl, genArgNoParam(p, ri.sons[i])) - if i < length - 1: add(pl, ~", ") + genParamLoop(pl) template genCallPattern {.dirty.} = lineF(p, cpsStmts, callPattern & ";$n", [op.r, pl, pl.addComma, rawProc]) @@ -241,13 +240,14 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = genCallPattern() proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope = - if ri.sons[i].typ.isCompileTimeOnly: - result = nil - elif i < sonsLen(typ): + if i < sonsLen(typ): # 'var T' is 'T&' in C++. This means we ignore the request of # any nkHiddenAddr when it's a 'var T'. - assert(typ.n.sons[i].kind == nkSym) - if typ.sons[i].kind == tyVar and ri.sons[i].kind == nkHiddenAddr: + let paramType = typ.n.sons[i] + assert(paramType.kind == nkSym) + if paramType.typ.isCompileTimeOnly: + result = nil + elif typ.sons[i].kind == tyVar and ri.sons[i].kind == nkHiddenAddr: result = genArgNoParam(p, ri.sons[i][0]) else: result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 05a3602d1..388b6d047 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -221,7 +221,7 @@ proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc = proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = let newflags = - if src.k == locData: + if src.s == OnStatic: flags + {needToCopy} elif tfShallow in dest.t.flags: flags - {needToCopy} @@ -238,7 +238,7 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags, t: PNode) = if t == nil: return let newflags = - if src.k == locData: + if src.s == OnStatic: flags + {needToCopy} elif tfShallow in dest.t.flags: flags - {needToCopy} @@ -287,13 +287,13 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = of tyRef: genRefAssign(p, dest, src, flags) of tySequence: - if needToCopy notin flags and src.k != locData: + if needToCopy notin flags and src.s != OnStatic: genRefAssign(p, dest, src, flags) else: linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n", addrLoc(dest), rdLoc(src), genTypeInfo(p.module, dest.t)) of tyString: - if needToCopy notin flags and src.k != locData: + if needToCopy notin flags and src.s != OnStatic: genRefAssign(p, dest, src, flags) else: if dest.s == OnStack or not usesNativeGC(): @@ -347,8 +347,8 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = else: useStringh(p.module) linefmt(p, cpsStmts, - "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($1));$n", - rdLoc(dest), rdLoc(src)) + "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", + rdLoc(dest), rdLoc(src), getTypeDesc(p.module, ty)) of tyOpenArray, tyVarargs: # open arrays are always on the stack - really? What if a sequence is # passed to an open array? @@ -413,7 +413,7 @@ proc putDataIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope) = var a: TLoc if d.k != locNone: # need to generate an assignment here - initLoc(a, locData, t, OnUnknown) + initLoc(a, locData, t, OnStatic) a.r = r if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {}) else: genAssignment(p, d, a, {needToCopy}) @@ -424,11 +424,11 @@ proc putDataIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope) = d.t = t d.r = r -proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope) = +proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope; s=OnUnknown) = var a: TLoc if d.k != locNone: # need to generate an assignment here - initLoc(a, locExpr, t, OnUnknown) + initLoc(a, locExpr, t, s) a.r = r if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {}) else: genAssignment(p, d, a, {needToCopy}) @@ -503,15 +503,15 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = "$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n", "$# = #mulInt($#, $#);$n", "$# = #divInt($#, $#);$n", "$# = #modInt($#, $#);$n", + "$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n"] + prc64: array[mAddI..mPred, string] = [ "$# = #addInt64($#, $#);$n", "$# = #subInt64($#, $#);$n", "$# = #mulInt64($#, $#);$n", "$# = #divInt64($#, $#);$n", "$# = #modInt64($#, $#);$n", - "$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n"] + "$# = #addInt64($#, $#);$n", "$# = #subInt64($#, $#);$n"] opr: array[mAddI..mPred, string] = [ "($#)($# + $#)", "($#)($# - $#)", "($#)($# * $#)", "($#)($# / $#)", "($#)($# % $#)", - "($#)($# + $#)", "($#)($# - $#)", "($#)($# * $#)", - "($#)($# / $#)", "($#)($# % $#)", "($#)($# + $#)", "($#)($# - $#)"] var a, b: TLoc assert(e.sons[1].typ != nil) @@ -525,16 +525,16 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = let res = opr[m] % [getTypeDesc(p.module, t), rdLoc(a), rdLoc(b)] putIntoDest(p, d, e.typ, res) else: - let res = binaryArithOverflowRaw(p, t, a, b, prc[m]) + let res = binaryArithOverflowRaw(p, t, a, b, + if t.kind == tyInt64: prc64[m] else: prc[m]) putIntoDest(p, d, e.typ, "($#)($#)" % [getTypeDesc(p.module, t), res]) proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = const - opr: array[mUnaryMinusI..mAbsI64, string] = [ + opr: array[mUnaryMinusI..mAbsI, string] = [ mUnaryMinusI: "((NI$2)-($1))", mUnaryMinusI64: "-($1)", - mAbsI: "($1 > 0? ($1) : -($1))", - mAbsI64: "($1 > 0? ($1) : -($1))"] + mAbsI: "($1 > 0? ($1) : -($1))"] var a: TLoc t: PType @@ -561,11 +561,6 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = "($4)($1 ^ $2)", # BitxorI "(($1 <= $2) ? $1 : $2)", # MinI "(($1 >= $2) ? $1 : $2)", # MaxI - "($4)((NU64)($1) >> (NU64)($2))", # ShrI64 - "($4)((NU64)($1) << (NU64)($2))", # ShlI64 - "($4)($1 & $2)", # BitandI64 - "($4)($1 | $2)", # BitorI64 - "($4)($1 ^ $2)", # BitxorI64 "(($1 <= $2) ? $1 : $2)", # MinF64 "(($1 >= $2) ? $1 : $2)", # MaxF64 "($4)((NU$3)($1) + (NU$3)($2))", # AddU @@ -576,9 +571,6 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = "($1 == $2)", # EqI "($1 <= $2)", # LeI "($1 < $2)", # LtI - "($1 == $2)", # EqI64 - "($1 <= $2)", # LeI64 - "($1 < $2)", # LtI64 "($1 == $2)", # EqF64 "($1 <= $2)", # LeF64 "($1 < $2)", # LtF64 @@ -638,7 +630,6 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = unArithTab: array[mNot..mToBiggestInt, string] = ["!($1)", # Not "$1", # UnaryPlusI "($3)((NU$2) ~($1))", # BitnotI - "($3)((NU$2) ~($1))", # BitnotI64 "$1", # UnaryPlusF64 "-($1)", # UnaryMinusF64 "($1 > 0? ($1) : -($1))", # AbsF64; BUGFIX: fabs() makes problems @@ -694,7 +685,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = d.s = OnUnknown if tfVarIsPtr notin typ.flags and p.module.compileToCpp and e.kind == nkHiddenDeref: - putIntoDest(p, d, e.typ, rdLoc(a)) + putIntoDest(p, d, e.typ, rdLoc(a), a.s) return of tyPtr: d.s = OnUnknown # BUGFIX! @@ -703,7 +694,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = let typ = skipTypes(a.t, abstractInst) if typ.kind == tyVar and tfVarIsPtr notin typ.flags and e.kind == nkHiddenDeref: - putIntoDest(p, d, e.typ, rdLoc(a)) + putIntoDest(p, d, e.typ, rdLoc(a), a.s) return if enforceDeref and mt == ctPtrToArray: # we lie about the type for better C interop: 'ptr array[3,T]' is @@ -711,23 +702,23 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = # See tmissingderef. So we get rid of the deref instead. The codegen # ends up using 'memcpy' for the array assignment, # so the '&' and '*' cancel out: - putIntoDest(p, d, a.t.sons[0], rdLoc(a)) + putIntoDest(p, d, a.t.sons[0], rdLoc(a), a.s) else: - putIntoDest(p, d, e.typ, "(*$1)" % [rdLoc(a)]) + putIntoDest(p, d, e.typ, "(*$1)" % [rdLoc(a)], a.s) proc genAddr(p: BProc, e: PNode, d: var TLoc) = # careful 'addr(myptrToArray)' needs to get the ampersand: if e.sons[0].typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}: var a: TLoc initLocExpr(p, e.sons[0], a) - putIntoDest(p, d, e.typ, "&" & a.r) + putIntoDest(p, d, e.typ, "&" & a.r, a.s) #Message(e.info, warnUser, "HERE NEW &") elif mapType(e.sons[0].typ) == ctArray or isCppRef(p, e.sons[0].typ): expr(p, e.sons[0], d) else: var a: TLoc initLocExpr(p, e.sons[0], a) - putIntoDest(p, d, e.typ, addrLoc(a)) + putIntoDest(p, d, e.typ, addrLoc(a), a.s) template inheritLocation(d: var TLoc, a: TLoc) = if d.k == locNone: d.s = a.s @@ -754,7 +745,18 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) = of nkIntLit..nkUInt64Lit: i = int(e.sons[1].intVal) else: internalError(e.info, "genTupleElem") addf(r, ".Field$1", [rope(i)]) - putIntoDest(p, d, ty.sons[i], r) + putIntoDest(p, d, ty.sons[i], r, a.s) + +proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope): PSym = + var ty = ty + assert r != nil + while ty != nil: + assert(ty.kind in {tyTuple, tyObject}) + result = lookupInRecord(ty.n, field.name) + if result != nil: break + if not p.module.compileToCpp: add(r, ".Sup") + ty = getUniqueType(ty.sons[0]) + if result == nil: internalError(field.info, "genCheckedRecordField") proc genRecordField(p: BProc, e: PNode, d: var TLoc) = var a: TLoc @@ -765,25 +767,18 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) = # we found a unique tuple type which lacks field information # so we use Field$i addf(r, ".Field$1", [rope(f.position)]) - putIntoDest(p, d, f.typ, r) + putIntoDest(p, d, f.typ, r, a.s) else: - var field: PSym = nil - while ty != nil: - if ty.kind notin {tyTuple, tyObject}: - internalError(e.info, "genRecordField") - field = lookupInRecord(ty.n, f.name) - if field != nil: break - if not p.module.compileToCpp: add(r, ".Sup") - ty = getUniqueType(ty.sons[0]) - if field == nil: internalError(e.info, "genRecordField 2 ") + let field = lookupFieldAgain(p, ty, f, r) if field.loc.r == nil: internalError(e.info, "genRecordField 3") addf(r, ".$1", [field.loc.r]) - putIntoDest(p, d, field.typ, r) + putIntoDest(p, d, field.typ, r, a.s) #d.s = a.s proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) -proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = +proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym; + origTy: PType) = var test, u, v: TLoc for i in countup(1, sonsLen(e) - 1): var it = e.sons[i] @@ -795,8 +790,12 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = assert(disc.kind == nkSym) initLoc(test, locNone, it.typ, OnStack) initLocExpr(p, it.sons[1], u) - initLoc(v, locExpr, disc.typ, OnUnknown) - v.r = "$1.$2" % [obj, disc.sym.loc.r] + var o = obj + let d = lookupFieldAgain(p, origTy, disc.sym, o) + initLoc(v, locExpr, d.typ, OnUnknown) + v.r = o + v.r.add(".") + v.r.add(d.loc.r) genInExprAux(p, it, u, v, test) let id = nodeTableTestOrSet(p.module.dataCache, newStrNode(nkStrLit, field.name.s), gBackendId) @@ -813,27 +812,16 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = if optFieldCheck in p.options: - var - a: TLoc - f, field: PSym - ty: PType - r: Rope - ty = genRecordFieldAux(p, e.sons[0], d, a) - r = rdLoc(a) - f = e.sons[0].sons[1].sym - field = nil - while ty != nil: - assert(ty.kind in {tyTuple, tyObject}) - field = lookupInRecord(ty.n, f.name) - if field != nil: break - if not p.module.compileToCpp: add(r, ".Sup") - ty = getUniqueType(ty.sons[0]) - if field == nil: internalError(e.info, "genCheckedRecordField") + var a: TLoc + let ty = genRecordFieldAux(p, e.sons[0], d, a) + var r = rdLoc(a) + let f = e.sons[0].sons[1].sym + let field = lookupFieldAgain(p, ty, f, r) if field.loc.r == nil: internalError(e.info, "genCheckedRecordField") # generate the checks: - genFieldCheck(p, e, r, field) + genFieldCheck(p, e, r, field, ty) add(r, rfmt(nil, ".$1", field.loc.r)) - putIntoDest(p, d, field.typ, r) + putIntoDest(p, d, field.typ, r, a.s) else: genRecordField(p, e.sons[0], d) @@ -860,7 +848,7 @@ proc genArrayElem(p: BProc, x, y: PNode, d: var TLoc) = localError(x.info, errIndexOutOfBounds) d.inheritLocation(a) putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)), - rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first)) + rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.s) proc genCStringElem(p: BProc, x, y: PNode, d: var TLoc) = var a, b: TLoc @@ -869,7 +857,7 @@ proc genCStringElem(p: BProc, x, y: PNode, d: var TLoc) = var ty = skipTypes(a.t, abstractVarRange) if d.k == locNone: d.s = a.s putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)), - rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b))) + rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s) proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) = var a, b: TLoc @@ -880,7 +868,7 @@ proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) = rdLoc(b), rdLoc(a)) # BUGFIX: ``>=`` and not ``>``! if d.k == locNone: d.s = a.s putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)), - rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b))) + rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s) proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) = var a, b: TLoc @@ -903,7 +891,7 @@ proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) = if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: a.r = rfmt(nil, "(*$1)", a.r) putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)), - rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b))) + rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b)), a.s) proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) = var ty = skipTypes(n.sons[0].typ, abstractVarRange) @@ -1061,9 +1049,9 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = # seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x)); # seq->data[seq->len-1] = x; let seqAppendPattern = if not p.module.compileToCpp: - "$1 = ($2) #incrSeq(&($1)->Sup, sizeof($3));$n" + "$1 = ($2) #incrSeqV2(&($1)->Sup, sizeof($3));$n" else: - "$1 = ($2) #incrSeq($1, sizeof($3));$n" + "$1 = ($2) #incrSeqV2($1, sizeof($3));$n" var a, b, dest: TLoc initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) @@ -1073,8 +1061,9 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = getTypeDesc(p.module, skipTypes(e.sons[2].typ, abstractVar))]) keepAlive(p, a) initLoc(dest, locExpr, b.t, OnHeap) - dest.r = rfmt(nil, "$1->data[$1->$2-1]", rdLoc(a), lenField(p)) + dest.r = rfmt(nil, "$1->data[$1->$2]", rdLoc(a), lenField(p)) genAssignment(p, dest, b, {needToCopy, afDestIsNil}) + lineCg(p, cpsStmts, "++$1->$2;$n", rdLoc(a), lenField(p)) gcUsage(e) proc genReset(p: BProc, n: PNode) = @@ -1158,20 +1147,15 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = else: constructLoc(p, tmp) discard getTypeDesc(p.module, t) + let ty = getUniqueType(t) for i in 1 .. <e.len: let it = e.sons[i] var tmp2: TLoc tmp2.r = r - var field: PSym = nil - var ty = getUniqueType(t) - while ty != nil: - field = lookupInRecord(ty.n, it.sons[0].sym.name) - if field != nil: break - if not p.module.compileToCpp: add(tmp2.r, ".Sup") - ty = getUniqueType(ty.sons[0]) - if field == nil or field.loc.r == nil: internalError(e.info, "genObjConstr") + let field = lookupFieldAgain(p, ty, it.sons[0].sym, tmp2.r) + if field.loc.r == nil: internalError(e.info, "genObjConstr") if it.len == 3 and optFieldCheck in p.options: - genFieldCheck(p, it.sons[2], tmp2.r, field) + genFieldCheck(p, it.sons[2], r, field, ty) add(tmp2.r, ".") add(tmp2.r, field.loc.r) tmp2.k = locTemp @@ -1280,7 +1264,7 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r)) else: r = rfmt(p.module, "($1)", genOfHelper(p, dest, r)) - putIntoDest(p, d, getSysType(tyBool), r) + putIntoDest(p, d, getSysType(tyBool), r, a.s) proc genOf(p: BProc, n: PNode, d: var TLoc) = genOf(p, n.sons[1], n.sons[2].typ, d) @@ -1292,45 +1276,47 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = case t.kind of tyInt..tyInt64, tyUInt..tyUInt64: putIntoDest(p, d, e.typ, - ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)])) + ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)]), a.s) of tyFloat..tyFloat128: - putIntoDest(p, d, e.typ, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)])) + putIntoDest(p, d, e.typ, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)]), a.s) of tyBool: - putIntoDest(p, d, e.typ, ropecg(p.module, "#reprBool($1)", [rdLoc(a)])) + putIntoDest(p, d, e.typ, ropecg(p.module, "#reprBool($1)", [rdLoc(a)]), a.s) of tyChar: - putIntoDest(p, d, e.typ, ropecg(p.module, "#reprChar($1)", [rdLoc(a)])) + putIntoDest(p, d, e.typ, ropecg(p.module, "#reprChar($1)", [rdLoc(a)]), a.s) of tyEnum, tyOrdinal: putIntoDest(p, d, e.typ, ropecg(p.module, "#reprEnum($1, $2)", [ - rdLoc(a), genTypeInfo(p.module, t)])) + rdLoc(a), genTypeInfo(p.module, t)]), a.s) of tyString: - putIntoDest(p, d, e.typ, ropecg(p.module, "#reprStr($1)", [rdLoc(a)])) + putIntoDest(p, d, e.typ, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.s) of tySet: putIntoDest(p, d, e.typ, ropecg(p.module, "#reprSet($1, $2)", [ - addrLoc(a), genTypeInfo(p.module, t)])) + addrLoc(a), genTypeInfo(p.module, t)]), a.s) of tyOpenArray, tyVarargs: var b: TLoc case a.t.kind of tyOpenArray, tyVarargs: - putIntoDest(p, b, e.typ, "$1, $1Len0" % [rdLoc(a)]) + putIntoDest(p, b, e.typ, "$1, $1Len0" % [rdLoc(a)], a.s) of tyString, tySequence: putIntoDest(p, b, e.typ, - "$1->data, $1->$2" % [rdLoc(a), lenField(p)]) + "$1->data, $1->$2" % [rdLoc(a), lenField(p)], a.s) of tyArray, tyArrayConstr: putIntoDest(p, b, e.typ, - "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))]) + "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.s) else: internalError(e.sons[0].info, "genRepr()") putIntoDest(p, d, e.typ, ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b), - genTypeInfo(p.module, elemType(t))])) + genTypeInfo(p.module, elemType(t))]), a.s) of tyCString, tyArray, tyArrayConstr, tyRef, tyPtr, tyPointer, tyNil, tySequence: putIntoDest(p, d, e.typ, ropecg(p.module, "#reprAny($1, $2)", [ - rdLoc(a), genTypeInfo(p.module, t)])) + rdLoc(a), genTypeInfo(p.module, t)]), a.s) + of tyEmpty: + localError(e.info, "'repr' doesn't support 'void' type") else: putIntoDest(p, d, e.typ, ropecg(p.module, "#reprAny($1, $2)", - [addrLoc(a), genTypeInfo(p.module, t)])) + [addrLoc(a), genTypeInfo(p.module, t)]), a.s) gcUsage(e) proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) = @@ -1492,11 +1478,11 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of 1, 2, 4, 8: case op of mIncl: - var ts = "NI" & $(size * 8) + var ts = "NU" & $(size * 8) binaryStmtInExcl(p, e, d, "$1 |= ((" & ts & ")1)<<(($2)%(sizeof(" & ts & ")*8));$n") of mExcl: - var ts = "NI" & $(size * 8) + var ts = "NU" & $(size * 8) binaryStmtInExcl(p, e, d, "$1 &= ~(((" & ts & ")1) << (($2) % (sizeof(" & ts & ")*8)));$n") of mCard: @@ -1555,13 +1541,13 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) = let etyp = skipTypes(e.typ, abstractRange) if etyp.kind in ValueTypes and lfIndirect notin a.flags: putIntoDest(p, d, e.typ, "(*($1*) ($2))" % - [getTypeDesc(p.module, e.typ), addrLoc(a)]) + [getTypeDesc(p.module, e.typ), addrLoc(a)], a.s) elif etyp.kind == tyProc and etyp.callConv == ccClosure: putIntoDest(p, d, e.typ, "(($1) ($2))" % - [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)]) + [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.s) else: putIntoDest(p, d, e.typ, "(($1) ($2))" % - [getTypeDesc(p.module, e.typ), rdCharLoc(a)]) + [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.s) proc genCast(p: BProc, e: PNode, d: var TLoc) = const floatTypes = {tyFloat..tyFloat128} @@ -1581,7 +1567,7 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) = tmp.s = OnStack tmp.flags = {} expr(p, e.sons[1], tmp) - putIntoDest(p, d, e.typ, "LOC$#.dest" % [lbl]) + putIntoDest(p, d, e.typ, "LOC$#.dest" % [lbl], tmp.s) else: # I prefer the shorter cast version for pointer types -> generate less # C code; plus it's the right thing to do for closures: @@ -1591,16 +1577,17 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc, magic: string) = var a: TLoc var dest = skipTypes(n.typ, abstractVar) # range checks for unsigned turned out to be buggy and annoying: - if optRangeCheck notin p.options or dest.kind in {tyUInt..tyUInt64}: + if optRangeCheck notin p.options or dest.skipTypes({tyRange}).kind in + {tyUInt..tyUInt64}: initLocExpr(p, n.sons[0], a) putIntoDest(p, d, n.typ, "(($1) ($2))" % - [getTypeDesc(p.module, dest), rdCharLoc(a)]) + [getTypeDesc(p.module, dest), rdCharLoc(a)], a.s) else: initLocExpr(p, n.sons[0], a) putIntoDest(p, d, dest, ropecg(p.module, "(($1)#$5($2, $3, $4))", [ getTypeDesc(p.module, dest), rdCharLoc(a), genLiteral(p, n.sons[1], dest), genLiteral(p, n.sons[2], dest), - rope(magic)])) + rope(magic)]), a.s) proc genConv(p: BProc, e: PNode, d: var TLoc) = let destType = e.typ.skipTypes({tyVar, tyGenericInst}) @@ -1612,13 +1599,13 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) = proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = var a: TLoc initLocExpr(p, n.sons[0], a) - putIntoDest(p, d, skipTypes(n.typ, abstractVar), "$1->data" % [rdLoc(a)]) + putIntoDest(p, d, skipTypes(n.typ, abstractVar), "$1->data" % [rdLoc(a)], a.s) proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = var a: TLoc initLocExpr(p, n.sons[0], a) putIntoDest(p, d, skipTypes(n.typ, abstractVar), - ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)])) + ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]), a.s) gcUsage(n) proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = @@ -1661,7 +1648,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = case op of mOr, mAnd: genAndOr(p, e, d, op) of mNot..mToBiggestInt: unaryArith(p, e, d, op) - of mUnaryMinusI..mAbsI64: unaryArithOverflow(p, e, d, op) + of mUnaryMinusI..mAbsI: unaryArithOverflow(p, e, d, op) of mAddF64..mDivF64: binaryFloatArith(p, e, d, op) of mShrI..mXor: binaryArith(p, e, d, op) of mEqProc: genEqProc(p, e, d) @@ -1746,6 +1733,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mEcho: genEcho(p, e[1].skipConv) of mArrToSeq: genArrToSeq(p, e, d) of mNLen..mNError, mSlurp..mQuoteAst: + echo "from here ", p.prc.name.s, " ", p.prc.info + writestacktrace() localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s) of mSpawn: let n = lowerings.wrapProcForSpawn(p.module.module, e, e.typ, nil, nil) @@ -1768,7 +1757,7 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = var t = getUniqueType(n.typ) discard getTypeDesc(p.module, t) # so that any fields are initialized var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId) - fillLoc(d, locData, t, "TMP" & rope(id), OnHeap) + fillLoc(d, locData, t, "TMP" & rope(id), OnStatic) if id == gBackendId: # expression not found in the cache: inc(gBackendId) @@ -1807,7 +1796,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = [rdLoc(d), rdSetElemLoc(a, e.typ)]) else: # small set - var ts = "NI" & $(getSize(e.typ) * 8) + var ts = "NU" & $(getSize(e.typ) * 8) lineF(p, cpsStmts, "$1 = 0;$n", [rdLoc(d)]) for i in countup(0, sonsLen(e) - 1): if e.sons[i].kind == nkRange: @@ -1815,13 +1804,13 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, e.sons[i].sons[0], a) initLocExpr(p, e.sons[i].sons[1], b) lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" & - "$2 |=(1<<((" & ts & ")($1)%(sizeof(" & ts & ")*8)));$n", [ + "$2 |=((" & ts & ")(1)<<(($1)%(sizeof(" & ts & ")*8)));$n", [ rdLoc(idx), rdLoc(d), rdSetElemLoc(a, e.typ), rdSetElemLoc(b, e.typ)]) else: initLocExpr(p, e.sons[i], a) lineF(p, cpsStmts, - "$1 |=(1<<((" & ts & ")($2)%(sizeof(" & ts & ")*8)));$n", + "$1 |=((" & ts & ")(1)<<(($2)%(sizeof(" & ts & ")*8)));$n", [rdLoc(d), rdSetElemLoc(a, e.typ)]) proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = @@ -1854,7 +1843,7 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) = var tmp = "LOC" & rope(p.labels) addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", [getTypeDesc(p.module, n.typ), tmp, genConstExpr(p, n)]) - putIntoDest(p, d, n.typ, tmp) + putIntoDest(p, d, n.typ, tmp, OnStatic) else: var tmp, a, b: TLoc initLocExpr(p, n.sons[0], a) @@ -1908,10 +1897,10 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = r, genTypeInfo(p.module, dest)) if n.sons[0].typ.kind != tyObject: putIntoDest(p, d, n.typ, - "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)]) + "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.s) else: putIntoDest(p, d, n.typ, "(*($1*) ($2))" % - [getTypeDesc(p.module, dest), addrLoc(a)]) + [getTypeDesc(p.module, dest), addrLoc(a)], a.s) proc downConv(p: BProc, n: PNode, d: var TLoc) = if p.module.compileToCpp: @@ -1943,9 +1932,9 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) = linefmt(p, cpsStmts, "$1 = &$2;$n", rdLoc(d), r) else: r = "&" & r - putIntoDest(p, d, n.typ, r) + putIntoDest(p, d, n.typ, r, a.s) else: - putIntoDest(p, d, n.typ, r) + putIntoDest(p, d, n.typ, r, a.s) proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = var t = getUniqueType(n.typ) @@ -1960,7 +1949,7 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = [getTypeDesc(p.module, t), tmp, genConstExpr(p, n)]) if d.k == locNone: - fillLoc(d, locData, t, tmp, OnHeap) + fillLoc(d, locData, t, tmp, OnStatic) else: putDataIntoDest(p, d, t, tmp) @@ -1978,6 +1967,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = genProc(p.module, sym) putLocIntoDest(p, d, sym.loc) of skProc, skConverter, skIterators: + if sfCompileTime in sym.flags: + localError(n.info, "request to generate code for .compileTime proc: " & + sym.name.s) genProc(p.module, sym) if sym.loc.r == nil or sym.loc.t == nil: internalError(n.info, "expr: proc not init " & sym.name.s) @@ -1987,7 +1979,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = if sfGlobal in sym.flags: genVarPrototype(p.module, sym) putLocIntoDest(p, d, sym.loc) elif isSimpleConst(sym.typ): - putIntoDest(p, d, n.typ, genLiteral(p, sym.ast, sym.typ)) + putIntoDest(p, d, n.typ, genLiteral(p, sym.ast, sym.typ), OnStatic) else: genComplexConst(p, sym, d) of skEnumField: @@ -2074,6 +2066,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkStmtList: for i in countup(0, sonsLen(n) - 1): genStmts(p, n.sons[i]) of nkIfExpr, nkIfStmt: genIf(p, n, d) + of nkWhen: + # This should be a "when nimvm" node. + expr(p, n.sons[1].sons[0], d) of nkObjDownConv: downConv(p, n, d) of nkObjUpConv: upConv(p, n, d) of nkChckRangeF: genRangeChck(p, n, d, "chckRangeF") @@ -2128,7 +2123,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = # due to a bug/limitation in the lambda lifting, unused inner procs # are not transformed correctly. We work around this issue (#411) here # by ensuring it's no inner proc (owner is a module): - if prc.skipGenericOwner.kind == skModule: + if prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags: if (optDeadCodeElim notin gGlobalOptions and sfDeadCodeElim notin getModule(prc).flags) or ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or @@ -2150,7 +2145,7 @@ proc genNamedConstExpr(p: BProc, n: PNode): Rope = proc genConstSimpleList(p: BProc, n: PNode): Rope = var length = sonsLen(n) result = rope("{") - for i in countup(0, length - 2): + for i in countup(ord(n.kind == nkObjConstr), length - 2): addf(result, "$1,$n", [genNamedConstExpr(p, n.sons[i])]) if length > 0: add(result, genNamedConstExpr(p, n.sons[length - 1])) addf(result, "}$n", []) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 6d29b1684..f4a7c4400 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -102,7 +102,7 @@ proc assignLabel(b: var TBlock): Rope {.inline.} = proc blockBody(b: var TBlock): Rope = result = b.sections[cpsLocals] if b.frameLen > 0: - result.addf("F.len+=$1;$n", [b.frameLen.rope]) + result.addf("FR.len+=$1;$n", [b.frameLen.rope]) result.add(b.sections[cpsInit]) result.add(b.sections[cpsStmts]) @@ -123,7 +123,7 @@ proc endBlock(p: BProc) = ~"}$n" let frameLen = p.blocks[topBlock].frameLen if frameLen > 0: - blockEnd.addf("F.len-=$1;$n", [frameLen.rope]) + blockEnd.addf("FR.len-=$1;$n", [frameLen.rope]) endBlock(p, blockEnd) proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} = @@ -1002,8 +1002,10 @@ proc genAsmStmt(p: BProc, t: PNode) = proc determineSection(n: PNode): TCFileSection = result = cfsProcHeaders if n.len >= 1 and n.sons[0].kind in {nkStrLit..nkTripleStrLit}: - if n.sons[0].strVal.startsWith("/*TYPESECTION*/"): result = cfsTypes - elif n.sons[0].strVal.startsWith("/*VARSECTION*/"): result = cfsVars + let sec = n.sons[0].strVal + if sec.startsWith("/*TYPESECTION*/"): result = cfsTypes + elif sec.startsWith("/*VARSECTION*/"): result = cfsVars + elif sec.startsWith("/*INCLUDESECTION*/"): result = cfsHeaders proc genEmit(p: BProc, t: PNode) = var s = genAsmOrEmitStmt(p, t.sons[1]) @@ -1099,7 +1101,10 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = genGotoVar(p, e.sons[1]) elif not fieldDiscriminantCheckNeeded(p, e): var a: TLoc - initLocExpr(p, e.sons[0], a) + if e[0].kind in {nkDerefExpr, nkHiddenDeref}: + genDeref(p, e[0], a, enforceDeref=true) + else: + initLocExpr(p, e.sons[0], a) if fastAsgn: incl(a.flags, lfNoDeepCopy) assert(a.t != nil) loadInto(p, e.sons[0], e.sons[1], a) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 3742fd2fd..1ed9ce113 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -323,7 +323,8 @@ proc paramStorageLoc(param: PSym): TStorageLoc = result = OnUnknown proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, - check: var IntSet, declareEnvironment=true) = + check: var IntSet, declareEnvironment=true; + weakDep=false) = params = nil if (t.sons[0] == nil) or isInvalidReturnType(t.sons[0]): rettype = ~"void" @@ -341,6 +342,8 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, add(params, ~"*") incl(param.loc.flags, lfIndirect) param.loc.s = OnUnknown + elif weakDep: + add(params, getTypeDescWeak(m, param.typ, check)) else: add(params, getTypeDescAux(m, param.typ, check)) add(params, ~" ") @@ -438,6 +441,8 @@ proc genRecordFieldsAux(m: BModule, n: PNode, elif fieldType.kind == tySequence: # we need to use a weak dependency here for trecursive_table. addf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname]) + elif field.bitsize != 0: + addf(result, "$1 $2:$3;$n", [getTypeDescAux(m, field.loc.t, check), sname, rope($field.bitsize)]) else: # don't use fieldType here because we need the # tyGenericInst for C++ template support @@ -577,7 +582,7 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope = result = getTypeName(t) idTablePut(m.typeCache, t, result) var rettype, desc: Rope - genProcParams(m, t, rettype, desc, check) + genProcParams(m, t, rettype, desc, check, true, true) if not isImportedType(t): if t.callConv != ccClosure: # procedure vars may need a closure! addf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n", diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index 4ba6643ec..6dfd7b52c 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -99,7 +99,10 @@ proc getUniqueType*(key: PType): PType = gCanonicalTypes[k] = key result = key of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr, tyFieldAccessor: - internalError("getUniqueType") + if key.sym != nil: + internalError(key.sym.info, "metatype not eliminated") + else: + internalError("metatype not eliminated") of tyDistinct: if key.deepCopy != nil: result = key else: result = getUniqueType(lastSon(key)) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index da9c6f653..f63134b66 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -10,12 +10,11 @@ ## This module implements the C code generator. import - ast, astalgo, hashes, trees, platform, magicsys, extccomp, - options, intsets, - nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os, - ropes, math, passes, rodread, wordrecg, treetab, cgmeth, condsyms, - rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, lowerings, - semparallel + ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets, + nversion, nimsets, msgs, securehash, bitsets, idents, lists, types, + ccgutils, os, ropes, math, passes, rodread, wordrecg, treetab, cgmeth, + condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, + lowerings, semparallel import strutils except `%` # collides with ropes.`%` @@ -376,7 +375,7 @@ proc localDebugInfo(p: BProc, s: PSym) = var a = "&" & s.loc.r if s.kind == skParam and ccgIntroducedPtr(s): a = s.loc.r lineF(p, cpsInit, - "F.s[$1].address = (void*)$3; F.s[$1].typ = $4; F.s[$1].name = $2;$n", + "FR.s[$1].address = (void*)$3; FR.s[$1].typ = $4; FR.s[$1].name = $2;$n", [p.maxFrameLen.rope, makeCString(normalize(s.name.s)), a, genTypeInfo(p.module, s.loc.t)]) inc(p.maxFrameLen) @@ -506,8 +505,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) = if lib.path.kind in {nkStrLit..nkTripleStrLit}: var s: TStringSeq = @[] libCandidates(lib.path.strVal, s) - if gVerbosity >= 2: - msgWriteln("Dependency: " & lib.path.strVal) + rawMessage(hintDependency, lib.path.strVal) var loadlib: Rope = nil for i in countup(0, high(s)): inc(m.labels) @@ -599,7 +597,7 @@ proc cgsym(m: BModule, name: string): Rope = of skProc, skMethod, skConverter, skIterators: genProc(m, sym) of skVar, skResult, skLet: genVarPrototype(m, sym) of skType: discard getTypeDesc(m, sym.typ) - else: internalError("cgsym: " & name) + else: internalError("cgsym: " & name & ": " & $sym.kind) else: # we used to exclude the system module from this check, but for DLL # generation support this sloppyness leads to hard to detect bugs, so @@ -611,10 +609,12 @@ proc generateHeaders(m: BModule) = add(m.s[cfsHeaders], tnl & "#include \"nimbase.h\"" & tnl) var it = PStrEntry(m.headerFiles.head) while it != nil: - if it.data[0] notin {'\"', '<'}: - addf(m.s[cfsHeaders], "$N#include \"$1\"$N", [rope(it.data)]) + if it.data[0] == '#': + add(m.s[cfsHeaders], rope(it.data.replace('`', '"') & tnl)) + elif it.data[0] notin {'\"', '<'}: + addf(m.s[cfsHeaders], "#include \"$1\"$N", [rope(it.data)]) else: - addf(m.s[cfsHeaders], "$N#include $1$N", [rope(it.data)]) + addf(m.s[cfsHeaders], "#include $1$N", [rope(it.data)]) it = PStrEntry(it.next) proc retIsNotVoid(s: PSym): bool = @@ -623,7 +623,7 @@ proc retIsNotVoid(s: PSym): bool = proc initFrame(p: BProc, procname, filename: Rope): Rope = discard cgsym(p.module, "nimFrame") if p.maxFrameLen > 0: - discard cgsym(p.module, "TVarSlot") + discard cgsym(p.module, "VarSlot") result = rfmt(nil, "\tnimfrs($1, $2, $3, $4)$N", procname, filename, p.maxFrameLen.rope, p.blocks[0].frameLen.rope) @@ -676,8 +676,11 @@ proc genProcAux(m: BModule, prc: PSym) = closureSetup(p, prc) genStmts(p, prc.getBody) # modifies p.locals, p.init, etc. var generatedProc: Rope + if sfNoReturn in prc.flags: + if hasDeclspec in extccomp.CC[extccomp.cCompiler].props: + header = "__declspec(noreturn) " & header if sfPure in prc.flags: - if hasNakedDeclspec in extccomp.CC[extccomp.cCompiler].props: + if hasDeclspec in extccomp.CC[extccomp.cCompiler].props: header = "__declspec(naked) " & header generatedProc = rfmt(nil, "$N$1 {$n$2$3$4}$N$N", header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)) @@ -718,10 +721,14 @@ proc genProcPrototype(m: BModule, sym: PSym) = getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym))) elif not containsOrIncl(m.declaredProtos, sym.id): var header = genProcHeader(m, sym) + if sfNoReturn in sym.flags and hasDeclspec in extccomp.CC[cCompiler].props: + header = "__declspec(noreturn) " & header if sym.typ.callConv != ccInline and crossesCppBoundary(m, sym): header = "extern \"C\" " & header - if sfPure in sym.flags and hasNakedAttribute in CC[cCompiler].props: + if sfPure in sym.flags and hasAttribute in CC[cCompiler].props: header.add(" __attribute__((naked))") + if sfNoReturn in sym.flags and hasAttribute in CC[cCompiler].props: + header.add(" __attribute__((noreturn))") add(m.s[cfsProcHeaders], rfmt(nil, "$1;$n", header)) proc genProcNoForward(m: BModule, prc: PSym) = @@ -753,7 +760,7 @@ proc requestConstImpl(p: BProc, sym: PSym) = var m = p.module useHeader(m, sym) if sym.loc.k == locNone: - fillLoc(sym.loc, locData, sym.typ, mangleName(sym), OnUnknown) + fillLoc(sym.loc, locData, sym.typ, mangleName(sym), OnStatic) if lfNoDecl in sym.loc.flags: return # declare implementation: var q = findPendingModule(m, sym) @@ -808,7 +815,7 @@ proc genVarPrototype(m: BModule, sym: PSym) = genVarPrototypeAux(m, sym) proc addIntTypes(result: var Rope) {.inline.} = - addf(result, "#define NIM_INTBITS $1", [ + addf(result, "#define NIM_INTBITS $1" & tnl, [ platform.CPU[targetCPU].intSize.rope]) proc getCopyright(cfile: string): Rope = @@ -846,14 +853,14 @@ proc genMainProc(m: BModule) = # functions, which might otherwise merge their stack frames. PreMainBody = "void PreMainInner() {$N" & - "\tsystemInit();$N" & + "\tsystemInit000();$N" & "$1" & "$2" & "$3" & "}$N$N" & "void PreMain() {$N" & "\tvoid (*volatile inner)();$N" & - "\tsystemDatInit();$N" & + "\tsystemDatInit000();$N" & "\tinner = PreMainInner;$N" & "$4$5" & "\t(*inner)();$N" & @@ -943,7 +950,7 @@ proc genMainProc(m: BModule) = gBreakpoints.add(m.genFilenames) let initStackBottomCall = - if platform.targetOS == osStandalone: "".rope + if platform.targetOS == osStandalone or gSelectedGC == gcNone: "".rope else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N") inc(m.labels) appcg(m, m.s[cfsProcs], PreMainBody, [ @@ -967,8 +974,8 @@ proc getSomeInitName(m: PSym, suffix: string): Rope = result.add m.name.s result.add suffix -proc getInitName(m: PSym): Rope = getSomeInitName(m, "Init") -proc getDatInitName(m: PSym): Rope = getSomeInitName(m, "DatInit") +proc getInitName(m: PSym): Rope = getSomeInitName(m, "Init000") +proc getDatInitName(m: PSym): Rope = getSomeInitName(m, "DatInit000") proc registerModuleToMain(m: PSym) = var @@ -1010,7 +1017,7 @@ proc genInitCode(m: BModule) = var procname = makeCString(m.module.name.s) add(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename)) else: - add(prc, ~"\tTFrame F; F.len = 0;$N") + add(prc, ~"\tTFrame F; FR.len = 0;$N") add(prc, genSectionStart(cpsInit)) add(prc, m.preInitProc.s(cpsInit)) @@ -1103,7 +1110,7 @@ proc rawNewModule(module: PSym, filename: string): BModule = proc nullify[T](arr: var T) = for i in low(arr)..high(arr): - arr[i] = nil + arr[i] = Rope(nil) proc resetModule*(m: BModule) = # between two compilations in CAAS mode, we can throw @@ -1322,4 +1329,3 @@ proc cgenWriteModules* = if generatedHeader != nil: writeHeader(generatedHeader) const cgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose) - diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index 6c997b983..3c2c51b76 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -9,33 +9,33 @@ ## This module implements code generation for multi methods. -import +import intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys, sempass2, strutils -proc genConv(n: PNode, d: PType, downcast: bool): PNode = +proc genConv(n: PNode, d: PType, downcast: bool): PNode = var dest = skipTypes(d, abstractPtrs) var source = skipTypes(n.typ, abstractPtrs) - if (source.kind == tyObject) and (dest.kind == tyObject): + if (source.kind == tyObject) and (dest.kind == tyObject): var diff = inheritanceDiff(dest, source) if diff == high(int): internalError(n.info, "cgmeth.genConv") - if diff < 0: + if diff < 0: result = newNodeIT(nkObjUpConv, n.info, d) addSon(result, n) if downcast: internalError(n.info, "cgmeth.genConv: no upcast allowed") - elif diff > 0: + elif diff > 0: result = newNodeIT(nkObjDownConv, n.info, d) addSon(result, n) - if not downcast: + if not downcast: internalError(n.info, "cgmeth.genConv: no downcast allowed") - else: + else: result = n - else: + else: result = n - -proc methodCall*(n: PNode): PNode = + +proc methodCall*(n: PNode): PNode = result = n - # replace ordinary method by dispatcher method: + # replace ordinary method by dispatcher method: var disp = lastSon(result.sons[0].sym.ast).sym assert sfDispatcher in disp.flags result.sons[0].sym = disp @@ -47,30 +47,35 @@ proc methodCall*(n: PNode): PNode = var gMethods: seq[tuple[methods: TSymSeq, dispatcher: PSym]] = @[] -proc sameMethodBucket(a, b: PSym): bool = - result = false - if a.name.id != b.name.id: return - if sonsLen(a.typ) != sonsLen(b.typ): +type + MethodResult = enum No, Invalid, Yes + +proc sameMethodBucket(a, b: PSym): MethodResult = + if a.name.id != b.name.id: return + if sonsLen(a.typ) != sonsLen(b.typ): return # check for return type: - if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]): return - for i in countup(1, sonsLen(a.typ) - 1): + if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]): return + for i in countup(1, sonsLen(a.typ) - 1): var aa = a.typ.sons[i] var bb = b.typ.sons[i] - while true: + while true: aa = skipTypes(aa, {tyGenericInst}) bb = skipTypes(bb, {tyGenericInst}) - if (aa.kind == bb.kind) and (aa.kind in {tyVar, tyPtr, tyRef}): + if (aa.kind == bb.kind) and (aa.kind in {tyVar, tyPtr, tyRef}): aa = aa.lastSon bb = bb.lastSon - else: - break - if sameType(aa, bb) or - (aa.kind == tyObject) and (bb.kind == tyObject) and - (inheritanceDiff(bb, aa) < 0): - discard + else: + break + if sameType(aa, bb): + if aa.kind == tyObject and result != Invalid: result = Yes + elif aa.kind == tyObject and bb.kind == tyObject: + let diff = inheritanceDiff(bb, aa) + if diff < 0: + if result != Invalid: result = Yes + elif diff != high(int): + result = Invalid else: - return - result = true + return No proc attachDispatcher(s: PSym, dispatcher: PNode) = var L = s.ast.len-1 @@ -133,18 +138,31 @@ proc fixupDispatcher(meth, disp: PSym) = proc methodDef*(s: PSym, fromCache: bool) = var L = len(gMethods) + var witness: PSym for i in countup(0, L - 1): var disp = gMethods[i].dispatcher - if sameMethodBucket(disp, s): + case sameMethodBucket(disp, s) + of Yes: add(gMethods[i].methods, s) attachDispatcher(s, lastSon(disp.ast)) fixupDispatcher(s, disp) when useEffectSystem: checkMethodEffects(disp, s) - return + if sfBase in s.flags and gMethods[i].methods[0] != s: + # already exists due to forwarding definition? + localError(s.info, "method is not a base") + return + of No: discard + of Invalid: + if witness.isNil: witness = gMethods[i].methods[0] # create a new dispatcher: add(gMethods, (methods: @[s], dispatcher: createDispatcher(s))) if fromCache: internalError(s.info, "no method dispatcher found") + if witness != nil: + localError(s.info, "invalid declaration order; cannot attach '" & s.name.s & + "' to method defined here: " & $witness.info) + elif sfBase notin s.flags: + message(s.info, warnUseBase) proc relevantCol(methods: TSymSeq, col: int): bool = # returns true iff the position is relevant @@ -154,35 +172,35 @@ proc relevantCol(methods: TSymSeq, col: int): bool = let t2 = skipTypes(methods[i].typ.sons[col], skipPtrs) if not sameType(t2, t): return true - -proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int = - for col in countup(1, sonsLen(a.typ) - 1): - if contains(relevantCols, col): + +proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int = + for col in countup(1, sonsLen(a.typ) - 1): + if contains(relevantCols, col): var aa = skipTypes(a.typ.sons[col], skipPtrs) var bb = skipTypes(b.typ.sons[col], skipPtrs) var d = inheritanceDiff(aa, bb) - if (d != high(int)): + if (d != high(int)): return d - -proc sortBucket(a: var TSymSeq, relevantCols: IntSet) = + +proc sortBucket(a: var TSymSeq, relevantCols: IntSet) = # we use shellsort here; fast and simple var n = len(a) var h = 1 - while true: + while true: h = 3 * h + 1 - if h > n: break - while true: + if h > n: break + while true: h = h div 3 - for i in countup(h, n - 1): + for i in countup(h, n - 1): var v = a[i] var j = i - while cmpSignatures(a[j - h], v, relevantCols) >= 0: + while cmpSignatures(a[j - h], v, relevantCols) >= 0: a[j] = a[j - h] j = j - h - if j < h: break + if j < h: break a[j] = v - if h == 1: break - + if h == 1: break + proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym = var base = lastSon(methods[0].ast).sym result = base @@ -199,7 +217,7 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym = addSon(isn, newSymNode(iss)) addSon(isn, newSymNode(base.typ.n.sons[col].sym)) addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col])) - if cond != nil: + if cond != nil: var a = newNodeIT(nkCall, base.info, getSysType(tyBool)) addSon(a, newSymNode(ands)) addSon(a, cond) @@ -209,12 +227,12 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym = cond = isn var call = newNodeI(nkCall, base.info) addSon(call, newSymNode(curr)) - for col in countup(1, paramLen - 1): - addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym), + for col in countup(1, paramLen - 1): + addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym), curr.typ.sons[col], false)) var ret: PNode if base.typ.sons[0] != nil: - var a = newNodeI(nkAsgn, base.info) + var a = newNodeI(nkFastAsgn, base.info) addSon(a, newSymNode(base.ast.sons[resultPos].sym)) addSon(a, call) ret = newNodeI(nkReturnStmt, base.info) @@ -230,13 +248,12 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym = disp = ret result.ast.sons[bodyPos] = disp -proc generateMethodDispatchers*(): PNode = +proc generateMethodDispatchers*(): PNode = result = newNode(nkStmtList) - for bucket in countup(0, len(gMethods) - 1): + for bucket in countup(0, len(gMethods) - 1): var relevantCols = initIntSet() - for col in countup(1, sonsLen(gMethods[bucket].methods[0].typ) - 1): + for col in countup(1, sonsLen(gMethods[bucket].methods[0].typ) - 1): if relevantCol(gMethods[bucket].methods, col): incl(relevantCols, col) sortBucket(gMethods[bucket].methods, relevantCols) addSon(result, newSymNode(genDispatcher(gMethods[bucket].methods, relevantCols))) - diff --git a/compiler/commands.nim b/compiler/commands.nim index b6ebb6bcb..b38e52bc5 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -17,11 +17,12 @@ template bootSwitch(name, expr, userString: expr): expr = const name = if expr: " " & userString else: "" bootSwitch(usedRelease, defined(release), "-d:release") -bootSwitch(usedGnuReadline, defined(useGnuReadline), "-d:useGnuReadline") +bootSwitch(usedGnuReadline, defined(useLinenoise), "-d:useLinenoise") bootSwitch(usedNoCaas, defined(noCaas), "-d:noCaas") bootSwitch(usedBoehm, defined(boehmgc), "--gc:boehm") bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep") bootSwitch(usedGenerational, defined(gcgenerational), "--gc:generational") +bootSwitch(usedGoGC, defined(gogc), "--gc:go") bootSwitch(usedNoGC, defined(nogc), "--gc:none") import @@ -64,14 +65,15 @@ proc getCommandLineDesc(): string = proc helpOnError(pass: TCmdLinePass) = if pass == passCmd1: - msgWriteln(getCommandLineDesc()) + msgWriteln(getCommandLineDesc(), {msgStdout}) msgQuit(0) proc writeAdvancedUsage(pass: TCmdLinePass) = if pass == passCmd1: msgWriteln(`%`(HelpMessage, [VersionAsString, platform.OS[platform.hostOS].name, - CPU[platform.hostCPU].name]) & AdvancedUsage) + CPU[platform.hostCPU].name]) & AdvancedUsage, + {msgStdout}) msgQuit(0) proc writeVersionInfo(pass: TCmdLinePass) = @@ -86,7 +88,7 @@ proc writeVersionInfo(pass: TCmdLinePass) = msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine & usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas & - usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedNoGC) + usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedGoGC & usedNoGC) msgQuit(0) var @@ -94,7 +96,7 @@ var proc writeCommandLineUsage() = if not helpWritten: - msgWriteln(getCommandLineDesc()) + msgWriteln(getCommandLineDesc(), {msgStdout}) helpWritten = true proc addPrefix(switch: string): string = @@ -127,6 +129,18 @@ proc processOnOffSwitch(op: TOptions, arg: string, pass: TCmdLinePass, of wOff: gOptions = gOptions - op else: localError(info, errOnOrOffExpectedButXFound, arg) +proc processOnOffSwitchOrList(op: TOptions, arg: string, pass: TCmdLinePass, + info: TLineInfo): bool = + result = false + case whichKeyword(arg) + of wOn: gOptions = gOptions + op + of wOff: gOptions = gOptions - op + else: + if arg == "list": + result = true + else: + localError(info, errOnOffOrListExpectedButXFound, arg) + proc processOnOffSwitchG(op: TGlobalOptions, arg: string, pass: TCmdLinePass, info: TLineInfo) = case whichKeyword(arg) @@ -140,6 +154,10 @@ proc expectArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = proc expectNoArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = if arg != "": localError(info, errCmdLineNoArgExpected, addPrefix(switch)) +var + enableNotes: TNoteKinds + disableNotes: TNoteKinds + proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass, info: TLineInfo; orig: string) = var id = "" # arg = "X]:on|off" @@ -161,8 +179,12 @@ proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass, if x >= 0: n = TNoteKind(x + ord(warnMin)) else: localError(info, "unknown warning: " & id) case whichKeyword(substr(arg, i)) - of wOn: incl(gNotes, n) - of wOff: excl(gNotes, n) + of wOn: + incl(gNotes, n) + incl(enableNotes, n) + of wOff: + excl(gNotes, n) + incl(disableNotes, n) else: localError(info, errOnOrOffExpectedButXFound, arg) proc processCompile(filename: string) = @@ -181,6 +203,7 @@ proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool = of "v2": result = gSelectedGC == gcV2 of "markandsweep": result = gSelectedGC == gcMarkAndSweep of "generational": result = gSelectedGC == gcGenerational + of "go": result = gSelectedGC == gcGo of "none": result = gSelectedGC == gcNone else: localError(info, errNoneBoehmRefcExpectedButXFound, arg) of "opt": @@ -229,7 +252,8 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool = of "experimental": result = gExperimentalMode else: invalidCmdLineOption(passCmd1, switch, info) -proc processPath(path: string, notRelativeToProj = false): string = +proc processPath(path: string, notRelativeToProj = false, + cfginfo = unknownLineInfo()): string = let p = if notRelativeToProj or os.isAbsolute(path) or '$' in path or path[0] == '.': path @@ -239,6 +263,7 @@ proc processPath(path: string, notRelativeToProj = false): string = "nim", getPrefixDir(), "lib", libpath, "home", removeTrailingDirSep(os.getHomeDir()), + "config", cfginfo.toFullPath().splitFile().dir, "projectname", options.gProjectName, "projectpath", options.gProjectPath]) @@ -281,7 +306,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = case switch.normalize of "path", "p": expectArg(switch, arg, pass, info) - addPath(processPath(arg), info) + addPath(processPath(arg, cfginfo=info), info) of "nimblepath", "babelpath": # keep the old name for compat if pass in {passCmd2, passPP} and not options.gNoNimblePath: @@ -363,14 +388,19 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = of "generational": gSelectedGC = gcGenerational defineSymbol("gcgenerational") + of "go": + gSelectedGC = gcGo + defineSymbol("gogc") of "none": gSelectedGC = gcNone defineSymbol("nogc") else: localError(info, errNoneBoehmRefcExpectedButXFound, arg) - of "warnings", "w": processOnOffSwitch({optWarns}, arg, pass, info) + of "warnings", "w": + if processOnOffSwitchOrList({optWarns}, arg, pass, info): listWarnings() of "warning": processSpecificNote(arg, wWarning, pass, info, switch) of "hint": processSpecificNote(arg, wHint, pass, info, switch) - of "hints": processOnOffSwitch({optHints}, arg, pass, info) + of "hints": + if processOnOffSwitchOrList({optHints}, arg, pass, info): listHints() of "threadanalysis": processOnOffSwitchG({optThreadAnalysis}, arg, pass, info) of "stacktrace": processOnOffSwitch({optStackTrace}, arg, pass, info) of "linetrace": processOnOffSwitch({optLineTrace}, arg, pass, info) @@ -405,6 +435,8 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = of "linedir": processOnOffSwitch({optLineDir}, arg, pass, info) of "assertions", "a": processOnOffSwitch({optAssert}, arg, pass, info) of "deadcodeelim": processOnOffSwitchG({optDeadCodeElim}, arg, pass, info) + of "reportconceptfailures": + processOnOffSwitchG({optReportConceptFailures}, arg, pass, info) of "threads": processOnOffSwitchG({optThreads}, arg, pass, info) #if optThreads in gGlobalOptions: incl(gNotes, warnGcUnsafe) @@ -501,6 +533,9 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = of "verbosity": expectArg(switch, arg, pass, info) gVerbosity = parseInt(arg) + gNotes = NotesVerbosity[gVerbosity] + incl(gNotes, enableNotes) + excl(gNotes, disableNotes) of "parallelbuild": expectArg(switch, arg, pass, info) gNumberOfProcessors = parseInt(arg) @@ -530,6 +565,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = of "genscript": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optGenScript) + of "colors": processOnOffSwitchG({optUseColors}, arg, pass, info) of "lib": expectArg(switch, arg, pass, info) libpath = processPath(arg, notRelativeToProj=true) @@ -579,6 +615,10 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = of "experimental": expectNoArg(switch, arg, pass, info) gExperimentalMode = true + of "assembler": + cAssembler = nameToCC(arg) + if cAssembler notin cValidAssemblers: + localError(info, errGenerated, "'$1' is not a valid assembler." % [arg]) else: if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg) else: invalidCmdLineOption(pass, switch, info) @@ -608,11 +648,18 @@ proc processSwitch*(pass: TCmdLinePass; p: OptParser) = proc processArgument*(pass: TCmdLinePass; p: OptParser; argsCount: var int): bool = if argsCount == 0: - options.command = p.key + # nim filename.nims is the same as "nim e filename.nims": + if p.key.endswith(".nims"): + options.command = "e" + options.gProjectName = unixToNativePath(p.key) + arguments = cmdLineRest(p) + result = true + elif pass != passCmd2: + options.command = p.key else: if pass == passCmd1: options.commandArgs.add p.key if argsCount == 1: - # support UNIX style filenames anywhere for portable build scripts: + # support UNIX style filenames everywhere for portable build scripts: options.gProjectName = unixToNativePath(p.key) arguments = cmdLineRest(p) result = true diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index ad7d80c85..60e8f2826 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -88,3 +88,8 @@ proc initDefines*() = defineSymbol("nimalias") defineSymbol("nimlocks") defineSymbol("nimnode") + defineSymbol("nimnomagic64") + defineSymbol("nimvarargstyped") + defineSymbol("nimtypedescfixed") + defineSymbol("nimKnowsNimvm") + defineSymbol("nimArrIdx") diff --git a/compiler/crc.nim b/compiler/crc.nim deleted file mode 100644 index a8b61f2a5..000000000 --- a/compiler/crc.nim +++ /dev/null @@ -1,147 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -import - strutils - -type - TCrc32* = int32 - -const - InitCrc32* = TCrc32(- 1) - InitAdler32* = int32(1) - -proc updateCrc32*(val: int8, crc: TCrc32): TCrc32 {.inline.} -proc updateCrc32*(val: char, crc: TCrc32): TCrc32 {.inline.} -proc crcFromBuf*(buf: pointer, length: int): TCrc32 -proc strCrc32*(s: string): TCrc32 -proc crcFromFile*(filename: string): TCrc32 -proc updateAdler32*(adler: int32, buf: pointer, length: int): int32 -# implementation - -type - TCRC_TabEntry = int - -const - crc32table: array[0..255, TCRC_TabEntry] = [0, 1996959894, - 301047508, - - 1727442502, 124634137, 1886057615, - 379345611, - 1637575261, 249268274, - 2044508324, - 522852066, - 1747789432, 162941995, 2125561021, - 407360249, - - 1866523247, 498536548, 1789927666, - 205950648, - 2067906082, 450548861, - 1843258603, - 187386543, - 2083289657, 325883990, 1684777152, - 43845254, - - 1973040660, 335633487, 1661365465, - 99664541, - 1928851979, 997073096, - 1281953886, - 715111964, - 1570279054, 1006888145, 1258607687, - 770865667, - - 1526024853, 901097722, 1119000684, - 608450090, - 1396901568, 853044451, - 1172266101, - 589951537, - 1412350631, 651767980, 1373503546, - 925412992, - - 1076862698, 565507253, 1454621731, - 809855591, - 1195530993, 671266974, - 1594198024, - 972236366, - 1324619484, 795835527, 1483230225, - 1050600021, - - 1234817731, 1994146192, 31158534, - 1731059524, - 271249366, 1907459465, - 112637215, - 1614814043, - 390540237, 2013776290, 251722036, - 1777751922, - - 519137256, 2137656763, 141376813, - 1855689577, - 429695999, 1802195444, - 476864866, - 2056965928, - 228458418, 1812370925, 453092731, - 2113342271, - - 183516073, 1706088902, 314042704, - 1950435094, - 54949764, 1658658271, - 366619977, - 1932296973, - 69972891, 1303535960, 984961486, - 1547960204, - - 725929758, 1256170817, 1037604311, - 1529756563, - 740887301, 1131014506, - 879679996, - 1385723834, - 631195440, 1141124467, 855842277, - 1442165665, - - 586318647, 1342533948, 654459306, - 1106571248, - 921952122, 1466479909, - 544179635, - 1184443383, - 832445281, 1591671054, 702138776, - 1328506846, - - 942167884, 1504918807, 783551873, - 1212326853, - 1061524307, - 306674912, - - 1698712650, 62317068, 1957810842, - 355121351, - 1647151185, 81470997, - 1943803523, - 480048366, - 1805370492, 225274430, 2053790376, - 468791541, - - 1828061283, 167816743, 2097651377, - 267414716, - 2029476910, 503444072, - 1762050814, - 144550051, - 2140837941, 426522225, 1852507879, - 19653770, - - 1982649376, 282753626, 1742555852, - 105259153, - 1900089351, 397917763, - 1622183637, - 690576408, - 1580100738, 953729732, 1340076626, - 776247311, - - 1497606297, 1068828381, 1219638859, - 670225446, - 1358292148, 906185462, - 1090812512, - 547295293, - 1469587627, 829329135, 1181335161, - 882789492, - - 1134132454, 628085408, 1382605366, - 871598187, - 1156888829, 570562233, - 1426400815, - 977650754, - 1296233688, 733239954, 1555261956, - 1026031705, - - 1244606671, 752459403, 1541320221, - 1687895376, - 328994266, 1969922972, - 40735498, - 1677130071, - 351390145, 1913087877, 83908371, - 1782625662, - - 491226604, 2075208622, 213261112, - 1831694693, - 438977011, 2094854071, - 198958881, - 2032938284, - 237706686, 1759359992, 534414190, - 2118248755, - - 155638181, 1873836001, 414664567, - 2012718362, - 15766928, 1711684554, - 285281116, - 1889165569, - 127750551, 1634467795, 376229701, - 1609899400, - - 686959890, 1308918612, 956543938, - 1486412191, - 799009033, 1231636301, - 1047427035, - 1362007478, - 640263460, 1088359270, 936918000, - 1447252397, - - 558129467, 1202900863, 817233897, - 1111625188, - 893730166, 1404277552, - 615818150, - 1160759803, - 841546093, 1423857449, 601450431, - 1285129682, - - 1000256840, 1567103746, 711928724, - 1274298825, - 1022587231, 1510334235, - 755167117] - -proc updateCrc32(val: int8, crc: TCrc32): TCrc32 = - result = TCrc32(crc32table[(int(crc) xor (int(val) and 0x000000FF)) and - 0x000000FF]) xor (crc shr TCrc32(8)) - -proc updateCrc32(val: char, crc: TCrc32): TCrc32 = - result = updateCrc32(toU8(ord(val)), crc) - -proc strCrc32(s: string): TCrc32 = - result = InitCrc32 - for i in countup(0, len(s) - 1): result = updateCrc32(s[i], result) - -proc `><`*(c: TCrc32, s: string): TCrc32 = - result = c - for i in 0..len(s)-1: result = updateCrc32(s[i], result) - -type - TByteArray = array[0..10000000, int8] - PByteArray = ref TByteArray - -proc crcFromBuf(buf: pointer, length: int): TCrc32 = - var p = cast[PByteArray](buf) - result = InitCrc32 - for i in countup(0, length - 1): result = updateCrc32(p[i], result) - -proc crcFromFile(filename: string): TCrc32 = - const - bufSize = 8000 # don't use 8K for the memory allocator! - var - bin: File - result = InitCrc32 - if not open(bin, filename): - return # not equal if file does not exist - var buf = alloc(bufSize) - var p = cast[PByteArray](buf) - while true: - var readBytes = readBuffer(bin, buf, bufSize) - for i in countup(0, readBytes - 1): result = updateCrc32(p[i], result) - if readBytes != bufSize: break - dealloc(buf) - close(bin) - -const - base = int32(65521) # largest prime smaller than 65536 - # NMAX = 5552; original code with unsigned 32 bit integer - # NMAX is the largest n - # such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 - nmax = 3854 # code with signed 32 bit integer - # NMAX is the largest n such that - # 255n(n+1)/2 + (n+1)(BASE-1) <= 2^31-1 - # The penalty is the time loss in the extra MOD-calls. - -proc updateAdler32(adler: int32, buf: pointer, length: int): int32 = - var - s1, s2: int32 - L, k, b: int - s1 = adler and int32(0x0000FFFF) - s2 = (adler shr int32(16)) and int32(0x0000FFFF) - L = length - b = 0 - while (L > 0): - if L < nmax: k = L - else: k = nmax - dec(L, k) - while (k > 0): - s1 = s1 +% int32((cast[cstring](buf))[b]) - s2 = s2 +% s1 - inc(b) - dec(k) - s1 = `%%`(s1, base) - s2 = `%%`(s2, base) - result = (s2 shl int32(16)) or s1 diff --git a/compiler/docgen.nim b/compiler/docgen.nim index f8489d825..8536cc619 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -18,7 +18,7 @@ import type TSections = array[TSymKind, Rope] - TDocumentor = object of rstgen.TRstGenerator + TDocumentor = object of rstgen.RstGenerator modDesc: Rope # module description id: int # for generating IDs toc, section: TSections @@ -29,7 +29,7 @@ type PDoc* = ref TDocumentor ## Alias to type less. proc compilerMsgHandler(filename: string, line, col: int, - msgKind: rst.TMsgKind, arg: string) {.procvar.} = + msgKind: rst.MsgKind, arg: string) {.procvar.} = # translate msg kind: var k: msgs.TMsgKind case msgKind @@ -53,7 +53,7 @@ proc docgenFindFile(s: string): string {.procvar.} = proc parseRst(text, filename: string, line, column: int, hasToc: var bool, - rstOptions: TRstParseOptions): PRstNode = + rstOptions: RstParseOptions): PRstNode = result = rstParse(text, filename, line, column, hasToc, rstOptions, docgenFindFile, compilerMsgHandler) @@ -75,7 +75,7 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc = ga('send', 'pageview'); </script> - """ % [config["doc.googleAnalytics"]] + """ % [config.getOrDefault"doc.googleAnalytics"] else: result.analytics = "" @@ -158,7 +158,7 @@ proc genRecComment(d: PDoc, n: PNode): Rope = if n == nil: return nil result = genComment(d, n).rope if result == nil: - if n.kind notin {nkEmpty..nkNilLit}: + if n.kind notin {nkEmpty..nkNilLit, nkEnumTy}: for i in countup(0, len(n)-1): result = genRecComment(d, n.sons[i]) if result != nil: return diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index aa832b6df..27de06811 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -10,10 +10,10 @@ # This module implements a new documentation generator that runs after # semantic checking. -import +import os, options, ast, astalgo, msgs, ropes, idents, passes, docgen -type +type TGen = object of TPassContext doc: PDoc module: PSym @@ -29,12 +29,12 @@ proc close(p: PPassContext, n: PNode): PNode = except IOError: discard -proc processNode(c: PPassContext, n: PNode): PNode = +proc processNode(c: PPassContext, n: PNode): PNode = result = n var g = PGen(c) generateDoc(g.doc, n) -proc myOpen(module: PSym): PPassContext = +proc myOpen(module: PSym): PPassContext = var g: PGen new(g) g.module = module @@ -45,5 +45,5 @@ proc myOpen(module: PSym): PPassContext = const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close) -proc finishDoc2Pass*(project: string) = +proc finishDoc2Pass*(project: string) = discard diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 8959aa4df..c33e5be86 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -25,16 +25,21 @@ proc copyNode(ctx: TemplCtx, a, b: PNode): PNode = if ctx.instLines: result.info = b.info proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = + template handleParam(param) = + let x = param + if x.kind == nkArgList: + for y in items(x): result.add(y) + else: + result.add copyTree(x) + case templ.kind of nkSym: var s = templ.sym if s.owner.id == c.owner.id: if s.kind == skParam and sfGenSym notin s.flags: - let x = actual.sons[s.position] - if x.kind == nkArgList: - for y in items(x): result.add(y) - else: - result.add copyTree(x) + handleParam actual.sons[s.position] + elif s.kind == skGenericParam: + handleParam actual.sons[s.owner.typ.len + s.position - 1] else: internalAssert sfGenSym in s.flags var x = PSym(idTableGet(c.mapping, s)) @@ -56,20 +61,44 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = proc evalTemplateArgs(n: PNode, s: PSym): PNode = # if the template has zero arguments, it can be called without ``()`` # `n` is then a nkSym or something similar - var a: int - case n.kind - of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: - a = sonsLen(n) - else: a = 0 - var f = s.typ.sonsLen - if a > f: globalError(n.info, errWrongNumberOfArguments) + var totalParams = case n.kind + of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: n.len-1 + else: 0 + + var + # XXX: Since immediate templates are not subjected to the + # standard sigmatching algorithm, they will have a number + # of deficiencies when it comes to generic params: + # Type dependencies between the parameters won't be honoured + # and the bound generic symbols won't be resolvable within + # their bodies. We could try to fix this, but it may be + # wiser to just deprecate immediate templates and macros + # now that we have working untyped parameters. + genericParams = if sfImmediate in s.flags: 0 + else: s.ast[genericParamsPos].len + expectedRegularParams = <s.typ.len + givenRegularParams = totalParams - genericParams + + if totalParams > expectedRegularParams + genericParams: + globalError(n.info, errWrongNumberOfArguments) result = newNodeI(nkArgList, n.info) - for i in countup(1, f - 1): - var arg = if i < a: n.sons[i] else: copyTree(s.typ.n.sons[i].sym.ast) - if arg == nil or arg.kind == nkEmpty: + for i in 1 .. givenRegularParams: + result.addSon n.sons[i] + + # handle parameters with default values, which were + # not supplied by the user + for i in givenRegularParams+1 .. expectedRegularParams: + let default = s.typ.n.sons[i].sym.ast + if default.isNil or default.kind == nkEmpty: localError(n.info, errWrongNumberOfArguments) - addSon(result, arg) + addSon(result, ast.emptyNode) + else: + addSon(result, default.copyTree) + + # add any generic paramaters + for i in 1 .. genericParams: + result.addSon n.sons[givenRegularParams + i] var evalTemplateCounter* = 0 # to prevent endless recursion in templates instantiation diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 26f0318ee..3882bdd03 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -13,12 +13,13 @@ # nim files. import - lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs, crc + lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs, + securehash, streams type TSystemCC* = enum ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc, - ccTcc, ccPcc, ccUcc, ccIcl + ccTcc, ccPcc, ccUcc, ccIcl, asmFasm TInfoCCProp* = enum # properties of the C compiler: hasSwitchRange, # CC allows ranges in switch statements (GNU C) hasComputedGoto, # CC has computed goto (GNU C extension) @@ -26,8 +27,8 @@ type hasAssume, # CC has __assume (Visual C extension) hasGcGuard, # CC supports GC_GUARD to keep stack roots hasGnuAsm, # CC's asm uses the absurd GNU assembler syntax - hasNakedDeclspec, # CC has __declspec(naked) - hasNakedAttribute # CC has __attribute__((naked)) + hasDeclspec, # CC has __declspec(X) + hasAttribute, # CC has __attribute__((X)) TInfoCCProps* = set[TInfoCCProp] TInfoCC* = tuple[ name: string, # the short name of the compiler @@ -85,7 +86,7 @@ compiler gcc: structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name packedPragma: "__attribute__((__packed__))", props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm, - hasNakedAttribute}) + hasAttribute}) # LLVM Frontend for GCC/G++ compiler llvmGcc: @@ -122,12 +123,12 @@ compiler vcc: includeCmd: " /I", linkDirCmd: " /LIBPATH:", linkLibCmd: " $1.lib", - debug: " /GZ /Zi ", + debug: " /RTC1 /Z7 ", pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", structStmtFmt: "$3$n$1 $2", packedPragma: "#pragma pack(1)", - props: {hasCpp, hasAssume, hasNakedDeclspec}) + props: {hasCpp, hasAssume, hasDeclspec}) # Intel C/C++ Compiler compiler icl: @@ -317,6 +318,31 @@ compiler ucc: packedPragma: "", # XXX: not supported yet props: {}) +# fasm assembler +compiler fasm: + result = ( + name: "fasm", + objExt: "o", + optSpeed: "", + optSize: "", + compilerExe: "fasm", + cppCompiler: "fasm", + compileTmpl: "$file $objfile", + buildGui: "", + buildDll: "", + buildLib: "", + linkerExe: "", + linkTmpl: "", + includeCmd: "", + linkDirCmd: "", + linkLibCmd: "", + debug: "", + pic: "", + asmStmtFrmt: "", + structStmtFmt: "", + packedPragma: "", + props: {}) + const CC*: array[succ(low(TSystemCC))..high(TSystemCC), TInfoCC] = [ gcc(), @@ -330,17 +356,22 @@ const tcc(), pcc(), ucc(), - icl()] + icl(), + fasm()] hExt* = ".h" var cCompiler* = ccGcc # the used compiler + cAssembler* = ccNone gMixedMode*: bool # true if some module triggered C++ codegen cIncludes*: seq[string] = @[] # directories to search for included files cLibs*: seq[string] = @[] # directories to search for lib files cLinkedLibs*: seq[string] = @[] # libraries to link +const + cValidAssemblers* = {asmFasm} + # implementation proc libNameTmpl(): string {.inline.} = @@ -428,7 +459,7 @@ proc resetCompilationLists* = initLinkedList(toCompile) ## XXX: we must associate these with their originating module # when the module is loaded/unloaded it adds/removes its items - # That's because we still need to CRC check the external files + # That's because we still need to hash check the external files # Maybe we can do that in checkDep on the other hand? initLinkedList(externalToCompile) initLinkedList(toLink) @@ -437,17 +468,13 @@ proc addFileToLink*(filename: string) = prependStr(toLink, filename) # BUGFIX: was ``appendStr`` -proc execWithEcho(cmd: string, prettyCmd = ""): int = - if optListCmd in gGlobalOptions or gVerbosity > 0: - if prettyCmd != "": - msgWriteln(prettyCmd) - else: - msgWriteln(cmd) +proc execWithEcho(cmd: string, msg = hintExecuting): int = + rawMessage(msg, cmd) result = execCmd(cmd) -proc execExternalProgram*(cmd: string, prettyCmd = "") = - if execWithEcho(cmd, prettyCmd) != 0: - rawMessage(errExecutionOfProgramFailed, "") +proc execExternalProgram*(cmd: string, msg = hintExecuting) = + if execWithEcho(cmd, msg) != 0: + rawMessage(errExecutionOfProgramFailed, cmd) proc generateScript(projectFile: string, script: Rope) = let (dir, name, ext) = splitFile(projectFile) @@ -530,6 +557,21 @@ proc getLinkerExe(compiler: TSystemCC): string = proc getCompileCFileCmd*(cfilename: string, isExternal = false): string = var c = cCompiler + if cfilename.endswith(".asm"): + var customAssembler = getConfigVar("assembler") + if customAssembler.len > 0: + c = nameToCC(customAssembler) + else: + if targetCPU == cpuI386 or targetCPU == cpuAmd64: + c = asmFasm + else: + c = ccNone + + if c == ccNone: + rawMessage(errExternalAssemblerNotFound, "") + elif c notin cValidAssemblers: + rawMessage(errExternalAssemblerNotValid, customAssembler) + var options = cFileSpecificOptions(cfilename) var exe = getConfigVar(c, ".exe") if exe.len == 0: exe = c.getCompilerExe @@ -572,32 +614,30 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string = "nim", quoteShell(getPrefixDir()), "lib", quoteShell(libpath)]) -proc footprint(filename: string): TCrc32 = - # note, '><' further modifies a crc value with a string. - result = crcFromFile(filename) >< - platform.OS[targetOS].name >< - platform.CPU[targetCPU].name >< - extccomp.CC[extccomp.cCompiler].name >< - getCompileCFileCmd(filename, true) +proc footprint(filename: string): SecureHash = + result = secureHash( + $secureHashFile(filename) & + platform.OS[targetOS].name & + platform.CPU[targetCPU].name & + extccomp.CC[extccomp.cCompiler].name & + getCompileCFileCmd(filename, true)) proc externalFileChanged(filename: string): bool = if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}: return false - var crcFile = toGeneratedFile(filename.withPackageName, "crc") - var currentCrc = int(footprint(filename)) + var hashFile = toGeneratedFile(filename.withPackageName, "sha1") + var currentHash = footprint(filename) var f: File - if open(f, crcFile, fmRead): - var line = newStringOfCap(40) - if not f.readLine(line): line = "0" + if open(f, hashFile, fmRead): + let oldHash = parseSecureHash(f.readLine()) close(f) - var oldCrc = parseInt(line) - result = oldCrc != currentCrc + result = oldHash != currentHash else: result = true if result: - if open(f, crcFile, fmWrite): - f.writeln($currentCrc) + if open(f, hashFile, fmWrite): + f.writeLine($currentHash) close(f) proc addExternalFileToCompile*(filename: string) = @@ -632,6 +672,12 @@ proc callCCompiler*(projectfile: string) = var prettyCmds: TStringSeq = @[] let prettyCb = proc (idx: int) = echo prettyCmds[idx] + let runCb = proc (idx: int, p: Process) = + let exitCode = p.peekExitCode + if exitCode != 0: + rawMessage(errGenerated, "execution of an external compiler program '" & + cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n" & + p.outputStream.readAll.strip) compileCFile(toCompile, script, cmds, prettyCmds, false) compileCFile(externalToCompile, script, cmds, prettyCmds, true) if optCompileOnly notin gGlobalOptions: @@ -640,22 +686,19 @@ proc callCCompiler*(projectfile: string) = if gNumberOfProcessors <= 1: for i in countup(0, high(cmds)): res = execWithEcho(cmds[i]) - if res != 0: rawMessage(errExecutionOfProgramFailed, []) + if res != 0: rawMessage(errExecutionOfProgramFailed, cmds[i]) elif optListCmd in gGlobalOptions or gVerbosity > 1: - res = execProcesses(cmds, {poEchoCmd, poUsePath, poParentStreams}, - gNumberOfProcessors) + res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams}, + gNumberOfProcessors, afterRunEvent=runCb) elif gVerbosity == 1: - res = execProcesses(cmds, {poUsePath, poParentStreams}, - gNumberOfProcessors, prettyCb) + res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams}, + gNumberOfProcessors, prettyCb, afterRunEvent=runCb) else: - res = execProcesses(cmds, {poUsePath, poParentStreams}, - gNumberOfProcessors) + res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams}, + gNumberOfProcessors, afterRunEvent=runCb) if res != 0: if gNumberOfProcessors <= 1: - rawMessage(errExecutionOfProgramFailed, []) - else: - rawMessage(errGenerated, " execution of an external program failed; " & - "rerun with --parallelBuild:1 to see the error message") + rawMessage(errExecutionOfProgramFailed, cmds.join()) if optNoLinking notin gGlobalOptions: # call the linker: var it = PStrEntry(toLink.head) @@ -668,7 +711,8 @@ proc callCCompiler*(projectfile: string) = it = PStrEntry(it.next) if optGenStaticLib in gGlobalOptions: - linkCmd = CC[c].buildLib % ["libfile", (libNameTmpl() % gProjectName), + let name = splitFile(gProjectName).name + linkCmd = CC[c].buildLib % ["libfile", (libNameTmpl() % name), "objfiles", objfiles] else: var linkerExe = getConfigVar(c, ".linkerexe") @@ -703,10 +747,8 @@ proc callCCompiler*(projectfile: string) = "nim", quoteShell(getPrefixDir()), "lib", quoteShell(libpath)]) if optCompileOnly notin gGlobalOptions: - if gVerbosity == 1: - execExternalProgram(linkCmd, "[Linking]") - else: - execExternalProgram(linkCmd) + execExternalProgram(linkCmd, + if gVerbosity > 1: hintExecuting else: hintLinking) else: linkCmd = "" if optGenScript in gGlobalOptions: diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim index 5d1f73be4..21810adb9 100644 --- a/compiler/filter_tmpl.nim +++ b/compiler/filter_tmpl.nim @@ -9,18 +9,18 @@ # This module implements Nim's standard template filter. -import - llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options, +import + llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options, renderer, filters proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream # #! template(subsChar='$', metaChar='#') | standard(version="0.7.2") # implementation -type - TParseState = enum +type + TParseState = enum psDirective, psTempl - TTmplParser{.final.} = object + TTmplParser{.final.} = object inp: PLLStream state: TParseState info: TLineInfo @@ -33,18 +33,18 @@ type pendingExprLine: bool -const +const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'} -proc newLine(p: var TTmplParser) = +proc newLine(p: var TTmplParser) = llStreamWrite(p.outp, repeat(')', p.emitPar)) p.emitPar = 0 if p.info.line > int16(1): llStreamWrite(p.outp, "\n") if p.pendingExprLine: llStreamWrite(p.outp, spaces(2)) p.pendingExprLine = false - -proc scanPar(p: var TTmplParser, d: int) = + +proc scanPar(p: var TTmplParser, d: int) = var i = d while true: case p.x[i] @@ -58,44 +58,44 @@ proc scanPar(p: var TTmplParser, d: int) = else: discard inc(i) -proc withInExpr(p: TTmplParser): bool {.inline.} = +proc withInExpr(p: TTmplParser): bool {.inline.} = result = p.par > 0 or p.bracket > 0 or p.curly > 0 - -proc parseLine(p: var TTmplParser) = - var + +proc parseLine(p: var TTmplParser) = + var d, j, curly: int keyw: string j = 0 while p.x[j] == ' ': inc(j) - if (p.x[0] == p.nimDirective) and (p.x[0 + 1] == '!'): + if p.x[0] == p.nimDirective and p.x[1] in {'?', '!'}: newLine(p) - elif (p.x[j] == p.nimDirective): + elif p.x[j] == p.nimDirective: newLine(p) inc(j) while p.x[j] == ' ': inc(j) d = j keyw = "" - while p.x[j] in PatternChars: + while p.x[j] in PatternChars: add(keyw, p.x[j]) inc(j) - + scanPar(p, j) p.pendingExprLine = withInExpr(p) or llstream.endsWithOpr(p.x) case whichKeyword(keyw) - of wEnd: - if p.indent >= 2: + of wEnd: + if p.indent >= 2: dec(p.indent, 2) - else: + else: p.info.col = int16(j) localError(p.info, errXNotAllowedHere, "end") llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, "#end") - of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator, - wConverter, wMacro, wTemplate, wMethod: + of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator, + wConverter, wMacro, wTemplate, wMethod: llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, substr(p.x, d)) inc(p.indent, 2) - of wElif, wOf, wElse, wExcept, wFinally: + of wElif, wOf, wElse, wExcept, wFinally: llStreamWrite(p.outp, spaces(p.indent - 2)) llStreamWrite(p.outp, substr(p.x, d)) of wLet, wVar, wConst, wType: @@ -108,7 +108,7 @@ proc parseLine(p: var TTmplParser) = llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, substr(p.x, d)) p.state = psDirective - else: + else: # data line # reset counters p.par = 0 @@ -116,42 +116,42 @@ proc parseLine(p: var TTmplParser) = p.bracket = 0 j = 0 case p.state - of psTempl: + of psTempl: # next line of string literal: llStreamWrite(p.outp, p.conc) llStreamWrite(p.outp, "\n") llStreamWrite(p.outp, spaces(p.indent + 2)) llStreamWrite(p.outp, "\"") - of psDirective: + of psDirective: newLine(p) llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, p.emit) llStreamWrite(p.outp, "(\"") inc(p.emitPar) p.state = psTempl - while true: + while true: case p.x[j] - of '\0': - break - of '\x01'..'\x1F', '\x80'..'\xFF': + of '\0': + break + of '\x01'..'\x1F', '\x80'..'\xFF': llStreamWrite(p.outp, "\\x") llStreamWrite(p.outp, toHex(ord(p.x[j]), 2)) inc(j) - of '\\': + of '\\': llStreamWrite(p.outp, "\\\\") inc(j) - of '\'': + of '\'': llStreamWrite(p.outp, "\\\'") inc(j) - of '\"': + of '\"': llStreamWrite(p.outp, "\\\"") inc(j) - else: - if p.x[j] == p.subsChar: + else: + if p.x[j] == p.subsChar: # parse Nim expression: inc(j) case p.x[j] - of '{': + of '{': p.info.col = int16(j) llStreamWrite(p.outp, '\"') llStreamWrite(p.outp, p.conc) @@ -159,50 +159,50 @@ proc parseLine(p: var TTmplParser) = llStreamWrite(p.outp, '(') inc(j) curly = 0 - while true: + while true: case p.x[j] - of '\0': + of '\0': localError(p.info, errXExpected, "}") break - of '{': + of '{': inc(j) inc(curly) llStreamWrite(p.outp, '{') - of '}': + of '}': inc(j) - if curly == 0: break + if curly == 0: break if curly > 0: dec(curly) llStreamWrite(p.outp, '}') - else: + else: llStreamWrite(p.outp, p.x[j]) inc(j) llStreamWrite(p.outp, ')') llStreamWrite(p.outp, p.conc) llStreamWrite(p.outp, '\"') - of 'a'..'z', 'A'..'Z', '\x80'..'\xFF': + of 'a'..'z', 'A'..'Z', '\x80'..'\xFF': llStreamWrite(p.outp, '\"') llStreamWrite(p.outp, p.conc) llStreamWrite(p.outp, p.toStr) llStreamWrite(p.outp, '(') - while p.x[j] in PatternChars: + while p.x[j] in PatternChars: llStreamWrite(p.outp, p.x[j]) inc(j) llStreamWrite(p.outp, ')') llStreamWrite(p.outp, p.conc) llStreamWrite(p.outp, '\"') - else: - if p.x[j] == p.subsChar: + else: + if p.x[j] == p.subsChar: llStreamWrite(p.outp, p.subsChar) inc(j) - else: + else: p.info.col = int16(j) localError(p.info, errInvalidExpression, "$") - else: + else: llStreamWrite(p.outp, p.x[j]) inc(j) llStreamWrite(p.outp, "\\n\"") -proc filterTmpl(stdin: PLLStream, filename: string, call: PNode): PLLStream = +proc filterTmpl(stdin: PLLStream, filename: string, call: PNode): PLLStream = var p: TTmplParser p.info = newLineInfo(filename, 0, 0) p.outp = llStreamOpen("") diff --git a/compiler/filters.nim b/compiler/filters.nim index 783a320a4..adafe464e 100644 --- a/compiler/filters.nim +++ b/compiler/filters.nim @@ -10,7 +10,7 @@ # This module implements Nim's simple filters and helpers for filters. import - llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options, + llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options, renderer proc filterReplace*(stdin: PLLStream, filename: string, call: PNode): PLLStream @@ -21,40 +21,40 @@ proc strArg*(n: PNode, name: string, pos: int, default: string): string proc boolArg*(n: PNode, name: string, pos: int, default: bool): bool # implementation -proc invalidPragma(n: PNode) = +proc invalidPragma(n: PNode) = localError(n.info, errXNotAllowedHere, renderTree(n, {renderNoComments})) -proc getArg(n: PNode, name: string, pos: int): PNode = +proc getArg(n: PNode, name: string, pos: int): PNode = result = nil - if n.kind in {nkEmpty..nkNilLit}: return - for i in countup(1, sonsLen(n) - 1): - if n.sons[i].kind == nkExprEqExpr: + if n.kind in {nkEmpty..nkNilLit}: return + for i in countup(1, sonsLen(n) - 1): + if n.sons[i].kind == nkExprEqExpr: if n.sons[i].sons[0].kind != nkIdent: invalidPragma(n) - if identEq(n.sons[i].sons[0].ident, name): + if identEq(n.sons[i].sons[0].ident, name): return n.sons[i].sons[1] - elif i == pos: + elif i == pos: return n.sons[i] - -proc charArg(n: PNode, name: string, pos: int, default: char): char = + +proc charArg(n: PNode, name: string, pos: int, default: char): char = var x = getArg(n, name, pos) if x == nil: result = default elif x.kind == nkCharLit: result = chr(int(x.intVal)) else: invalidPragma(n) - -proc strArg(n: PNode, name: string, pos: int, default: string): string = + +proc strArg(n: PNode, name: string, pos: int, default: string): string = var x = getArg(n, name, pos) if x == nil: result = default elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal else: invalidPragma(n) - -proc boolArg(n: PNode, name: string, pos: int, default: bool): bool = + +proc boolArg(n: PNode, name: string, pos: int, default: bool): bool = var x = getArg(n, name, pos) if x == nil: result = default elif (x.kind == nkIdent) and identEq(x.ident, "true"): result = true elif (x.kind == nkIdent) and identEq(x.ident, "false"): result = false else: invalidPragma(n) - -proc filterStrip(stdin: PLLStream, filename: string, call: PNode): PLLStream = + +proc filterStrip(stdin: PLLStream, filename: string, call: PNode): PLLStream = var pattern = strArg(call, "startswith", 1, "") var leading = boolArg(call, "leading", 2, true) var trailing = boolArg(call, "trailing", 3, true) @@ -62,13 +62,13 @@ proc filterStrip(stdin: PLLStream, filename: string, call: PNode): PLLStream = var line = newStringOfCap(80) while llStreamReadLine(stdin, line): var stripped = strip(line, leading, trailing) - if (len(pattern) == 0) or startsWith(stripped, pattern): + if (len(pattern) == 0) or startsWith(stripped, pattern): llStreamWriteln(result, stripped) - else: + else: llStreamWriteln(result, line) llStreamClose(stdin) -proc filterReplace(stdin: PLLStream, filename: string, call: PNode): PLLStream = +proc filterReplace(stdin: PLLStream, filename: string, call: PNode): PLLStream = var sub = strArg(call, "sub", 1, "") if len(sub) == 0: invalidPragma(call) var by = strArg(call, "by", 2, "") diff --git a/compiler/forloops.nim b/compiler/forloops.nim index efe000968..5074d79d5 100644 --- a/compiler/forloops.nim +++ b/compiler/forloops.nim @@ -12,13 +12,13 @@ import ast, astalgo const - someCmp = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, - mEqUntracedRef, mLeI, mLeI64, mLeF64, mLeU, mLeU64, mLeEnum, - mLeCh, mLeB, mLePtr, mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum, + someCmp = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, + mEqUntracedRef, mLeI, mLeF64, mLeU, mLeU64, mLeEnum, + mLeCh, mLeB, mLePtr, mLtI, mLtF64, mLtU, mLtU64, mLtEnum, mLtCh, mLtB, mLtPtr} proc isCounter(s: PSym): bool {.inline.} = - s.kind in {skResult, skVar, skLet, skTemp} and + s.kind in {skResult, skVar, skLet, skTemp} and {sfGlobal, sfAddrTaken} * s.flags == {} proc isCall(n: PNode): bool {.inline.} = @@ -29,7 +29,7 @@ proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags proc getCounter(lastStmt: PNode): PSym = if lastStmt.isCall: let op = lastStmt.sym - if op.magic in {mDec, mInc} or + if op.magic in {mDec, mInc} or ((op.name.s == "+=" or op.name.s == "-=") and op.fromSystem): if op[1].kind == nkSym and isCounter(op[1].sym): result = op[1].sym @@ -67,7 +67,7 @@ proc extractForLoop*(loop, fullTree: PNode): ForLoop = if not cond.isCall: return if cond[0].sym.magic notin someCmp: return - + var lastStmt = loop[1] while lastStmt.kind in {nkStmtList, nkStmtListExpr}: lastStmt = lastStmt.lastSon @@ -76,7 +76,7 @@ proc extractForLoop*(loop, fullTree: PNode): ForLoop = if counter.isNil or counter.ast.isNil: return template `=~`(a, b): expr = a.kind == nkSym and a.sym == b - + if cond[1] =~ counter or cond[2] =~ counter: # ok, now check 'counter' is not used *after* the loop if counterInTree(fullTree, loop, counter): return diff --git a/compiler/guards.nim b/compiler/guards.nim index df2c1dd75..5ad932e48 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -13,13 +13,13 @@ import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents, saturate const - someEq = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, + someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, mEqUntracedRef, mEqStr, mEqSet, mEqCString} # set excluded here as the semantics are vastly different: - someLe = {mLeI, mLeI64, mLeF64, mLeU, mLeU64, mLeEnum, + someLe = {mLeI, mLeF64, mLeU, mLeU64, mLeEnum, mLeCh, mLeB, mLePtr, mLeStr} - someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum, + someLt = {mLtI, mLtF64, mLtU, mLtU64, mLtEnum, mLtCh, mLtB, mLtPtr, mLtStr} someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq, @@ -30,13 +30,14 @@ const someHigh = {mHigh} # we don't list unsigned here because wrap around semantics suck for # proving anything: - someAdd = {mAddI, mAddI64, mAddF64, mSucc} - someSub = {mSubI, mSubI64, mSubF64, mPred} - someMul = {mMulI, mMulI64, mMulF64} - someDiv = {mDivI, mDivI64, mDivF64} - someMod = {mModI, mModI64} + someAdd = {mAddI, mAddF64, mSucc} + someSub = {mSubI, mSubF64, mPred} + someMul = {mMulI, mMulF64} + someDiv = {mDivI, mDivF64} + someMod = {mModI} someMax = {mMaxI, mMaxF64} someMin = {mMinI, mMinF64} + someBinaryOp = someAdd+someSub+someMul+someMax+someMin proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit} proc isLocation(n: PNode): bool = not n.isValue @@ -165,11 +166,21 @@ proc `|+|`(a, b: PNode): PNode = if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal |+| b.intVal else: result.floatVal = a.floatVal + b.floatVal +proc `|-|`(a, b: PNode): PNode = + result = copyNode(a) + if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal |-| b.intVal + else: result.floatVal = a.floatVal - b.floatVal + proc `|*|`(a, b: PNode): PNode = result = copyNode(a) if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal |*| b.intVal else: result.floatVal = a.floatVal * b.floatVal +proc `|div|`(a, b: PNode): PNode = + result = copyNode(a) + if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal div b.intVal + else: result.floatVal = a.floatVal / b.floatVal + proc negate(a, b, res: PNode): PNode = if b.kind in {nkCharLit..nkUInt64Lit} and b.intVal != low(BiggestInt): var b = copyNode(b) @@ -213,10 +224,16 @@ proc reassociation(n: PNode): PNode = if result[2].isValue and result[1].getMagic in someAdd and result[1][2].isValue: result = opAdd.buildCall(result[1][1], result[1][2] |+| result[2]) + if result[2].intVal == 0: + result = result[1] of someMul: if result[2].isValue and result[1].getMagic in someMul and result[1][2].isValue: - result = opAdd.buildCall(result[1][1], result[1][2] |*| result[2]) + result = opMul.buildCall(result[1][1], result[1][2] |*| result[2]) + if result[2].intVal == 1: + result = result[1] + elif result[2].intVal == 0: + result = zero() else: discard proc pred(n: PNode): PNode = @@ -234,7 +251,7 @@ proc canon*(n: PNode): PNode = result.sons[i] = canon(n.sons[i]) elif n.kind == nkSym and n.sym.kind == skLet and n.sym.ast.getMagic in (someEq + someAdd + someMul + someMin + - someMax + someHigh + {mUnaryLt} + someSub + someLen): + someMax + someHigh + {mUnaryLt} + someSub + someLen + someDiv): result = n.sym.ast.copyTree else: result = n @@ -248,7 +265,7 @@ proc canon*(n: PNode): PNode = # high == len+(-1) result = opAdd.buildCall(opLen.buildCall(result[1]), minusOne()) of mUnaryLt: - result = buildCall(opAdd, result[1], newIntNode(nkIntLit, -1)) + result = buildCall(opAdd, result[1], minusOne()) of someSub: # x - 4 --> x + (-4) result = negate(result[1], result[2], result) @@ -294,6 +311,16 @@ proc canon*(n: PNode): PNode = if plus != nil and not isLetLocation(x, true): result = buildCall(result[0].sym, plus, y[1]) else: discard + elif x.isValue and y.getMagic in someAdd and y[2].isValue: + # 0 <= a.len + 3 + # -3 <= a.len + result.sons[1] = x |-| y[2] + result.sons[2] = y[1] + elif x.isValue and y.getMagic in someSub and y[2].isValue: + # 0 <= a.len - 3 + # 3 <= a.len + result.sons[1] = x |+| y[2] + result.sons[2] = y[1] else: discard proc `+@`*(a: PNode; b: BiggestInt): PNode = @@ -313,6 +340,9 @@ proc usefulFact(n: PNode): PNode = if isLetLocation(n.sons[1], true) or isLetLocation(n.sons[2], true): # XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1' result = n + elif n[1].getMagic in someLen or n[2].getMagic in someLen: + # XXX Rethink this whole idea of 'usefulFact' for semparallel + result = n of mIsNil: if isLetLocation(n.sons[1], false) or isVar(n.sons[1]): result = n @@ -366,8 +396,8 @@ proc usefulFact(n: PNode): PNode = type TModel* = seq[PNode] # the "knowledge base" -proc addFact*(m: var TModel, n: PNode) = - let n = usefulFact(n) +proc addFact*(m: var TModel, nn: PNode) = + let n = usefulFact(nn) if n != nil: m.add n proc addFactNeg*(m: var TModel, n: PNode) = @@ -697,10 +727,57 @@ proc simpleSlice*(a, b: PNode): BiggestInt = else: result = -1 + +template isMul(x): expr = x.getMagic in someMul +template isDiv(x): expr = x.getMagic in someDiv +template isAdd(x): expr = x.getMagic in someAdd +template isSub(x): expr = x.getMagic in someSub +template isVal(x): expr = x.kind in {nkCharLit..nkUInt64Lit} +template isIntVal(x, y): expr = x.intVal == y + +import macros + +macro `=~`(x: PNode, pat: untyped): bool = + proc m(x, pat, conds: NimNode) = + case pat.kind + of nnkInfix: + case $pat[0] + of "*": conds.add getAst(isMul(x)) + of "/": conds.add getAst(isDiv(x)) + of "+": conds.add getAst(isAdd(x)) + of "-": conds.add getAst(isSub(x)) + else: + error("invalid pattern") + m(newTree(nnkBracketExpr, x, newLit(1)), pat[1], conds) + m(newTree(nnkBracketExpr, x, newLit(2)), pat[2], conds) + of nnkPar: + if pat.len == 1: + m(x, pat[0], conds) + else: + error("invalid pattern") + of nnkIdent: + let c = newTree(nnkStmtListExpr, newLetStmt(pat, x)) + conds.add c + if ($pat)[^1] == 'c': c.add(getAst(isVal(pat))) + else: c.add bindSym"true" + of nnkIntLit: + conds.add(getAst(isIntVal(pat.intVal))) + else: + error("invalid pattern") + + var conds = newTree(nnkBracket) + m(x, pat, conds) + result = nestList(!"and", conds) + + +proc isMinusOne(n: PNode): bool = + n.kind in {nkCharLit..nkUInt64Lit} and n.intVal == -1 + proc pleViaModel(model: TModel; aa, bb: PNode): TImplication proc ple(m: TModel; a, b: PNode): TImplication = template `<=?`(a,b): expr = ple(m,a,b) == impYes + template `>=?`(a,b): expr = ple(m, nkIntLit.newIntNode(b), a) == impYes # 0 <= 3 if a.isValue and b.isValue: @@ -721,6 +798,7 @@ proc ple(m: TModel; a, b: PNode): TImplication = if a.intVal <= 0: return impYes # x <= y+c if 0 <= c and x <= y + # x <= y+(-c) if c <= 0 and y >= x if b.getMagic in someAdd and zero() <=? b[2] and a <=? b[1]: return impYes # x+c <= y if c <= 0 and x <= y @@ -730,10 +808,44 @@ proc ple(m: TModel; a, b: PNode): TImplication = if b.getMagic in someMul: if a <=? b[1] and one() <=? b[2] and zero() <=? b[1]: return impYes + + if a.getMagic in someMul and a[2].isValue and a[1].getMagic in someDiv and + a[1][2].isValue: + # simplify (x div 4) * 2 <= y to x div (c div d) <= y + if ple(m, buildCall(opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes: + return impYes + + # x*3 + x == x*4. It follows that: + # x*3 + y <= x*4 if y <= x and 3 <= 4 + if a =~ x*dc + y and b =~ x2*ec: + if sameTree(x, x2): + let ec1 = opAdd.buildCall(ec, minusOne()) + if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? x: + return impYes + elif a =~ x*dc and b =~ x2*ec + y: + #echo "BUG cam ehrer e ", a, " <=? ", b + if sameTree(x, x2): + let ec1 = opAdd.buildCall(ec, minusOne()) + if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? zero(): + return impYes + + # x+c <= x+d if c <= d. Same for *, - etc. + if a.getMagic in someBinaryOp and a.getMagic == b.getMagic: + if sameTree(a[1], b[1]) and a[2] <=? b[2]: return impYes + elif sameTree(a[2], b[2]) and a[1] <=? b[1]: return impYes + # x div c <= y if 1 <= c and 0 <= y and x <= y: if a.getMagic in someDiv: if one() <=? a[2] and zero() <=? b and a[1] <=? b: return impYes + # x div c <= x div d if d <= c + if b.getMagic in someDiv: + if sameTree(a[1], b[1]) and b[2] <=? a[2]: return impYes + + # x div z <= x - 1 if z <= x + if a[2].isValue and b.getMagic in someAdd and b[2].isMinusOne: + if a[2] <=? a[1] and sameTree(a[1], b[1]): return impYes + # slightly subtle: # x <= max(y, z) iff x <= y or x <= z # note that 'x <= max(x, z)' is a special case of the above rule @@ -769,11 +881,19 @@ proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication = for i in 0..m.high: let fact = m[i] if fact != nil and fact.getMagic in someLe: - # x <= y implies a <= b if a <= x and y <= b - let x = fact[1] - let y = fact[2] # mark as used: m[i] = nil + # i <= len-100 + # i <=? len-1 + # --> true if (len-100) <= (len-1) + let x = fact[1] + let y = fact[2] + if sameTree(x, a) and y.getMagic in someAdd and b.getMagic in someAdd and + sameTree(y[1], b[1]): + if ple(m, b[2], y[2]) == impYes: + return impYes + + # x <= y implies a <= b if a <= x and y <= b if ple(m, a, x) == impYes: if ple(m, y, b) == impYes: return impYes diff --git a/compiler/hlo.nim b/compiler/hlo.nim index 363d100be..6cc9567af 100644 --- a/compiler/hlo.nim +++ b/compiler/hlo.nim @@ -28,7 +28,7 @@ proc evalPattern(c: PContext, n, orig: PNode): PNode = else: result = semDirectOp(c, n, {}) if optHints in gOptions and hintPattern in gNotes: - message(orig.info, hintPattern, rule & " --> '" & + message(orig.info, hintPattern, rule & " --> '" & renderTree(result, {renderNoComments}) & "'") proc applyPatterns(c: PContext, n: PNode): PNode = @@ -68,7 +68,7 @@ proc hlo(c: PContext, n: PNode): PNode = result = n else: if n.kind in {nkFastAsgn, nkAsgn, nkIdentDefs, nkVarTuple} and - n.sons[0].kind == nkSym and + n.sons[0].kind == nkSym and {sfGlobal, sfPure} * n.sons[0].sym.flags == {sfGlobal, sfPure}: # do not optimize 'var g {.global} = re(...)' again! return n diff --git a/compiler/idents.nim b/compiler/idents.nim index 0cca18929..d9b72baf0 100644 --- a/compiler/idents.nim +++ b/compiler/idents.nim @@ -11,19 +11,19 @@ # An identifier is a shared immutable string that can be compared by its # id. This module is essential for the compiler's performance. -import - hashes, strutils +import + hashes, strutils, etcpriv -type +type TIdObj* = object of RootObj id*: int # unique id; use this for comparisons and not the pointers - + PIdObj* = ref TIdObj PIdent* = ref TIdent TIdent*{.acyclic.} = object of TIdObj s*: string next*: PIdent # for hash-table chaining - h*: THash # hash value of s + h*: Hash # hash value of s var firstCharIsCS*: bool = true var buckets*: array[0..4096 * 2 - 1, PIdent] @@ -37,18 +37,20 @@ proc cmpIgnoreStyle(a, b: cstring, blen: int): int = while j < blen: while a[i] == '_': inc(i) while b[j] == '_': inc(j) + while isMagicIdentSeparatorRune(a, i): inc(i, magicIdentSeparatorRuneByteWidth) + while isMagicIdentSeparatorRune(b, j): inc(j, magicIdentSeparatorRuneByteWidth) # tolower inlined: var aa = a[i] var bb = b[j] if aa >= 'A' and aa <= 'Z': aa = chr(ord(aa) + (ord('a') - ord('A'))) if bb >= 'A' and bb <= 'Z': bb = chr(ord(bb) + (ord('a') - ord('A'))) result = ord(aa) - ord(bb) - if (result != 0) or (aa == '\0'): break + if (result != 0) or (aa == '\0'): break inc(i) inc(j) if result == 0: if a[i] != '\0': result = 1 - + proc cmpExact(a, b: cstring, blen: int): int = var i = 0 var j = 0 @@ -57,27 +59,27 @@ proc cmpExact(a, b: cstring, blen: int): int = var aa = a[i] var bb = b[j] result = ord(aa) - ord(bb) - if (result != 0) or (aa == '\0'): break + if (result != 0) or (aa == '\0'): break inc(i) inc(j) - if result == 0: + if result == 0: if a[i] != '\0': result = 1 var wordCounter = 1 -proc getIdent*(identifier: cstring, length: int, h: THash): PIdent = +proc getIdent*(identifier: cstring, length: int, h: Hash): PIdent = var idx = h and high(buckets) result = buckets[idx] var last: PIdent = nil var id = 0 - while result != nil: - if cmpExact(cstring(result.s), identifier, length) == 0: - if last != nil: + while result != nil: + if cmpExact(cstring(result.s), identifier, length) == 0: + if last != nil: # make access to last looked up identifier faster: last.next = result.next result.next = buckets[idx] buckets[idx] = result - return + return elif cmpIgnoreStyle(cstring(result.s), identifier, length) == 0: assert((id == 0) or (id == result.id)) id = result.id @@ -89,20 +91,20 @@ proc getIdent*(identifier: cstring, length: int, h: THash): PIdent = for i in countup(0, length - 1): result.s[i] = identifier[i] result.next = buckets[idx] buckets[idx] = result - if id == 0: + if id == 0: inc(wordCounter) result.id = -wordCounter - else: + else: result.id = id -proc getIdent*(identifier: string): PIdent = - result = getIdent(cstring(identifier), len(identifier), +proc getIdent*(identifier: string): PIdent = + result = getIdent(cstring(identifier), len(identifier), hashIgnoreStyle(identifier)) -proc getIdent*(identifier: string, h: THash): PIdent = +proc getIdent*(identifier: string, h: Hash): PIdent = result = getIdent(cstring(identifier), len(identifier), h) -proc identEq*(id: PIdent, name: string): bool = +proc identEq*(id: PIdent, name: string): bool = result = id.id == getIdent(name).id var idAnon* = getIdent":anonymous" diff --git a/compiler/idgen.nim b/compiler/idgen.nim index 3c5669b54..c07782fb2 100644 --- a/compiler/idgen.nim +++ b/compiler/idgen.nim @@ -18,26 +18,26 @@ const when debugIds: import intsets - + var usedIds = initIntSet() -proc registerID*(id: PIdObj) = - when debugIds: - if id.id == -1 or containsOrIncl(usedIds, id.id): +proc registerID*(id: PIdObj) = + when debugIds: + if id.id == -1 or containsOrIncl(usedIds, id.id): internalError("ID already used: " & $id.id) -proc getID*(): int {.inline.} = +proc getID*(): int {.inline.} = result = gFrontEndId inc(gFrontEndId) -proc backendId*(): int {.inline.} = +proc backendId*(): int {.inline.} = result = gBackendId inc(gBackendId) -proc setId*(id: int) {.inline.} = +proc setId*(id: int) {.inline.} = gFrontEndId = max(gFrontEndId, id + 1) -proc idSynchronizationPoint*(idRange: int) = +proc idSynchronizationPoint*(idRange: int) = gFrontEndId = (gFrontEndId div idRange + 1) * idRange + 1 proc toGid(f: string): string = @@ -48,10 +48,10 @@ proc toGid(f: string): string = proc saveMaxIds*(project: string) = var f = open(project.toGid, fmWrite) - f.writeln($gFrontEndId) - f.writeln($gBackendId) + f.writeLine($gFrontEndId) + f.writeLine($gBackendId) f.close() - + proc loadMaxIds*(project: string) = var f: File if open(f, project.toGid, fmRead): diff --git a/compiler/importer.nim b/compiler/importer.nim index d619725db..c121059fd 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -121,7 +121,7 @@ proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) = if s.kind != skEnumField: if s.kind notin ExportableSymKinds: internalError(s.info, "importAllSymbols: " & $s.kind) - if exceptSet.empty or s.name.id notin exceptSet: + if exceptSet.isNil or s.name.id notin exceptSet: rawImportSymbol(c, s) s = nextIter(i, fromMod.tab) @@ -138,7 +138,7 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet) = let s = a.sym if s.kind == skModule: importAllSymbolsExcept(c, s, exceptSet) - elif exceptSet.empty or s.name.id notin exceptSet: + elif exceptSet.isNil or s.name.id notin exceptSet: rawImportSymbol(c, s) of nkExportExceptStmt: localError(n.info, errGenerated, "'export except' not implemented") diff --git a/compiler/installer.ini b/compiler/installer.ini index fff82cb5b..318bb36b1 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -6,7 +6,7 @@ Name: "Nim" Version: "$version" Platforms: """ windows: i386;amd64 - linux: i386;amd64;powerpc64;arm;sparc;mips;powerpc + linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;powerpc;powerpc64el;arm64 macosx: i386;amd64;powerpc64 solaris: i386;amd64;sparc freebsd: i386;amd64 @@ -99,9 +99,13 @@ Files: "lib/pure/concurrency/*.nim" Files: "lib/pure/unidecode/*.nim" Files: "lib/pure/concurrency/*.cfg" Files: "lib/impure/*.nim" +Files: "lib/impure/nre/private/*.nim" Files: "lib/wrappers/*.nim" Files: "lib/wrappers/readline/*.nim" +Files: "lib/wrappers/linenoise/*.nim" +Files: "lib/wrappers/linenoise/*.c" +Files: "lib/wrappers/linenoise/*.h" Files: "lib/wrappers/sdl/*.nim" Files: "lib/wrappers/zip/*.nim" Files: "lib/wrappers/zip/libzip_all.c" @@ -111,6 +115,9 @@ Files: "lib/posix/*.nim" Files: "lib/js/*.nim" Files: "lib/packages/docutils/*.nim" +Files: "lib/deprecated/core/*.nim" +Files: "lib/deprecated/pure/*.nim" +Files: "lib/deprecated/pure/*.cfg" [Other] Files: "examples/*.nim" @@ -203,7 +210,11 @@ Files: "tests/stdlib/*.nim" Files: "tests/system/*.nim" Files: "tests/template/*.nim" Files: "tests/testament/*.nim" -Files: "tests/testdata/*.nim" +Files: "tests/testdata/*.csv" +Files: "tests/testdata/*.html" +Files: "tests/testdata/*.json" +Files: "tests/testdata/*.txt" +Files: "tests/testdata/*.xml" Files: "tests/threads/*.nim" Files: "tests/threads/*.cfg" Files: "tests/trmacros/*.nim" @@ -236,6 +247,7 @@ BinPath: r"bin;dist\mingw\bin;dist" ; Section | dir | zipFile | size hint (in KB) | url | exe start menu entry Download: r"Documentation|doc|docs.zip|13824|http://nim-lang.org/download/docs-${version}.zip|overview.html" Download: r"C Compiler (MingW)|dist|mingw.zip|82944|http://nim-lang.org/download/${mingw}.zip" +Download: r"Support DLL's|bin|nim_dlls.zip|479|http://nim-lang.org/download/dlls.zip" Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.3.0.zip|aporia-0.3.0\bin\aporia.exe" ; for now only NSIS supports optional downloads diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 704713243..f8bf35ed6 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -30,8 +30,8 @@ implements the required case distinction. import - ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, - options, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, os, + ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options, + nversion, nimsets, msgs, securehash, bitsets, idents, lists, types, os, times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils, intsets, cgmeth, lowerings @@ -136,7 +136,7 @@ proc mapType(typ: PType): TJSTypeKind = result = etyBaseIndex of tyPointer: # treat a tyPointer like a typed pointer to an array of bytes - result = etyInt + result = etyBaseIndex of tyRange, tyDistinct, tyOrdinal, tyConst, tyMutable, tyIter, tyProxy: result = mapType(t.sons[0]) of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt @@ -197,10 +197,10 @@ proc isSimpleExpr(n: PNode): bool = elif n.isAtom: result = true -proc getTemp(p: PProc): Rope = +proc getTemp(p: PProc, defineInLocals: bool = true): Rope = inc(p.unique) result = "Tmp$1" % [rope(p.unique)] - addf(p.locals, "var $1;$n" | "local $1;$n", [result]) + if defineInLocals: addf(p.locals, "var $1;$n" | "local $1;$n", [result]) proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) = assert r.kind == resNone @@ -258,11 +258,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI ["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI ["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI - ["addInt64", "", "addInt64($1, $2)", "($1 + $2)"], # AddI64 - ["subInt64", "", "subInt64($1, $2)", "($1 - $2)"], # SubI64 - ["mulInt64", "", "mulInt64($1, $2)", "($1 * $2)"], # MulI64 - ["divInt64", "", "divInt64($1, $2)", "Math.floor($1 / $2)"], # DivI64 - ["modInt64", "", "modInt64($1, $2)", "Math.floor($1 % $2)"], # ModI64 ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred ["", "", "($1 + $2)", "($1 + $2)"], # AddF64 @@ -276,11 +271,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI - ["", "", "($1 >>> $2)", "($1 >>> $2)"], # ShrI64 - ["", "", "($1 << $2)", "($1 << $2)"], # ShlI64 - ["", "", "($1 & $2)", "($1 & $2)"], # BitandI64 - ["", "", "($1 | $2)", "($1 | $2)"], # BitorI64 - ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI64 ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64 ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64 ["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU @@ -291,9 +281,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["", "", "($1 == $2)", "($1 == $2)"], # EqI ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI ["", "", "($1 < $2)", "($1 < $2)"], # LtI - ["", "", "($1 == $2)", "($1 == $2)"], # EqI64 - ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI64 - ["", "", "($1 < $2)", "($1 < $2)"], # LtI64 ["", "", "($1 == $2)", "($1 == $2)"], # EqF64 ["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64 ["", "", "($1 < $2)", "($1 < $2)"], # LtF64 @@ -320,11 +307,9 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64 ["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI - ["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64 ["", "", "!($1)", "!($1)"], # Not ["", "", "+($1)", "+($1)"], # UnaryPlusI ["", "", "~($1)", "~($1)"], # BitnotI - ["", "", "~($1)", "~($1)"], # BitnotI64 ["", "", "+($1)", "+($1)"], # UnaryPlusF64 ["", "", "-($1)", "-($1)"], # UnaryMinusF64 ["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64 @@ -357,11 +342,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI ["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI ["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI - ["addInt64", "", "addInt64($1, $2)", "($1 + $2)"], # AddI64 - ["subInt64", "", "subInt64($1, $2)", "($1 - $2)"], # SubI64 - ["mulInt64", "", "mulInt64($1, $2)", "($1 * $2)"], # MulI64 - ["divInt64", "", "divInt64($1, $2)", "Math.floor($1 / $2)"], # DivI64 - ["modInt64", "", "modInt64($1, $2)", "Math.floor($1 % $2)"], # ModI64 ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred ["", "", "($1 + $2)", "($1 + $2)"], # AddF64 @@ -375,11 +355,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI - ["", "", "($1 >>> $2)", "($1 >>> $2)"], # ShrI64 - ["", "", "($1 << $2)", "($1 << $2)"], # ShlI64 - ["", "", "($1 & $2)", "($1 & $2)"], # BitandI64 - ["", "", "($1 | $2)", "($1 | $2)"], # BitorI64 - ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI64 ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64 ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64 ["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU @@ -390,9 +365,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["", "", "($1 == $2)", "($1 == $2)"], # EqI ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI ["", "", "($1 < $2)", "($1 < $2)"], # LtI - ["", "", "($1 == $2)", "($1 == $2)"], # EqI64 - ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI64 - ["", "", "($1 < $2)", "($1 < $2)"], # LtI64 ["", "", "($1 == $2)", "($1 == $2)"], # EqF64 ["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64 ["", "", "($1 < $2)", "($1 < $2)"], # LtF64 @@ -419,11 +391,9 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64 ["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI - ["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64 ["", "", "not ($1)", "not ($1)"], # Not ["", "", "+($1)", "+($1)"], # UnaryPlusI ["", "", "~($1)", "~($1)"], # BitnotI - ["", "", "~($1)", "~($1)"], # BitnotI64 ["", "", "+($1)", "+($1)"], # UnaryPlusF64 ["", "", "-($1)", "-($1)"], # UnaryMinusF64 ["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64 @@ -535,12 +505,12 @@ proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) = proc genTry(p: PProc, n: PNode, r: var TCompRes) = # code to generate: # - # var sp = {prev: excHandler, exc: null}; - # excHandler = sp; + # ++excHandler; # try { # stmts; - # TMP = e - # } catch (e) { + # } catch (EXC) { + # var prevJSError = lastJSError; lastJSError = EXC; + # --excHandler; # if (e.typ && e.typ == NTI433 || e.typ == NTI2321) { # stmts; # } else if (e.typ && e.typ == NTI32342) { @@ -548,35 +518,41 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = # } else { # stmts; # } + # lastJSError = prevJSError; # } finally { # stmts; - # excHandler = excHandler.prev; # } genLineDir(p, n) if not isEmptyType(n.typ): r.kind = resVal r.res = getTemp(p) inc(p.unique) + var i = 1 + var length = sonsLen(n) + var catchBranchesExist = length > 1 and n.sons[i].kind == nkExceptBranch + if catchBranchesExist: + add(p.body, "++excHandler;" & tnl) var safePoint = "Tmp$1" % [rope(p.unique)] addf(p.body, - "var $1 = {prev: excHandler, exc: null};$nexcHandler = $1;$n" | + "" | "local $1 = pcall(", [safePoint]) if optStackTrace in p.options: add(p.body, "framePtr = F;" & tnl) addf(p.body, "try {$n" | "function()$n", []) - var length = sonsLen(n) var a: TCompRes gen(p, n.sons[0], a) moveInto(p, a, r) - var i = 1 - if p.target == targetJS and length > 1 and n.sons[i].kind == nkExceptBranch: - addf(p.body, "} catch (EXC) {$n lastJSError = EXC;$n", []) + var generalCatchBranchExists = false + if p.target == targetJS and catchBranchesExist: + addf(p.body, "} catch (EXC) {$n var prevJSError = lastJSError;$n" & + " lastJSError = EXC;$n --excHandler;$n", []) elif p.target == targetLua: addf(p.body, "end)$n", []) while i < length and n.sons[i].kind == nkExceptBranch: let blen = sonsLen(n.sons[i]) if blen == 1: # general except section: + generalCatchBranchExists = true if i > 1: addf(p.body, "else {$n" | "else$n", []) gen(p, n.sons[i].sons[0], a) moveInto(p, a, r) @@ -588,17 +564,22 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = if n.sons[i].sons[j].kind != nkType: internalError(n.info, "genTryStmt") if orExpr != nil: add(orExpr, "||" | " or ") - addf(orExpr, "isObj($1.exc.m_type, $2)", - [safePoint, genTypeInfo(p, n.sons[i].sons[j].typ)]) + addf(orExpr, "isObj(lastJSError.m_type, $1)", + [genTypeInfo(p, n.sons[i].sons[j].typ)]) if i > 1: add(p.body, "else ") - addf(p.body, "if ($1.exc && ($2)) {$n" | "if $1.exc and ($2) then$n", + addf(p.body, "if (lastJSError && ($2)) {$n" | "if $1.exc and ($2) then$n", [safePoint, orExpr]) gen(p, n.sons[i].sons[blen - 1], a) moveInto(p, a, r) addf(p.body, "}$n" | "end$n", []) inc(i) if p.target == targetJS: - add(p.body, "} finally {" & tnl & "excHandler = excHandler.prev;" & tnl) + if catchBranchesExist: + if not generalCatchBranchExists: + useMagic(p, "reraiseException") + add(p.body, "else {" & tnl & "reraiseException();" & tnl & "}" & tnl) + add(p.body, "lastJSError = prevJSError;" & tnl) + add(p.body, "} finally {" & tnl) if i < length and n.sons[i].kind == nkFinally: genStmt(p, n.sons[i].sons[0]) if p.target == targetJS: @@ -748,14 +729,13 @@ proc genBreakStmt(p: PProc, n: PNode) = p.blocks[idx].id = abs(p.blocks[idx].id) # label is used addf(p.body, "break L$1;$n" | "goto ::L$1::;$n", [rope(p.blocks[idx].id)]) -proc genAsmStmt(p: PProc, n: PNode) = +proc genAsmOrEmitStmt(p: PProc, n: PNode) = genLineDir(p, n) - assert(n.kind == nkAsmStmt) for i in countup(0, sonsLen(n) - 1): case n.sons[i].kind of nkStrLit..nkTripleStrLit: add(p.body, n.sons[i].strVal) of nkSym: add(p.body, mangleName(n.sons[i].sym)) - else: internalError(n.sons[i].info, "jsgen: genAsmStmt()") + else: internalError(n.sons[i].info, "jsgen: genAsmOrEmitStmt()") proc genIf(p: PProc, n: PNode, r: var TCompRes) = var cond, stmt: TCompRes @@ -810,19 +790,36 @@ proc needsNoCopy(y: PNode): bool = proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = var a, b: TCompRes gen(p, x, a) + + let xtyp = mapType(x.typ) + + if x.kind == nkHiddenDeref and x.sons[0].kind == nkCall and xtyp != etyObject: + gen(p, x.sons[0], a) + let tmp = p.getTemp(false) + addf(p.body, "var $1 = $2;$n", [tmp, a.rdLoc]) + a.res = "$1[0][$1[1]]" % [tmp] + else: + gen(p, x, a) + gen(p, y, b) - case mapType(x.typ) + + case xtyp of etyObject: - if needsNoCopy(y) or noCopyNeeded: + if (needsNoCopy(y) and needsNoCopy(x)) or noCopyNeeded: addf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: useMagic(p, "nimCopy") - addf(p.body, "$1 = nimCopy($2, $3);$n", + addf(p.body, "nimCopy($1, $2, $3);$n", [a.res, b.res, genTypeInfo(p, y.typ)]) of etyBaseIndex: if a.typ != etyBaseIndex or b.typ != etyBaseIndex: - internalError(x.info, "genAsgn") - addf(p.body, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) + if y.kind == nkCall: + let tmp = p.getTemp(false) + addf(p.body, "var $1 = $4; $2 = $1[0]; $3 = $1[1];$n", [tmp, a.address, a.res, b.rdLoc]) + else: + internalError(x.info, "genAsgn") + else: + addf(p.body, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) else: addf(p.body, "$1 = $2;$n", [a.res, b.res]) @@ -838,11 +835,9 @@ proc genSwap(p: PProc, n: PNode) = var a, b: TCompRes gen(p, n.sons[1], a) gen(p, n.sons[2], b) - inc(p.unique) - var tmp = "Tmp$1" % [rope(p.unique)] + var tmp = p.getTemp(false) if mapType(skipTypes(n.sons[1].typ, abstractVar)) == etyBaseIndex: - inc(p.unique) - let tmp2 = "Tmp$1" % [rope(p.unique)] + let tmp2 = p.getTemp(false) if a.typ != etyBaseIndex or b.typ != etyBaseIndex: internalError(n.info, "genSwap") addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;$n" | @@ -880,7 +875,7 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = if skipTypes(n.sons[0].typ, abstractVarRange).kind == tyTuple: r.res = "$1.Field$2" % [r.res, getFieldPosition(n.sons[1]).rope] else: - if n.sons[1].kind != nkSym: internalError(n.sons[1].info, "genFieldAddr") + if n.sons[1].kind != nkSym: internalError(n.sons[1].info, "genFieldAccess") var f = n.sons[1].sym if f.loc.r == nil: f.loc.r = mangleName(f) r.res = "$1.$2" % [r.res, f.loc.r] @@ -970,18 +965,35 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) = of nkCheckedFieldExpr: genCheckedFieldAddr(p, n, r) of nkDotExpr: - genFieldAddr(p, n.sons[0], r) + if mapType(n.typ) == etyBaseIndex: + genFieldAddr(p, n.sons[0], r) + else: + genFieldAccess(p, n.sons[0], r) of nkBracketExpr: var ty = skipTypes(n.sons[0].typ, abstractVarRange) - if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) - case ty.kind - of tyArray, tyArrayConstr, tyOpenArray, tySequence, tyString, tyCString, - tyVarargs, tyChar: - genArrayAddr(p, n.sons[0], r) - of tyTuple: - genFieldAddr(p, n.sons[0], r) - else: internalError(n.sons[0].info, "expr(nkBracketExpr, " & $ty.kind & ')') - else: internalError(n.sons[0].info, "genAddr") + if ty.kind in MappedToObject: + gen(p, n.sons[0], r) + else: + let kindOfIndexedExpr = skipTypes(n.sons[0].sons[0].typ, abstractVarRange).kind + case kindOfIndexedExpr + of tyArray, tyArrayConstr, tyOpenArray, tySequence, tyString, tyCString, + tyVarargs: + genArrayAddr(p, n.sons[0], r) + of tyTuple: + genFieldAddr(p, n.sons[0], r) + else: internalError(n.sons[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')') + of nkObjDownConv: + gen(p, n.sons[0], r) + else: internalError(n.sons[0].info, "genAddr: " & $n.sons[0].kind) + +proc genProcForSymIfNeeded(p: PProc, s: PSym) = + if not p.g.generatedSyms.containsOrIncl(s.id): + let newp = genProc(p, s) + var owner = p + while owner != nil and owner.prc != s.owner: + owner = owner.up + if owner != nil: add(owner.locals, newp) + else: add(p.g.code, newp) proc genSym(p: PProc, n: PNode, r: var TCompRes) = var s = n.sym @@ -1018,13 +1030,8 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = discard elif sfForward in s.flags: p.g.forwarded.add(s) - elif not p.g.generatedSyms.containsOrIncl(s.id): - let newp = genProc(p, s) - var owner = p - while owner != nil and owner.prc != s.owner: - owner = owner.up - if owner != nil: add(owner.locals, newp) - else: add(p.g.code, newp) + else: + genProcForSymIfNeeded(p, s) else: if s.loc.r == nil: internalError(n.info, "symbol has no generated name: " & s.name.s) @@ -1037,10 +1044,15 @@ proc genDeref(p: PProc, n: PNode, r: var TCompRes) = else: var a: TCompRes gen(p, n.sons[0], a) - if a.typ != etyBaseIndex: internalError(n.info, "genDeref") - r.res = "$1[$2]" % [a.address, a.res] + if a.typ == etyBaseIndex: + r.res = "$1[$2]" % [a.address, a.res] + elif n.sons[0].kind == nkCall: + let tmp = p.getTemp + r.res = "($1 = $2, $1[0][$1[1]])" % [tmp, a.res] + else: + internalError(n.info, "genDeref") -proc genArg(p: PProc, n: PNode, r: var TCompRes) = +proc genArgNoParam(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes gen(p, n, a) if a.typ == etyBaseIndex: @@ -1050,13 +1062,42 @@ proc genArg(p: PProc, n: PNode, r: var TCompRes) = else: add(r.res, a.res) +proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes) = + var a: TCompRes + gen(p, n, a) + if skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs} and + a.typ == etyBaseIndex: + add(r.res, "$1[$2]" % [a.address, a.res]) + elif a.typ == etyBaseIndex: + add(r.res, a.address) + add(r.res, ", ") + add(r.res, a.res) + else: + add(r.res, a.res) + + proc genArgs(p: PProc, n: PNode, r: var TCompRes) = add(r.res, "(") + var hasArgs = false + + var typ = skipTypes(n.sons[0].typ, abstractInst) + assert(typ.kind == tyProc) + assert(sonsLen(typ) == sonsLen(typ.n)) + for i in countup(1, sonsLen(n) - 1): let it = n.sons[i] - if it.typ.isCompileTimeOnly: continue - if i > 1: add(r.res, ", ") - genArg(p, it, r) + var paramType : PNode = nil + if i < sonsLen(typ): + assert(typ.n.sons[i].kind == nkSym) + paramType = typ.n.sons[i] + if paramType.typ.isCompileTimeOnly: continue + + if hasArgs: add(r.res, ", ") + if paramType.isNil: + genArgNoParam(p, it, r) + else: + genArg(p, it, paramType.sym, r) + hasArgs = true add(r.res, ")") r.kind = resExpr @@ -1080,11 +1121,12 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = add(r.res, "(") for i in countup(2, sonsLen(n) - 1): if i > 2: add(r.res, ", ") - genArg(p, n.sons[i], r) + genArgNoParam(p, n.sons[i], r) add(r.res, ")") r.kind = resExpr proc genEcho(p: PProc, n: PNode, r: var TCompRes) = + useMagic(p, "toJSStr") # Used in rawEcho useMagic(p, "rawEcho") add(r.res, "rawEcho(") let n = n[1].skipConv @@ -1093,7 +1135,7 @@ proc genEcho(p: PProc, n: PNode, r: var TCompRes) = let it = n.sons[i] if it.typ.isCompileTimeOnly: continue if i > 0: add(r.res, ", ") - genArg(p, it, r) + genArgNoParam(p, it, r) add(r.res, ")") r.kind = resExpr @@ -1102,24 +1144,32 @@ proc putToSeq(s: string, indirect: bool): Rope = if indirect: result = "[$1]" % [result] proc createVar(p: PProc, typ: PType, indirect: bool): Rope -proc createRecordVarAux(p: PProc, rec: PNode, c: var int): Rope = - result = nil +proc createRecordVarAux(p: PProc, rec: PNode, excludedFieldIDs: IntSet, output: var Rope) = case rec.kind of nkRecList: for i in countup(0, sonsLen(rec) - 1): - add(result, createRecordVarAux(p, rec.sons[i], c)) + createRecordVarAux(p, rec.sons[i], excludedFieldIDs, output) of nkRecCase: - add(result, createRecordVarAux(p, rec.sons[0], c)) + createRecordVarAux(p, rec.sons[0], excludedFieldIDs, output) for i in countup(1, sonsLen(rec) - 1): - add(result, createRecordVarAux(p, lastSon(rec.sons[i]), c)) + createRecordVarAux(p, lastSon(rec.sons[i]), excludedFieldIDs, output) of nkSym: - if c > 0: add(result, ", ") - add(result, mangleName(rec.sym)) - add(result, ": ") - add(result, createVar(p, rec.sym.typ, false)) - inc(c) + if rec.sym.id notin excludedFieldIDs: + if output.len > 0: output.add(", ") + output.add(mangleName(rec.sym)) + output.add(": ") + output.add(createVar(p, rec.sym.typ, false)) else: internalError(rec.info, "createRecordVarAux") +proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: var Rope) = + var t = typ + if tfFinal notin t.flags or t.sons[0] != nil: + if output.len > 0: output.add(", ") + addf(output, "m_type: $1" | "m_type = $#", [genTypeInfo(p, t)]) + while t != nil: + createRecordVarAux(p, t.n, excludedFieldIDs, output) + t = t.sons[0] + proc createVar(p: PProc, typ: PType, indirect: bool): Rope = var t = skipTypes(typ, abstractInst) case t.kind @@ -1160,15 +1210,9 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = add(result, "}") if indirect: result = "[$1]" % [result] of tyObject: - result = rope("{") - var c = 0 - if tfFinal notin t.flags or t.sons[0] != nil: - inc(c) - addf(result, "m_type: $1" | "m_type = $#", [genTypeInfo(p, t)]) - while t != nil: - add(result, createRecordVarAux(p, t.n, c)) - t = t.sons[0] - add(result, "}") + var initList : Rope + createObjInitList(p, t, initIntSet(), initList) + result = "{$1}" % [initList] if indirect: result = "[$1]" % [result] of tyVar, tyPtr, tyRef: if mapType(t) == etyBaseIndex: @@ -1197,7 +1241,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = s = a.res else: useMagic(p, "nimCopy") - s = "nimCopy($1, $2)" % [a.res, genTypeInfo(p, n.typ)] + s = "nimCopy(null, $1, $2)" % [a.res, genTypeInfo(p, n.typ)] of etyBaseIndex: if (a.typ != etyBaseIndex): internalError(n.info, "genVarInit") if {sfAddrTaken, sfGlobal} * v.flags != {}: @@ -1389,6 +1433,12 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mCopyStrLast: ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))") of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)") of mNewStringOfCap: unaryExpr(p, n, r, "mnewString", "mnewString(0)") + of mDotDot: + genProcForSymIfNeeded(p, n.sons[0].sym) + genCall(p, n, r) + of mParseBiggestFloat: + useMagic(p, "nimParseBiggestFloat") + genCall(p, n, r) else: genCall(p, n, r) #else internalError(e.info, 'genMagic: ' + magicToStr[op]); @@ -1434,19 +1484,22 @@ proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = r.res.add("}") proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = - # XXX inheritance? var a: TCompRes - r.res = rope("{") r.kind = resExpr + var initList : Rope + var fieldIDs = initIntSet() for i in countup(1, sonsLen(n) - 1): - if i > 1: add(r.res, ", ") + if i > 1: add(initList, ", ") var it = n.sons[i] internalAssert it.kind == nkExprColonExpr gen(p, it.sons[1], a) var f = it.sons[0].sym if f.loc.r == nil: f.loc.r = mangleName(f) - addf(r.res, "$#: $#" | "$# = $#" , [f.loc.r, a.res]) - r.res.add("}") + fieldIDs.incl(f.id) + addf(initList, "$#: $#" | "$# = $#" , [f.loc.r, a.res]) + let t = skipTypes(n.typ, abstractInst + skipPtrs) + createObjInitList(p, t, fieldIDs, initList) + r.res = "{$1}" % [initList] proc genConv(p: PProc, n: PNode, r: var TCompRes) = var dest = skipTypes(n.typ, abstractVarRange) @@ -1551,7 +1604,10 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = mangleName(resultSym), createVar(p, resultSym.typ, isIndirect(resultSym))] gen(p, prc.ast.sons[resultPos], a) - returnStmt = "return $#;$n" % [a.res] + if mapType(resultSym.typ) == etyBaseIndex: + returnStmt = "return [$#, $#];$n" % [a.address, a.res] + else: + returnStmt = "return $#;$n" % [a.res] genStmt(p, prc.getBody) result = ("function $#($#) {$n$#$#$#$#}$n" | "function $#($#) $n$#$#$#$#$nend$n") % @@ -1565,6 +1621,12 @@ proc genStmt(p: PProc, n: PNode) = gen(p, n, r) if r.res != nil: addf(p.body, "$#;$n", [r.res]) +proc genPragma(p: PProc, n: PNode) = + for it in n.sons: + case whichPragma(it) + of wEmit: genAsmOrEmitStmt(p, it.sons[1]) + else: discard + proc gen(p: PProc, n: PNode, r: var TCompRes) = r.typ = etyNone r.kind = resNone @@ -1648,6 +1710,9 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = gen(p, lastSon(n), r) of nkBlockStmt, nkBlockExpr: genBlock(p, n, r) of nkIfStmt, nkIfExpr: genIf(p, n, r) + of nkWhen: + # This is "when nimvm" node + gen(p, n.sons[1].sons[0], r) of nkWhileStmt: genWhileStmt(p, n) of nkVarSection, nkLetSection: genVarStmt(p, n) of nkConstSection: discard @@ -1664,12 +1729,13 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = if n.sons[0].kind != nkEmpty: genLineDir(p, n) gen(p, n.sons[0], r) - of nkAsmStmt: genAsmStmt(p, n) + of nkAsmStmt: genAsmOrEmitStmt(p, n) of nkTryStmt: genTry(p, n, r) of nkRaiseStmt: genRaiseStmt(p, n) of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt, nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, - nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: discard + nkFromStmt, nkTemplateDef, nkMacroDef: discard + of nkPragma: genPragma(p, n) of nkProcDef, nkMethodDef, nkConverterDef: var s = n.sons[namePos].sym if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}: @@ -1691,7 +1757,7 @@ proc genHeader(): Rope = result = ("/* Generated by the Nim Compiler v$1 */$n" & "/* (c) 2015 Andreas Rumpf */$n$n" & "var framePtr = null;$n" & - "var excHandler = null;$n" & + "var excHandler = 0;$n" & "var lastJSError = null;$n") % [rope(VersionAsString)] diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim index 851938327..367c173ea 100644 --- a/compiler/jstypes.nim +++ b/compiler/jstypes.nim @@ -116,13 +116,12 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) = [name, genTypeInfo(p, typ.sons[0])]) proc genTypeInfo(p: PProc, typ: PType): Rope = - var t = typ - if t.kind == tyGenericInst: t = lastSon(t) + let t = typ.skipTypes({tyGenericInst}) result = "NTI$1" % [rope(t.id)] if containsOrIncl(p.g.typeInfoGenerated, t.id): return case t.kind of tyDistinct: - result = genTypeInfo(p, typ.sons[0]) + result = genTypeInfo(p, t.sons[0]) of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64: var s = "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" % @@ -134,14 +133,14 @@ proc genTypeInfo(p: PProc, typ: PType): Rope = [result, rope(ord(t.kind))] prepend(p.g.typeInfo, s) addf(p.g.typeInfo, "$1.base = $2;$n", - [result, genTypeInfo(p, typ.lastSon)]) + [result, genTypeInfo(p, t.lastSon)]) of tyArrayConstr, tyArray: var s = "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" % [result, rope(ord(t.kind))] prepend(p.g.typeInfo, s) addf(p.g.typeInfo, "$1.base = $2;$n", - [result, genTypeInfo(p, typ.sons[1])]) + [result, genTypeInfo(p, t.sons[1])]) of tyEnum: genEnumInfo(p, t, result) of tyObject: genObjectInfo(p, t, result) of tyTuple: genTupleInfo(p, t, result) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index c68bc352c..cccc94756 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -184,7 +184,7 @@ proc addHiddenParam(routine: PSym, param: PSym) = var params = routine.ast.sons[paramsPos] # -1 is correct here as param.position is 0 based but we have at position 0 # some nkEffect node: - param.position = params.len-1 + param.position = routine.typ.n.len-1 addSon(params, newSymNode(param)) incl(routine.typ.flags, tfCapturesEnv) assert sfFromGeneric in param.flags @@ -859,11 +859,17 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode = return indirectAccess(newSymNode(it.closureParam), local, n.info) if local.kind == skClosureIterator: + # bug #3354; allow for + #iterator iter(): int {.closure.}= + # s.add(iter) + # yield 1 + + #if local == o.fn or local == it.fn: + # message(n.info, errRecursiveDependencyX, local.name.s) + # consider: [i1, i2, i1] Since we merged the iterator's closure # with the captured owning variables, we need to generate the # closure generation code again: - if local == o.fn or local == it.fn: - message(n.info, errRecursiveDependencyX, local.name.s) # XXX why doesn't this work? var closure = PEnv(idTableGet(o.lambdasToEnv, local)) if closure.isNil: @@ -946,7 +952,11 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode = proc liftLambdas*(fn: PSym, body: PNode): PNode = # XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs # the transformation even when compiling to JS ... - if body.kind == nkEmpty or gCmd == cmdCompileToJS or + + # However we can do lifting for the stuff which is *only* compiletime. + let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro + + if body.kind == nkEmpty or (gCmd == cmdCompileToJS and not isCompileTime) or fn.skipGenericOwner.kind != skModule: # ignore forward declaration: result = body @@ -1012,7 +1022,9 @@ proc liftForLoop*(body: PNode): PNode = ... """ var L = body.len - internalAssert body.kind == nkForStmt and body[L-2].kind in nkCallKinds + if not (body.kind == nkForStmt and body[L-2].kind in nkCallKinds): + localError(body.info, "ignored invalid for loop") + return body var call = body[L-2] result = newNodeI(nkStmtList, body.info) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 8080e0e8c..cea42ad1e 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -17,7 +17,7 @@ import hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream, - wordrecg + wordrecg, etcpriv const MaxLineLength* = 80 # lines longer than this lead to a warning @@ -140,10 +140,12 @@ proc isKeyword*(kind: TTokType): bool = proc isNimIdentifier*(s: string): bool = if s[0] in SymStartChars: var i = 1 - while i < s.len: + var sLen = s.len + while i < sLen: if s[i] == '_': inc(i) - if s[i] notin SymChars: return + elif isMagicIdentSeparatorRune(cstring s, i): + inc(i, magicIdentSeparatorRuneByteWidth) if s[i] notin SymChars: return inc(i) result = true @@ -229,23 +231,6 @@ proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") = var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart) L.dispMessage(info, msg, arg) -proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]) = - var pos = L.bufpos # use registers for pos, buf - var buf = L.buf - while true: - if buf[pos] in chars: - add(tok.literal, buf[pos]) - inc(pos) - else: - break - if buf[pos] == '_': - if buf[pos+1] notin chars: - lexMessage(L, errInvalidToken, "_") - break - add(tok.literal, '_') - inc(pos) - L.bufpos = pos - proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool = result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in second) @@ -268,136 +253,200 @@ proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int = result = i - start {.pop.} # overflowChecks + +template eatChar(L: var TLexer, t: var TToken, replacementChar: char) = + add(t.literal, replacementChar) + inc(L.bufpos) + +template eatChar(L: var TLexer, t: var TToken) = + add(t.literal, L.buf[L.bufpos]) + inc(L.bufpos) + proc getNumber(L: var TLexer): TToken = + proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]) = + var pos = L.bufpos # use registers for pos, buf + var buf = L.buf + while true: + if buf[pos] in chars: + add(tok.literal, buf[pos]) + inc(pos) + else: + break + if buf[pos] == '_': + if buf[pos+1] notin chars: + lexMessage(L, errInvalidToken, "_") + break + add(tok.literal, '_') + inc(pos) + L.bufpos = pos + + proc matchChars(L: var TLexer, tok: var TToken, chars: set[char]) = + var pos = L.bufpos # use registers for pos, buf + var buf = L.buf + while buf[pos] in chars: + add(tok.literal, buf[pos]) + inc(pos) + L.bufpos = pos + + proc lexMessageLitNum(L: var TLexer, msg: TMsgKind, startpos: int) = + # Used to get slightly human friendlier err messages. + # Note: the erroneous 'O' char in the character set is intentional + const literalishChars = {'A'..'F', 'a'..'f', '0'..'9', 'X', 'x', 'o', 'O', + 'c', 'C', 'b', 'B', '_', '.', '\'', 'd', 'i', 'u'} + var msgPos = L.bufpos + var t: TToken + t.literal = "" + L.bufpos = startpos # Use L.bufpos as pos because of matchChars + matchChars(L, t, literalishChars) + # We must verify +/- specifically so that we're not past the literal + if L.buf[L.bufpos] in {'+', '-'} and + L.buf[L.bufpos - 1] in {'e', 'E'}: + add(t.literal, L.buf[L.bufpos]) + inc(L.bufpos) + matchChars(L, t, literalishChars) + if L.buf[L.bufpos] in {'\'', 'f', 'F', 'd', 'D', 'i', 'I', 'u', 'U'}: + inc(L.bufpos) + add(t.literal, L.buf[L.bufpos]) + matchChars(L, t, {'0'..'9'}) + L.bufpos = msgPos + lexMessage(L, msg, t.literal) + var - pos, endpos: int + startpos, endpos: int xi: BiggestInt - # get the base: + isBase10 = true + const + baseCodeChars = {'X', 'x', 'o', 'c', 'C', 'b', 'B'} + literalishChars = baseCodeChars + {'A'..'F', 'a'..'f', '0'..'9', '_', '\''} + floatTypes = {tkFloatLit, tkFloat32Lit, tkFloat64Lit, tkFloat128Lit} result.tokType = tkIntLit # int literal until we know better result.literal = "" - result.base = base10 # BUGFIX - pos = L.bufpos # make sure the literal is correct for error messages: - var eallowed = false - if L.buf[pos] == '0' and L.buf[pos+1] in {'X', 'x'}: - matchUnderscoreChars(L, result, {'A'..'F', 'a'..'f', '0'..'9', 'X', 'x'}) + result.base = base10 + startpos = L.bufpos + + # First stage: find out base, make verifications, build token literal string + if L.buf[L.bufpos] == '0' and L.buf[L.bufpos + 1] in baseCodeChars + {'O'}: + isBase10 = false + eatChar(L, result, '0') + case L.buf[L.bufpos] + of 'O': + lexMessageLitNum(L, errInvalidNumberOctalCode, startpos) + of 'x', 'X': + eatChar(L, result, 'x') + matchUnderscoreChars(L, result, {'0'..'9', 'a'..'f', 'A'..'F'}) + of 'o', 'c', 'C': + eatChar(L, result, 'c') + matchUnderscoreChars(L, result, {'0'..'7'}) + of 'b', 'B': + eatChar(L, result, 'b') + matchUnderscoreChars(L, result, {'0'..'1'}) + else: + internalError(getLineInfo(L), "getNumber") else: - matchUnderscoreChars(L, result, {'0'..'9', 'b', 'B', 'o', 'c', 'C'}) - eallowed = true - if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}): - add(result.literal, '.') - inc(L.bufpos) - matchUnderscoreChars(L, result, {'0'..'9'}) - eallowed = true - if eallowed and L.buf[L.bufpos] in {'e', 'E'}: - add(result.literal, 'e') - inc(L.bufpos) - if L.buf[L.bufpos] in {'+', '-'}: - add(result.literal, L.buf[L.bufpos]) - inc(L.bufpos) matchUnderscoreChars(L, result, {'0'..'9'}) + if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}): + result.tokType = tkFloat64Lit + eatChar(L, result, '.') + matchUnderscoreChars(L, result, {'0'..'9'}) + if L.buf[L.bufpos] in {'e', 'E'}: + result.tokType = tkFloat64Lit + eatChar(L, result, 'e') + if L.buf[L.bufpos] in {'+', '-'}: + eatChar(L, result) + matchUnderscoreChars(L, result, {'0'..'9'}) endpos = L.bufpos - if L.buf[endpos] in {'\'', 'f', 'F', 'i', 'I', 'u', 'U'}: - if L.buf[endpos] == '\'': inc(endpos) - L.bufpos = pos # restore position - case L.buf[endpos] + + # Second stage, find out if there's a datatype suffix and handle it + var postPos = endpos + if L.buf[postPos] in {'\'', 'f', 'F', 'd', 'D', 'i', 'I', 'u', 'U'}: + if L.buf[postPos] == '\'': + inc(postPos) + + case L.buf[postPos] of 'f', 'F': - inc(endpos) - if (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'): + inc(postPos) + if (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'): result.tokType = tkFloat32Lit - inc(endpos, 2) - elif (L.buf[endpos] == '6') and (L.buf[endpos + 1] == '4'): + inc(postPos, 2) + elif (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'): result.tokType = tkFloat64Lit - inc(endpos, 2) - elif (L.buf[endpos] == '1') and - (L.buf[endpos + 1] == '2') and - (L.buf[endpos + 2] == '8'): + inc(postPos, 2) + elif (L.buf[postPos] == '1') and + (L.buf[postPos + 1] == '2') and + (L.buf[postPos + 2] == '8'): result.tokType = tkFloat128Lit - inc(endpos, 3) - else: - lexMessage(L, errInvalidNumber, result.literal & "'f" & L.buf[endpos]) + inc(postPos, 3) + else: # "f" alone defaults to float32 + result.tokType = tkFloat32Lit + of 'd', 'D': # ad hoc convenience shortcut for f64 + inc(postPos) + result.tokType = tkFloat64Lit of 'i', 'I': - inc(endpos) - if (L.buf[endpos] == '6') and (L.buf[endpos + 1] == '4'): + inc(postPos) + if (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'): result.tokType = tkInt64Lit - inc(endpos, 2) - elif (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'): + inc(postPos, 2) + elif (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'): result.tokType = tkInt32Lit - inc(endpos, 2) - elif (L.buf[endpos] == '1') and (L.buf[endpos + 1] == '6'): + inc(postPos, 2) + elif (L.buf[postPos] == '1') and (L.buf[postPos + 1] == '6'): result.tokType = tkInt16Lit - inc(endpos, 2) - elif (L.buf[endpos] == '8'): + inc(postPos, 2) + elif (L.buf[postPos] == '8'): result.tokType = tkInt8Lit - inc(endpos) + inc(postPos) else: - lexMessage(L, errInvalidNumber, result.literal & "'i" & L.buf[endpos]) + lexMessageLitNum(L, errInvalidNumber, startpos) of 'u', 'U': - inc(endpos) - if (L.buf[endpos] == '6') and (L.buf[endpos + 1] == '4'): + inc(postPos) + if (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'): result.tokType = tkUInt64Lit - inc(endpos, 2) - elif (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'): + inc(postPos, 2) + elif (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'): result.tokType = tkUInt32Lit - inc(endpos, 2) - elif (L.buf[endpos] == '1') and (L.buf[endpos + 1] == '6'): + inc(postPos, 2) + elif (L.buf[postPos] == '1') and (L.buf[postPos + 1] == '6'): result.tokType = tkUInt16Lit - inc(endpos, 2) - elif (L.buf[endpos] == '8'): + inc(postPos, 2) + elif (L.buf[postPos] == '8'): result.tokType = tkUInt8Lit - inc(endpos) + inc(postPos) else: result.tokType = tkUIntLit - else: lexMessage(L, errInvalidNumber, result.literal & "'" & L.buf[endpos]) - else: - L.bufpos = pos # restore position + else: + lexMessageLitNum(L, errInvalidNumber, startpos) + + # Is there still a literalish char awaiting? Then it's an error! + if L.buf[postPos] in literalishChars or + (L.buf[postPos] == '.' and L.buf[postPos + 1] in {'0'..'9'}): + lexMessageLitNum(L, errInvalidNumber, startpos) + + # Third stage, extract actual number + L.bufpos = startpos # restore position + var pos: int = startpos try: - if (L.buf[pos] == '0') and - (L.buf[pos + 1] in {'x', 'X', 'b', 'B', 'o', 'O', 'c', 'C'}): + if (L.buf[pos] == '0') and (L.buf[pos + 1] in baseCodeChars): inc(pos, 2) - xi = 0 # it may be a base prefix - case L.buf[pos - 1] # now look at the optional type suffix: + xi = 0 # it is a base prefix + + case L.buf[pos - 1] of 'b', 'B': result.base = base2 - while true: - case L.buf[pos] - of '2'..'9', '.': - lexMessage(L, errInvalidNumber, result.literal) - inc(pos) - of '_': - if L.buf[pos+1] notin {'0'..'1'}: - lexMessage(L, errInvalidToken, "_") - break - inc(pos) - of '0', '1': + while pos < endpos: + if L.buf[pos] != '_': xi = `shl`(xi, 1) or (ord(L.buf[pos]) - ord('0')) - inc(pos) - else: break + inc(pos) of 'o', 'c', 'C': result.base = base8 - while true: - case L.buf[pos] - of '8'..'9', '.': - lexMessage(L, errInvalidNumber, result.literal) - inc(pos) - of '_': - if L.buf[pos+1] notin {'0'..'7'}: - lexMessage(L, errInvalidToken, "_") - break - inc(pos) - of '0'..'7': + while pos < endpos: + if L.buf[pos] != '_': xi = `shl`(xi, 3) or (ord(L.buf[pos]) - ord('0')) - inc(pos) - else: break - of 'O': - lexMessage(L, errInvalidNumber, result.literal) + inc(pos) of 'x', 'X': result.base = base16 - while true: + while pos < endpos: case L.buf[pos] of '_': - if L.buf[pos+1] notin {'0'..'9', 'a'..'f', 'A'..'F'}: - lexMessage(L, errInvalidToken, "_") - break inc(pos) of '0'..'9': xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0')) @@ -408,51 +457,81 @@ proc getNumber(L: var TLexer): TToken = of 'A'..'F': xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10) inc(pos) - else: break - else: internalError(getLineInfo(L), "getNumber") + else: + break + else: + internalError(getLineInfo(L), "getNumber") + case result.tokType of tkIntLit, tkInt64Lit: result.iNumber = xi of tkInt8Lit: result.iNumber = BiggestInt(int8(toU8(int(xi)))) - of tkInt16Lit: result.iNumber = BiggestInt(toU16(int(xi))) - of tkInt32Lit: result.iNumber = BiggestInt(toU32(xi)) + of tkInt16Lit: result.iNumber = BiggestInt(int16(toU16(int(xi)))) + of tkInt32Lit: result.iNumber = BiggestInt(int32(toU32(int64(xi)))) of tkUIntLit, tkUInt64Lit: result.iNumber = xi - of tkUInt8Lit: result.iNumber = BiggestInt(int8(toU8(int(xi)))) - of tkUInt16Lit: result.iNumber = BiggestInt(toU16(int(xi))) - of tkUInt32Lit: result.iNumber = BiggestInt(toU32(xi)) + of tkUInt8Lit: result.iNumber = BiggestInt(uint8(toU8(int(xi)))) + of tkUInt16Lit: result.iNumber = BiggestInt(uint16(toU16(int(xi)))) + of tkUInt32Lit: result.iNumber = BiggestInt(uint32(toU32(int64(xi)))) of tkFloat32Lit: result.fNumber = (cast[PFloat32](addr(xi)))[] # note: this code is endian neutral! # XXX: Test this on big endian machine! of tkFloat64Lit: result.fNumber = (cast[PFloat64](addr(xi)))[] else: internalError(getLineInfo(L), "getNumber") - elif isFloatLiteral(result.literal) or (result.tokType == tkFloat32Lit) or - (result.tokType == tkFloat64Lit): - result.fNumber = parseFloat(result.literal) - if result.tokType == tkIntLit: result.tokType = tkFloatLit - elif result.tokType == tkUint64Lit: - xi = 0 - let len = unsafeParseUInt(result.literal, xi) - if len != result.literal.len or len == 0: - raise newException(ValueError, "invalid integer: " & $xi) - result.iNumber = xi + + # Bounds checks. Non decimal literals are allowed to overflow the range of + # the datatype as long as their pattern don't overflow _bitwise_, hence + # below checks of signed sizes against uint*.high is deliberate: + # (0x80'u8 = 128, 0x80'i8 = -128, etc == OK) + if result.tokType notin floatTypes: + let outOfRange = case result.tokType: + of tkUInt8Lit, tkUInt16Lit, tkUInt32Lit: result.iNumber != xi + of tkInt8Lit: (xi > BiggestInt(uint8.high)) + of tkInt16Lit: (xi > BiggestInt(uint16.high)) + of tkInt32Lit: (xi > BiggestInt(uint32.high)) + else: false + + if outOfRange: + #echo "out of range num: ", result.iNumber, " vs ", xi + lexMessageLitNum(L, errNumberOutOfRange, startpos) + else: - result.iNumber = parseBiggestInt(result.literal) + case result.tokType + of floatTypes: + result.fNumber = parseFloat(result.literal) + of tkUint64Lit: + xi = 0 + let len = unsafeParseUInt(result.literal, xi) + if len != result.literal.len or len == 0: + raise newException(ValueError, "invalid integer: " & $xi) + result.iNumber = xi + else: + result.iNumber = parseBiggestInt(result.literal) + + # Explicit bounds checks + let outOfRange = case result.tokType: + of tkInt8Lit: (result.iNumber < int8.low or result.iNumber > int8.high) + of tkUInt8Lit: (result.iNumber < BiggestInt(uint8.low) or + result.iNumber > BiggestInt(uint8.high)) + of tkInt16Lit: (result.iNumber < int16.low or result.iNumber > int16.high) + of tkUInt16Lit: (result.iNumber < BiggestInt(uint16.low) or + result.iNumber > BiggestInt(uint16.high)) + of tkInt32Lit: (result.iNumber < int32.low or result.iNumber > int32.high) + of tkUInt32Lit: (result.iNumber < BiggestInt(uint32.low) or + result.iNumber > BiggestInt(uint32.high)) + else: false + + if outOfRange: lexMessageLitNum(L, errNumberOutOfRange, startpos) + + # Promote int literal to int64? Not always necessary, but more consistent + if result.tokType == tkIntLit: if (result.iNumber < low(int32)) or (result.iNumber > high(int32)): - if result.tokType == tkIntLit: - result.tokType = tkInt64Lit - elif result.tokType in {tkInt8Lit, tkInt16Lit, tkInt32Lit}: - lexMessage(L, errNumberOutOfRange, result.literal) - elif result.tokType == tkInt8Lit and - (result.iNumber < int8.low or result.iNumber > int8.high): - lexMessage(L, errNumberOutOfRange, result.literal) - elif result.tokType == tkInt16Lit and - (result.iNumber < int16.low or result.iNumber > int16.high): - lexMessage(L, errNumberOutOfRange, result.literal) + result.tokType = tkInt64Lit + except ValueError: - lexMessage(L, errInvalidNumber, result.literal) + lexMessageLitNum(L, errInvalidNumber, startpos) except OverflowError, RangeError: - lexMessage(L, errNumberOutOfRange, result.literal) - L.bufpos = endpos + lexMessageLitNum(L, errNumberOutOfRange, startpos) + L.bufpos = postPos proc handleHexChar(L: var TLexer, xi: var int) = case L.buf[L.bufpos] @@ -625,23 +704,34 @@ proc getCharacter(L: var TLexer, tok: var TToken) = inc(L.bufpos) # skip ' proc getSymbol(L: var TLexer, tok: var TToken) = - var h: THash = 0 + var h: Hash = 0 var pos = L.bufpos var buf = L.buf while true: var c = buf[pos] case c of 'a'..'z', '0'..'9', '\x80'..'\xFF': - h = h !& ord(c) + if c == '\226' and + buf[pos+1] == '\128' and + buf[pos+2] == '\147': # It's a 'magic separator' en-dash Unicode + if buf[pos + magicIdentSeparatorRuneByteWidth] notin SymChars: + lexMessage(L, errInvalidToken, "–") + break + inc(pos, magicIdentSeparatorRuneByteWidth) + else: + h = h !& ord(c) + inc(pos) of 'A'..'Z': c = chr(ord(c) + (ord('a') - ord('A'))) # toLower() h = h !& ord(c) + inc(pos) of '_': if buf[pos+1] notin SymChars: lexMessage(L, errInvalidToken, "_") break + inc(pos) + else: break - inc(pos) h = !$h tok.ident = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h) L.bufpos = pos @@ -652,7 +742,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) = tok.tokType = TTokType(tok.ident.id + ord(tkSymbol)) proc endOperator(L: var TLexer, tok: var TToken, pos: int, - hash: THash) {.inline.} = + hash: Hash) {.inline.} = var h = !$hash tok.ident = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h) if (tok.ident.id < oprLow) or (tok.ident.id > oprHigh): tok.tokType = tkOpr @@ -662,7 +752,7 @@ proc endOperator(L: var TLexer, tok: var TToken, pos: int, proc getOperator(L: var TLexer, tok: var TToken) = var pos = L.bufpos var buf = L.buf - var h: THash = 0 + var h: Hash = 0 while true: var c = buf[pos] if c notin OpChars: break diff --git a/compiler/lists.nim b/compiler/lists.nim index 1b2b91bff..27e373342 100644 --- a/compiler/lists.nim +++ b/compiler/lists.nim @@ -10,7 +10,7 @@ # This module implements a generic doubled linked list. # TODO Remove this and replace it with something sensible import os -type +type PListEntry* = ref TListEntry TListEntry* = object of RootObj prev*, next*: PListEntry @@ -25,68 +25,68 @@ type TCompareProc* = proc (entry: PListEntry, closure: pointer): bool {.nimcall.} -proc initLinkedList*(list: var TLinkedList) = +proc initLinkedList*(list: var TLinkedList) = list.counter = 0 list.head = nil list.tail = nil -proc append*(list: var TLinkedList, entry: PListEntry) = +proc append*(list: var TLinkedList, entry: PListEntry) = inc(list.counter) entry.next = nil entry.prev = list.tail - if list.tail != nil: + if list.tail != nil: assert(list.tail.next == nil) list.tail.next = entry list.tail = entry if list.head == nil: list.head = entry - -proc contains*(list: TLinkedList, data: string): bool = + +proc contains*(list: TLinkedList, data: string): bool = var it = list.head - while it != nil: - if PStrEntry(it).data == data: + while it != nil: + if PStrEntry(it).data == data: return true it = it.next - -proc newStrEntry(data: string): PStrEntry = + +proc newStrEntry(data: string): PStrEntry = new(result) result.data = data -proc appendStr*(list: var TLinkedList, data: string) = +proc appendStr*(list: var TLinkedList, data: string) = append(list, newStrEntry(data)) -proc includeStr*(list: var TLinkedList, data: string): bool = +proc includeStr*(list: var TLinkedList, data: string): bool = if contains(list, data): return true appendStr(list, data) # else: add to list -proc prepend*(list: var TLinkedList, entry: PListEntry) = +proc prepend*(list: var TLinkedList, entry: PListEntry) = inc(list.counter) entry.prev = nil entry.next = list.head - if list.head != nil: + if list.head != nil: assert(list.head.prev == nil) list.head.prev = entry list.head = entry if list.tail == nil: list.tail = entry -proc prependStr*(list: var TLinkedList, data: string) = +proc prependStr*(list: var TLinkedList, data: string) = prepend(list, newStrEntry(data)) -proc insertBefore*(list: var TLinkedList, pos, entry: PListEntry) = +proc insertBefore*(list: var TLinkedList, pos, entry: PListEntry) = assert(pos != nil) - if pos == list.head: + if pos == list.head: prepend(list, entry) - else: + else: inc(list.counter) entry.next = pos entry.prev = pos.prev if pos.prev != nil: pos.prev.next = entry pos.prev = entry - -proc remove*(list: var TLinkedList, entry: PListEntry) = + +proc remove*(list: var TLinkedList, entry: PListEntry) = dec(list.counter) - if entry == list.tail: + if entry == list.tail: list.tail = entry.prev - if entry == list.head: + if entry == list.head: list.head = entry.next if entry.next != nil: entry.next.prev = entry.prev if entry.prev != nil: entry.prev.next = entry.next @@ -112,8 +112,8 @@ proc excludePath*(list: var TLinkedList, data: string) = remove(list, it) it = nxt -proc find*(list: TLinkedList, fn: TCompareProc, closure: pointer): PListEntry = +proc find*(list: TLinkedList, fn: TCompareProc, closure: pointer): PListEntry = result = list.head while result != nil: - if fn(result, closure): return + if fn(result, closure): return result = result.next diff --git a/compiler/llstream.nim b/compiler/llstream.nim index 18ca4aec7..0a1e09fc8 100644 --- a/compiler/llstream.nim +++ b/compiler/llstream.nim @@ -9,13 +9,14 @@ ## Low-level streams for high performance. -import +import strutils -when not defined(windows) and defined(useGnuReadline): +# support '-d:useGnuReadline' for backwards compatibility: +when not defined(windows) and (defined(useGnuReadline) or defined(useLinenoise)): import rdstdin -type +type TLLStreamKind* = enum # enum of different stream implementations llsNone, # null stream: reading and writing has no effect llsString, # stream encapsulates a string @@ -27,42 +28,42 @@ type s*: string rd*, wr*: int # for string streams lineOffset*: int # for fake stdin line numbers - + PLLStream* = ref TLLStream -proc llStreamOpen*(data: string): PLLStream = +proc llStreamOpen*(data: string): PLLStream = new(result) result.s = data result.kind = llsString -proc llStreamOpen*(f: File): PLLStream = +proc llStreamOpen*(f: File): PLLStream = new(result) result.f = f result.kind = llsFile -proc llStreamOpen*(filename: string, mode: FileMode): PLLStream = +proc llStreamOpen*(filename: string, mode: FileMode): PLLStream = new(result) result.kind = llsFile if not open(result.f, filename, mode): result = nil - -proc llStreamOpen*(): PLLStream = + +proc llStreamOpen*(): PLLStream = new(result) result.kind = llsNone -proc llStreamOpenStdIn*(): PLLStream = +proc llStreamOpenStdIn*(): PLLStream = new(result) result.kind = llsStdIn result.s = "" result.lineOffset = -1 -proc llStreamClose*(s: PLLStream) = +proc llStreamClose*(s: PLLStream) = case s.kind - of llsNone, llsString, llsStdIn: + of llsNone, llsString, llsStdIn: discard - of llsFile: + of llsFile: close(s.f) -when not declared(readLineFromStdin): +when not declared(readLineFromStdin): # fallback implementation: proc readLineFromStdin(prompt: string, line: var string): bool = stdout.write(prompt) @@ -77,7 +78,7 @@ proc endsWith*(x: string, s: set[char]): bool = if i >= 0 and x[i] in s: result = true -const +const LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '|', '%', '&', '$', '@', '~', ','} AdditionalLineContinuationOprs = {'#', ':', '='} @@ -104,31 +105,31 @@ proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int = s.rd = 0 var line = newStringOfCap(120) var triples = 0 - while readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line): + while readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line): add(s.s, line) add(s.s, "\n") inc triples, countTriples(line) if not continueLine(line, (triples and 1) == 1): break inc(s.lineOffset) result = min(bufLen, len(s.s) - s.rd) - if result > 0: + if result > 0: copyMem(buf, addr(s.s[s.rd]), result) inc(s.rd, result) -proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int = +proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int = case s.kind - of llsNone: + of llsNone: result = 0 - of llsString: + of llsString: result = min(bufLen, len(s.s) - s.rd) - if result > 0: + if result > 0: copyMem(buf, addr(s.s[0 + s.rd]), result) inc(s.rd, result) - of llsFile: + of llsFile: result = readBuffer(s.f, buf, bufLen) - of llsStdIn: + of llsStdIn: result = llReadFromStdin(s, buf, bufLen) - + proc llStreamReadLine*(s: PLLStream, line: var string): bool = setLen(line, 0) case s.kind @@ -152,60 +153,60 @@ proc llStreamReadLine*(s: PLLStream, line: var string): bool = result = readLine(s.f, line) of llsStdIn: result = readLine(stdin, line) - -proc llStreamWrite*(s: PLLStream, data: string) = + +proc llStreamWrite*(s: PLLStream, data: string) = case s.kind - of llsNone, llsStdIn: + of llsNone, llsStdIn: discard - of llsString: + of llsString: add(s.s, data) inc(s.wr, len(data)) - of llsFile: + of llsFile: write(s.f, data) - -proc llStreamWriteln*(s: PLLStream, data: string) = + +proc llStreamWriteln*(s: PLLStream, data: string) = llStreamWrite(s, data) llStreamWrite(s, "\n") -proc llStreamWrite*(s: PLLStream, data: char) = +proc llStreamWrite*(s: PLLStream, data: char) = var c: char case s.kind - of llsNone, llsStdIn: + of llsNone, llsStdIn: discard - of llsString: + of llsString: add(s.s, data) inc(s.wr) - of llsFile: + of llsFile: c = data discard writeBuffer(s.f, addr(c), sizeof(c)) -proc llStreamWrite*(s: PLLStream, buf: pointer, buflen: int) = +proc llStreamWrite*(s: PLLStream, buf: pointer, buflen: int) = case s.kind - of llsNone, llsStdIn: + of llsNone, llsStdIn: discard - of llsString: - if buflen > 0: + of llsString: + if buflen > 0: setLen(s.s, len(s.s) + buflen) copyMem(addr(s.s[0 + s.wr]), buf, buflen) inc(s.wr, buflen) - of llsFile: + of llsFile: discard writeBuffer(s.f, buf, buflen) - -proc llStreamReadAll*(s: PLLStream): string = - const + +proc llStreamReadAll*(s: PLLStream): string = + const bufSize = 2048 case s.kind - of llsNone, llsStdIn: + of llsNone, llsStdIn: result = "" - of llsString: + of llsString: if s.rd == 0: result = s.s else: result = substr(s.s, s.rd) s.rd = len(s.s) - of llsFile: + of llsFile: result = newString(bufSize) var bytes = readBuffer(s.f, addr(result[0]), bufSize) var i = bytes - while bytes == bufSize: + while bytes == bufSize: setLen(result, i + bufSize) bytes = readBuffer(s.f, addr(result[i + 0]), bufSize) inc(i, bytes) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 88e32404a..e88589c3e 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -116,7 +116,7 @@ proc errorSym*(c: PContext, n: PNode): PSym = result.typ = errorType(c) incl(result.flags, sfDiscardable) # pretend it's imported from some unknown module to prevent cascading errors: - if gCmd != cmdInteractive and c.inCompilesContext == 0: + if gCmd != cmdInteractive and c.compilesContextId == 0: c.importTable.addSym(result) type diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index b6b01d558..20800b809 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -63,6 +63,52 @@ proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode = if n.sons[i].kind == nkSym: v.addVar(n.sons[i]) result.add newAsgnStmt(n.sons[i], newTupleAccess(tempAsNode, i)) +proc newTupleAccessRaw*(tup: PNode, i: int): PNode = + result = newNodeI(nkBracketExpr, tup.info) + addSon(result, copyTree(tup)) + var lit = newNodeI(nkIntLit, tup.info) + lit.intVal = i + addSon(result, lit) + +proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode = + let value = n.lastSon + result = newNodeI(nkStmtList, n.info) + + var temp = newSym(skTemp, getIdent(genPrefix), owner, value.info) + var v = newNodeI(nkLetSection, value.info) + let tempAsNode = newIdentNode(getIdent(genPrefix & $temp.id), value.info) + + var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3) + vpart.sons[0] = tempAsNode + vpart.sons[1] = ast.emptyNode + vpart.sons[2] = value + addSon(v, vpart) + result.add(v) + + let lhs = n.sons[0] + for i in 0 .. lhs.len-1: + result.add newAsgnStmt(lhs.sons[i], newTupleAccessRaw(tempAsNode, i)) + +proc lowerSwap*(n: PNode; owner: PSym): PNode = + result = newNodeI(nkStmtList, n.info) + # note: cannot use 'skTemp' here cause we really need the copy for the VM :-( + var temp = newSym(skVar, getIdent(genPrefix), owner, n.info) + temp.typ = n.sons[1].typ + incl(temp.flags, sfFromGeneric) + + var v = newNodeI(nkVarSection, n.info) + let tempAsNode = newSymNode(temp) + + var vpart = newNodeI(nkIdentDefs, v.info, 3) + vpart.sons[0] = tempAsNode + vpart.sons[1] = ast.emptyNode + vpart.sons[2] = n[1] + addSon(v, vpart) + + result.add(v) + result.add newFastAsgnStmt(n[1], n[2]) + result.add newFastAsgnStmt(n[2], tempAsNode) + proc createObj*(owner: PSym, info: TLineInfo): PType = result = newType(tyObject, owner) rawAddSon(result, nil) @@ -167,7 +213,7 @@ proc genDeref*(n: PNode): PNode = result.add n proc callCodegenProc*(name: string, arg1: PNode; - arg2, arg3: PNode = nil): PNode = + arg2, arg3, optionalArgs: PNode = nil): PNode = result = newNodeI(nkCall, arg1.info) let sym = magicsys.getCompilerProc(name) if sym == nil: @@ -177,6 +223,9 @@ proc callCodegenProc*(name: string, arg1: PNode; result.add arg1 if arg2 != nil: result.add arg2 if arg3 != nil: result.add arg3 + if optionalArgs != nil: + for i in 1..optionalArgs.len-3: + result.add optionalArgs[i] result.typ = sym.typ.sons[0] proc callProc(a: PNode): PNode = @@ -483,7 +532,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType; barrier, dest: PNode = nil): PNode = # if 'barrier' != nil, then it is in a 'parallel' section and we # generate quite different code - let n = spawnExpr[1] + let n = spawnExpr[^2] let spawnKind = spawnResult(retType, barrier!=nil) case spawnKind of srVoid: @@ -569,7 +618,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType; fvField = newDotExpr(scratchObj, field) fvAsExpr = indirectAccess(castExpr, field, n.info) # create flowVar: - result.add newFastAsgnStmt(fvField, callProc(spawnExpr[2])) + result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1])) if barrier == nil: result.add callCodegenProc("nimFlowVarCreateSemaphore", fvField) @@ -584,7 +633,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType; let wrapper = createWrapperProc(fn, threadParam, argsParam, varSection, varInit, call, barrierAsExpr, fvAsExpr, spawnKind) - result.add callCodegenProc("nimSpawn", wrapper.newSymNode, - genAddrOf(scratchObj.newSymNode)) + result.add callCodegenProc("nimSpawn" & $spawnExpr.len, wrapper.newSymNode, + genAddrOf(scratchObj.newSymNode), nil, spawnExpr) if spawnKind == srFlowVar: result.add fvField diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index 6d4c65268..40e0728ae 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -9,43 +9,35 @@ # Built-in types and compilerprocs are registered here. -import +import ast, astalgo, hashes, msgs, platform, nversion, times, idents, rodread var systemModule*: PSym -proc registerSysType*(t: PType) - # magic symbols in the system module: -proc getSysType*(kind: TTypeKind): PType -proc getCompilerProc*(name: string): PSym -proc registerCompilerProc*(s: PSym) -proc finishSystem*(tab: TStrTable) -proc getSysSym*(name: string): PSym -# implementation - var gSysTypes: array[TTypeKind, PType] compilerprocs: TStrTable + exposed: TStrTable proc nilOrSysInt*: PType = gSysTypes[tyInt] -proc registerSysType(t: PType) = +proc registerSysType*(t: PType) = if gSysTypes[t.kind] == nil: gSysTypes[t.kind] = t - -proc newSysType(kind: TTypeKind, size: int): PType = + +proc newSysType(kind: TTypeKind, size: int): PType = result = newType(kind, systemModule) result.size = size result.align = size.int16 -proc getSysSym(name: string): PSym = +proc getSysSym*(name: string): PSym = result = strTableGet(systemModule.tab, getIdent(name)) - if result == nil: + if result == nil: rawMessage(errSystemNeeds, name) result = newSym(skError, getIdent(name), systemModule, systemModule.info) result.typ = newType(tyError, systemModule) if result.kind == skStub: loadStub(result) if result.kind == skAlias: result = result.owner - + proc getSysMagic*(name: string, m: TMagic): PSym = var ti: TIdentIter let id = getIdent(name) @@ -57,13 +49,13 @@ proc getSysMagic*(name: string, m: TMagic): PSym = rawMessage(errSystemNeeds, name) result = newSym(skError, id, systemModule, systemModule.info) result.typ = newType(tyError, systemModule) - -proc sysTypeFromName*(name: string): PType = + +proc sysTypeFromName*(name: string): PType = result = getSysSym(name).typ -proc getSysType(kind: TTypeKind): PType = +proc getSysType*(kind: TTypeKind): PType = result = gSysTypes[kind] - if result == nil: + if result == nil: case kind of tyInt: result = sysTypeFromName("int") of tyInt8: result = sysTypeFromName("int8") @@ -87,7 +79,7 @@ proc getSysType(kind: TTypeKind): PType = of tyNil: result = newSysType(tyNil, ptrSize) else: internalError("request for typekind: " & $kind) gSysTypes[kind] = result - if result.kind != kind: + if result.kind != kind: internalError("wanted: " & $kind & " got: " & $result.kind) if result == nil: internalError("type not found: " & $kind) @@ -97,6 +89,7 @@ var proc resetSysTypes* = systemModule = nil initStrTable(compilerprocs) + initStrTable(exposed) for i in low(gSysTypes)..high(gSysTypes): gSysTypes[i] = nil @@ -163,8 +156,8 @@ proc setIntLitType*(result: PNode) = result.typ = getSysType(tyInt64) else: internalError(result.info, "invalid int size") -proc getCompilerProc(name: string): PSym = - var ident = getIdent(name, hashIgnoreStyle(name)) +proc getCompilerProc*(name: string): PSym = + let ident = getIdent(name) result = strTableGet(compilerprocs, ident) if result == nil: result = strTableGet(rodCompilerprocs, ident) @@ -173,9 +166,22 @@ proc getCompilerProc(name: string): PSym = if result.kind == skStub: loadStub(result) if result.kind == skAlias: result = result.owner -proc registerCompilerProc(s: PSym) = +proc registerCompilerProc*(s: PSym) = strTableAdd(compilerprocs, s) -proc finishSystem(tab: TStrTable) = discard +proc registerNimScriptSymbol*(s: PSym) = + # Nimscript symbols must be al unique: + let conflict = strTableGet(exposed, s.name) + if conflict == nil: + strTableAdd(exposed, s) + else: + localError(s.info, "symbol conflicts with other .exportNims symbol at: " & + $conflict.info) + +proc getNimScriptSymbol*(name: string): PSym = + strTableGet(exposed, getIdent(name)) + +proc resetNimScriptSymbols*() = initStrTable(exposed) initStrTable(compilerprocs) +initStrTable(exposed) diff --git a/compiler/main.nim b/compiler/main.nim index 0c80c19b7..3d04cc61c 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -15,7 +15,7 @@ import wordrecg, sem, semdata, idents, passes, docgen, extccomp, cgen, jsgen, json, nversion, platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen, - tables, docgen2, service, parser, modules, ccgutils, sigmatch, ropes, lists + docgen2, service, parser, modules, ccgutils, sigmatch, ropes, lists from magicsys import systemModule, resetSysTypes @@ -63,14 +63,14 @@ proc commandCompileToC = compileProject() cgenWriteModules() if gCmd != cmdRun: - extccomp.callCCompiler(if gProjectName == "-": "stdinfile" else: changeFileExt(gProjectFull, "")) + extccomp.callCCompiler(changeFileExt(gProjectFull, "")) if isServing: # caas will keep track only of the compilation commands lastCaasCmd = curCaasCmd resetCgenModules() for i in 0 .. <gMemCacheData.len: - gMemCacheData[i].crcStatus = crcCached + gMemCacheData[i].hashStatus = hashCached gMemCacheData[i].needsRecompile = Maybe # XXX: clean these global vars @@ -116,7 +116,7 @@ proc interactivePasses = #incl(gGlobalOptions, optSafeCode) #setTarget(osNimrodVM, cpuNimrodVM) initDefines() - defineSymbol("nimrodvm") + defineSymbol("nimscript") when hasFFI: defineSymbol("nimffi") registerPass(verbosePass) registerPass(semPass) @@ -190,7 +190,6 @@ proc resetMemory = resetRopeCache() resetSysTypes() gOwners = @[] - rangeDestructorProc = nil for i in low(buckets)..high(buckets): buckets[i] = nil idAnon = nil @@ -237,7 +236,7 @@ proc mainCommand* = when SimulateCaasMemReset: gGlobalOptions.incl(optCaasEnabled) - # In "nimrod serve" scenario, each command must reset the registered passes + # In "nim serve" scenario, each command must reset the registered passes clearPasses() gLastCmdTime = epochTime() appendStr(searchPaths, options.libpath) @@ -318,11 +317,12 @@ proc mainCommand* = (key: "lib_paths", val: libpaths) ] - outWriteln($dumpdata) + msgWriteln($dumpdata, {msgStdout, msgSkipHook}) else: - outWriteln("-- list of currently defined symbols --") - for s in definedSymbolNames(): outWriteln(s) - outWriteln("-- end of list --") + msgWriteln("-- list of currently defined symbols --", + {msgStdout, msgSkipHook}) + for s in definedSymbolNames(): msgWriteln(s, {msgStdout, msgSkipHook}) + msgWriteln("-- end of list --", {msgStdout, msgSkipHook}) for it in iterSearchPath(searchPaths): msgWriteln(it) of "check": @@ -337,7 +337,7 @@ proc mainCommand* = wantMainModule() commandScan() msgWriteln("Beware: Indentation tokens depend on the parser\'s state!") - of "i": + of "secret": gCmd = cmdInteractive commandInteractive() of "e": @@ -356,12 +356,14 @@ proc mainCommand* = gGlobalOptions.incl(optCaasEnabled) msgs.gErrorMax = high(int) # do not stop after first error serve(mainCommand) + of "nop", "help": + # prevent the "success" message: + gCmd = cmdDump else: rawMessage(errInvalidCommandX, command) - if (msgs.gErrorCounter == 0 and - gCmd notin {cmdInterpret, cmdRun, cmdDump} and - gVerbosity > 0): + if msgs.gErrorCounter == 0 and + gCmd notin {cmdInterpret, cmdRun, cmdDump}: rawMessage(hintSuccessX, [$gLinesCompiled, formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3), formatSize(getTotalMem()), @@ -379,3 +381,4 @@ proc mainCommand* = when SimulateCaasMemReset: resetMemory() + resetAttributes() diff --git a/compiler/modules.nim b/compiler/modules.nim index a2b739efc..3893d377e 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -10,19 +10,19 @@ ## implements the module handling import - ast, astalgo, magicsys, crc, rodread, msgs, cgendata, sigmatch, options, - idents, os, lexer, idgen, passes, syntaxes + ast, astalgo, magicsys, securehash, rodread, msgs, cgendata, sigmatch, options, + idents, os, lexer, idgen, passes, syntaxes, llstream type TNeedRecompile* = enum Maybe, No, Yes, Probing, Recompiled - TCrcStatus* = enum crcNotTaken, crcCached, crcHasChanged, crcNotChanged + THashStatus* = enum hashNotTaken, hashCached, hashHasChanged, hashNotChanged TModuleInMemory* = object compiledAt*: float - crc*: TCrc32 + hash*: SecureHash deps*: seq[int32] ## XXX: slurped files are currently not tracked needsRecompile*: TNeedRecompile - crcStatus*: TCrcStatus + hashStatus*: THashStatus var gCompiledModules: seq[PSym] = @[] @@ -34,36 +34,36 @@ proc getModule(fileIdx: int32): PSym = if fileIdx >= 0 and fileIdx < gCompiledModules.len: result = gCompiledModules[fileIdx] -template crc(x: PSym): expr = - gMemCacheData[x.position].crc +template hash(x: PSym): expr = + gMemCacheData[x.position].hash -proc crcChanged(fileIdx: int32): bool = +proc hashChanged(fileIdx: int32): bool = internalAssert fileIdx >= 0 and fileIdx < gMemCacheData.len - + template updateStatus = - gMemCacheData[fileIdx].crcStatus = if result: crcHasChanged - else: crcNotChanged - # echo "TESTING CRC: ", fileIdx.toFilename, " ", result - - case gMemCacheData[fileIdx].crcStatus: - of crcHasChanged: + gMemCacheData[fileIdx].hashStatus = if result: hashHasChanged + else: hashNotChanged + # echo "TESTING Hash: ", fileIdx.toFilename, " ", result + + case gMemCacheData[fileIdx].hashStatus: + of hashHasChanged: result = true - of crcNotChanged: + of hashNotChanged: result = false - of crcCached: - let newCrc = crcFromFile(fileIdx.toFilename) - result = newCrc != gMemCacheData[fileIdx].crc - gMemCacheData[fileIdx].crc = newCrc + of hashCached: + let newHash = secureHashFile(fileIdx.toFullPath) + result = newHash != gMemCacheData[fileIdx].hash + gMemCacheData[fileIdx].hash = newHash updateStatus() - of crcNotTaken: - gMemCacheData[fileIdx].crc = crcFromFile(fileIdx.toFilename) + of hashNotTaken: + gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath) result = true updateStatus() -proc doCRC(fileIdx: int32) = - if gMemCacheData[fileIdx].crcStatus == crcNotTaken: - # echo "FIRST CRC: ", fileIdx.ToFilename - gMemCacheData[fileIdx].crc = crcFromFile(fileIdx.toFilename) +proc doHash(fileIdx: int32) = + if gMemCacheData[fileIdx].hashStatus == hashNotTaken: + # echo "FIRST Hash: ", fileIdx.ToFilename + gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath) proc addDep(x: PSym, dep: int32) = growCache gMemCacheData, dep @@ -78,6 +78,13 @@ proc resetModule*(fileIdx: int32) = if fileIdx <% cgendata.gModules.len: cgendata.gModules[fileIdx] = nil +proc resetModule*(module: PSym) = + let conflict = getModule(module.position.int32) + if conflict == nil: return + doAssert conflict == module + resetModule(module.position.int32) + initStrTable(module.tab) + proc resetAllModules* = for i in 0..gCompiledModules.high: if gCompiledModules[i] != nil: @@ -85,6 +92,14 @@ proc resetAllModules* = resetPackageCache() # for m in cgenModules(): echo "CGEN MODULE FOUND" +proc resetAllModulesHard* = + resetPackageCache() + gCompiledModules.setLen 0 + gMemCacheData.setLen 0 + magicsys.resetSysTypes() + # XXX + #gOwners = @[] + proc checkDepMem(fileIdx: int32): TNeedRecompile = template markDirty = resetModule(fileIdx) @@ -94,9 +109,9 @@ proc checkDepMem(fileIdx: int32): TNeedRecompile = return gMemCacheData[fileIdx].needsRecompile if optForceFullMake in gGlobalOptions or - crcChanged(fileIdx): + hashChanged(fileIdx): markDirty - + if gMemCacheData[fileIdx].deps != nil: gMemCacheData[fileIdx].needsRecompile = Probing for dep in gMemCacheData[fileIdx].deps: @@ -104,30 +119,30 @@ proc checkDepMem(fileIdx: int32): TNeedRecompile = if d in {Yes, Recompiled}: # echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d markDirty - + gMemCacheData[fileIdx].needsRecompile = No return No proc newModule(fileIdx: int32): PSym = # We cannot call ``newSym`` here, because we have to circumvent the ID - # mechanism, which we do in order to assign each module a persistent ID. + # mechanism, which we do in order to assign each module a persistent ID. new(result) result.id = - 1 # for better error checking result.kind = skModule let filename = fileIdx.toFullPath result.name = getIdent(splitFile(filename).name) - if result.name.s != "-" and not isNimIdentifier(result.name.s): + if not isNimIdentifier(result.name.s): rawMessage(errInvalidModuleName, result.name.s) - + result.info = newLineInfo(fileIdx, 1, 1) result.owner = newSym(skPackage, getIdent(getPackageName(filename)), nil, result.info) result.position = fileIdx - + growCache gMemCacheData, fileIdx growCache gCompiledModules, fileIdx gCompiledModules[result.position] = result - + incl(result.flags, sfUsed) initStrTable(result.tab) strTableAdd(result.tab, result) # a module knows itself @@ -143,16 +158,19 @@ proc compileModule*(fileIdx: int32, flags: TSymFlags): PSym = result.flags = result.flags + flags if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}: rd = handleSymbolFile(result) - if result.id < 0: + if result.id < 0: internalError("handleSymbolFile should have set the module\'s ID") return else: result.id = getID() - processModule(result, nil, rd) + if sfMainModule in flags and gProjectIsStdin: + processModule(result, llStreamOpen(stdin), rd) + else: + processModule(result, nil, rd) if optCaasEnabled in gGlobalOptions: gMemCacheData[fileIdx].compiledAt = gLastCmdTime gMemCacheData[fileIdx].needsRecompile = Recompiled - doCRC fileIdx + doHash fileIdx else: if checkDepMem(fileIdx) == Yes: result = compileModule(fileIdx, flags) @@ -171,7 +189,7 @@ proc includeModule*(s: PSym, fileIdx: int32): PNode {.procvar.} = if optCaasEnabled in gGlobalOptions: growCache gMemCacheData, fileIdx addDep(s, fileIdx) - doCRC(fileIdx) + doHash(fileIdx) proc `==^`(a, b: string): bool = try: @@ -202,9 +220,8 @@ proc compileProject*(projectFileIdx = -1'i32) = compileSystemModule() discard compileModule(projectFile, {sfMainModule}) -var stdinModule: PSym -proc makeStdinModule*(): PSym = - if stdinModule == nil: - stdinModule = newModule(fileInfoIdx"stdin") - stdinModule.id = getID() - result = stdinModule +proc makeModule*(filename: string): PSym = + result = newModule(fileInfoIdx filename) + result.id = getID() + +proc makeStdinModule*(): PSym = makeModule"stdin" diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 041a181be..7b44b4349 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -8,7 +8,7 @@ # import - options, strutils, os, tables, ropes, platform + options, strutils, os, tables, ropes, platform, terminal, macros type TMsgKind* = enum @@ -17,10 +17,9 @@ type errIntLiteralExpected, errInvalidCharacterConstant, errClosingTripleQuoteExpected, errClosingQuoteExpected, errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong, - errInvalidNumber, errNumberOutOfRange, errNnotAllowedInCharacter, - errClosingBracketExpected, errMissingFinalQuote, errIdentifierExpected, - errNewlineExpected, - errInvalidModuleName, + errInvalidNumber, errInvalidNumberOctalCode, errNumberOutOfRange, + errNnotAllowedInCharacter, errClosingBracketExpected, errMissingFinalQuote, + errIdentifierExpected, errNewlineExpected, errInvalidModuleName, errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected, errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected, errInvalidPragma, errUnknownPragma, errInvalidDirectiveX, @@ -31,11 +30,14 @@ type errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel, errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected, errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler, - errOnOrOffExpectedButXFound, errNoneBoehmRefcExpectedButXFound, + errOnOrOffExpectedButXFound, errOnOffOrListExpectedButXFound, + errNoneBoehmRefcExpectedButXFound, errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound, errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound, errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected, - errExprExpected, errUndeclaredIdentifier, errUseQualifier, errTypeExpected, + errExprExpected, errUndeclaredIdentifier, errUndeclaredField, + errUndeclaredRoutine, errUseQualifier, + errTypeExpected, errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable, errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue, errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous, @@ -80,7 +82,7 @@ type errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed, errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType, errNoReturnTypeDeclared, - errInvalidCommandX, errXOnlyAtModuleScope, + errNoCommand, errInvalidCommandX, errXOnlyAtModuleScope, errXNeedsParamObjectType, errTemplateInstantiationTooNested, errInstantiationFrom, errInvalidIndexValueForTuple, errCommandExpectsFilename, @@ -106,6 +108,8 @@ type errCannotInferReturnType, errGenericLambdaNotAllowed, errCompilerDoesntSupportTarget, + errExternalAssemblerNotFound, + errExternalAssemblerNotValid, errUser, warnCannotOpenFile, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, @@ -114,7 +118,7 @@ type warnUnknownSubstitutionX, warnLanguageXNotSupported, warnFieldXNotSupported, warnCommentXIgnored, warnNilStatement, warnTypelessParam, - warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode, + warnUseBase, warnWriteToForeignHeap, warnUnsafeCode, warnEachIdentIsTuple, warnShadowIdent, warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, @@ -124,6 +128,8 @@ type hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, hintConditionAlwaysTrue, hintName, hintPattern, + hintExecuting, hintLinking, hintDependency, + hintSource, hintStackTrace, hintGCStats, hintUser const @@ -143,6 +149,7 @@ const errInvalidToken: "invalid token: $1", errLineTooLong: "line too long", errInvalidNumber: "$1 is not a valid number", + errInvalidNumberOctalCode: "$1 is not a valid number; did you mean octal? Then use one of '0o', '0c' or '0C'.", errNumberOutOfRange: "number $1 out of valid range", errNnotAllowedInCharacter: "\\n not allowed in character literal", errClosingBracketExpected: "closing ']' expected, but end of file reached", @@ -179,6 +186,7 @@ const errUnknownVar: "unknown variable: \'$1\'", errUnknownCcompiler: "unknown C compiler: \'$1\'", errOnOrOffExpectedButXFound: "\'on\' or \'off\' expected, but \'$1\' found", + errOnOffOrListExpectedButXFound: "\'on\', \'off\' or \'list\' expected, but \'$1\' found", errNoneBoehmRefcExpectedButXFound: "'none', 'boehm' or 'refc' expected, but '$1' found", errNoneSpeedOrSizeExpectedButXFound: "'none', 'speed' or 'size' expected, but '$1' found", errGuiConsoleOrLibExpectedButXFound: "'gui', 'console' or 'lib' expected, but '$1' found", @@ -190,10 +198,12 @@ const errColonOrEqualsExpected: "\':\' or \'=\' expected, but found \'$1\'", errExprExpected: "expression expected, but found \'$1\'", errUndeclaredIdentifier: "undeclared identifier: \'$1\'", + errUndeclaredField: "undeclared field: \'$1\'", + errUndeclaredRoutine: "attempting to call undeclared routine: \'$1\'", errUseQualifier: "ambiguous identifier: \'$1\' -- use a qualifier", errTypeExpected: "type expected", errSystemNeeds: "system module needs \'$1\'", - errExecutionOfProgramFailed: "execution of an external program failed", + errExecutionOfProgramFailed: "execution of an external program failed: '$1'", errNotOverloadable: "overloaded \'$1\' leads to ambiguous calls", errInvalidArgForX: "invalid argument for \'$1\'", errStmtHasNoEffect: "statement has no effect", @@ -308,6 +318,7 @@ const errIteratorNotAllowed: "iterators can only be defined at the module\'s top level", errXNeedsReturnType: "$1 needs a return type", errNoReturnTypeDeclared: "no return type declared", + errNoCommand: "no command given", errInvalidCommandX: "invalid command: \'$1\'", errXOnlyAtModuleScope: "\'$1\' is only allowed at top level", errXNeedsParamObjectType: "'$1' needs a parameter that has an object type", @@ -362,55 +373,63 @@ const "it is used as an operand to another routine and the types " & "of the generic paramers can be inferred from the expected signature.", errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target", + errExternalAssemblerNotFound: "External assembler not found", + errExternalAssemblerNotValid: "External assembler '$1' is not a valid assembler", errUser: "$1", - warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]", - warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", - warnXIsNeverRead: "\'$1\' is never read [XIsNeverRead]", - warnXmightNotBeenInit: "\'$1\' might not have been initialized [XmightNotBeenInit]", - warnDeprecated: "$1 is deprecated [Deprecated]", - warnConfigDeprecated: "config file '$1' is deprecated [ConfigDeprecated]", - warnSmallLshouldNotBeUsed: "\'l\' should not be used as an identifier; may look like \'1\' (one) [SmallLshouldNotBeUsed]", - warnUnknownMagic: "unknown magic \'$1\' might crash the compiler [UnknownMagic]", - warnRedefinitionOfLabel: "redefinition of label \'$1\' [RedefinitionOfLabel]", - warnUnknownSubstitutionX: "unknown substitution \'$1\' [UnknownSubstitutionX]", - warnLanguageXNotSupported: "language \'$1\' not supported [LanguageXNotSupported]", - warnFieldXNotSupported: "field \'$1\' not supported [FieldXNotSupported]", - warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]", - warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead [NilStmt]", - warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template' [TypelessParam]", - warnDifferentHeaps: "possible inconsistency of thread local heaps [DifferentHeaps]", - warnWriteToForeignHeap: "write to foreign heap [WriteToForeignHeap]", - warnUnsafeCode: "unsafe code: '$1' [UnsafeCode]", - warnEachIdentIsTuple: "each identifier is a tuple [EachIdentIsTuple]", - warnShadowIdent: "shadowed identifier: '$1' [ShadowIdent]", - warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future. [ProveInit]", - warnProveField: "cannot prove that field '$1' is accessible [ProveField]", - warnProveIndex: "cannot prove index '$1' is valid [ProveIndex]", - warnGcUnsafe: "not GC-safe: '$1' [GcUnsafe]", + warnCannotOpenFile: "cannot open \'$1\'", + warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored", + warnXIsNeverRead: "\'$1\' is never read", + warnXmightNotBeenInit: "\'$1\' might not have been initialized", + warnDeprecated: "$1 is deprecated", + warnConfigDeprecated: "config file '$1' is deprecated", + warnSmallLshouldNotBeUsed: "\'l\' should not be used as an identifier; may look like \'1\' (one)", + warnUnknownMagic: "unknown magic \'$1\' might crash the compiler", + warnRedefinitionOfLabel: "redefinition of label \'$1\'", + warnUnknownSubstitutionX: "unknown substitution \'$1\'", + warnLanguageXNotSupported: "language \'$1\' not supported", + warnFieldXNotSupported: "field \'$1\' not supported", + warnCommentXIgnored: "comment \'$1\' ignored", + warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead", + warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'", + warnUseBase: "use {.base.} for base methods; baseless methods are deprecated", + warnWriteToForeignHeap: "write to foreign heap", + warnUnsafeCode: "unsafe code: '$1'", + warnEachIdentIsTuple: "each identifier is a tuple", + warnShadowIdent: "shadowed identifier: '$1'", + warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.", + warnProveField: "cannot prove that field '$1' is accessible", + warnProveIndex: "cannot prove index '$1' is valid", + warnGcUnsafe: "not GC-safe: '$1'", warnGcUnsafe2: "$1", - warnUninit: "'$1' might not have been initialized [Uninit]", - warnGcMem: "'$1' uses GC'ed memory [GcMem]", - warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future. [Destructor]", - warnLockLevel: "$1 [LockLevel]", - warnResultShadowed: "Special variable 'result' is shadowed. [ResultShadowed]", - warnUser: "$1 [User]", - hintSuccess: "operation successful [Success]", - hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#) [SuccessX]", - hintLineTooLong: "line too long [LineTooLong]", - hintXDeclaredButNotUsed: "\'$1\' is declared but not used [XDeclaredButNotUsed]", - hintConvToBaseNotNeeded: "conversion to base object is not needed [ConvToBaseNotNeeded]", - hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless [ConvFromXtoItselfNotNeeded]", - hintExprAlwaysX: "expression evaluates always to \'$1\' [ExprAlwaysX]", - hintQuitCalled: "quit() called [QuitCalled]", - hintProcessing: "$1 [Processing]", - hintCodeBegin: "generated code listing: [CodeBegin]", - hintCodeEnd: "end of listing [CodeEnd]", - hintConf: "used config file \'$1\' [Conf]", - hintPath: "added path: '$1' [Path]", - hintConditionAlwaysTrue: "condition is always true: '$1' [CondTrue]", - hintName: "name should be: '$1' [Name]", - hintPattern: "$1 [Pattern]", - hintUser: "$1 [User]"] + warnUninit: "'$1' might not have been initialized", + warnGcMem: "'$1' uses GC'ed memory", + warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.", + warnLockLevel: "$1", + warnResultShadowed: "Special variable 'result' is shadowed.", + warnUser: "$1", + hintSuccess: "operation successful", + hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)", + hintLineTooLong: "line too long", + hintXDeclaredButNotUsed: "\'$1\' is declared but not used", + hintConvToBaseNotNeeded: "conversion to base object is not needed", + hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless", + hintExprAlwaysX: "expression evaluates always to \'$1\'", + hintQuitCalled: "quit() called", + hintProcessing: "$1", + hintCodeBegin: "generated code listing:", + hintCodeEnd: "end of listing", + hintConf: "used config file \'$1\'", + hintPath: "added path: '$1'", + hintConditionAlwaysTrue: "condition is always true: '$1'", + hintName: "name should be: '$1'", + hintPattern: "$1", + hintExecuting: "$1", + hintLinking: "", + hintDependency: "$1", + hintSource: "$1", + hintStackTrace: "$1", + hintGCStats: "$1", + hintUser: "$1"] const WarningsToStr*: array[0..30, string] = ["CannotOpenFile", "OctalEscape", @@ -420,15 +439,16 @@ const "RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported", "FieldXNotSupported", "CommentXIgnored", "NilStmt", - "TypelessParam", "DifferentHeaps", "WriteToForeignHeap", + "TypelessParam", "UseBase", "WriteToForeignHeap", "UnsafeCode", "EachIdentIsTuple", "ShadowIdent", "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", "GcMem", "Destructor", "LockLevel", "ResultShadowed", "User"] - HintsToStr*: array[0..16, string] = ["Success", "SuccessX", "LineTooLong", + HintsToStr*: array[0..22, string] = ["Success", "SuccessX", "LineTooLong", "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", - "Path", "CondTrue", "Name", "Pattern", + "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency", + "Source", "StackTrace", "GCStats", "User"] const @@ -449,10 +469,10 @@ type fullPath: string # This is a canonical full filesystem path projPath*: string # This is relative to the project's root shortName*: string # short name of the module - quotedName*: Rope # cached quoted short name for codegen + quotedName*: Rope # cached quoted short name for codegen # purposes - lines*: seq[Rope] # the source code of the module + lines*: seq[Rope] # the source code of the module # used for better error messages and # embedding the original source in the # generated code @@ -480,6 +500,30 @@ type ESuggestDone* = object of Exception const + NotesVerbosity*: array[0..3, TNoteKinds] = [ + {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, + warnProveField, warnProveIndex, + warnGcUnsafe, + hintSuccessX, hintPath, hintConf, + hintProcessing, + hintDependency, + hintExecuting, hintLinking, + hintCodeBegin, hintCodeEnd, + hintSource, hintStackTrace, + hintGCStats}, + {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, + warnProveField, warnProveIndex, + warnGcUnsafe, + hintPath, hintConf, + hintDependency, + hintExecuting, + hintCodeBegin, hintCodeEnd, + hintSource, hintStackTrace, + hintGCStats}, + {low(TNoteKind)..high(TNoteKind)} - {hintStackTrace}, + {low(TNoteKind)..high(TNoteKind)}] + +const InvalidFileIDX* = int32(-1) var @@ -567,9 +611,7 @@ proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} = proc sourceLine*(i: TLineInfo): Rope var - gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} - - {warnShadowIdent, warnUninit, - warnProveField, warnProveIndex, warnGcUnsafe} + gNotes*: TNoteKinds = NotesVerbosity[1] # defaults to verbosity of 1 gErrorCounter*: int = 0 # counts the number of errors gHintCounter*: int = 0 gWarnCounter*: int = 0 @@ -589,8 +631,11 @@ var proc suggestWriteln*(s: string) = if eStdOut in errorOutputs: - if isNil(writelnHook): writeln(stdout, s) - else: writelnHook(s) + if isNil(writelnHook): + writeLine(stdout, s) + flushFile(stdout) + else: + writelnHook(s) proc msgQuit*(x: int8) = quit x proc msgQuit*(x: string) = quit x @@ -601,13 +646,17 @@ proc suggestQuit*() = # this format is understood by many text editors: it is the same that # Borland and Freepascal use const - PosErrorFormat* = "$1($2, $3) Error: $4" - PosWarningFormat* = "$1($2, $3) Warning: $4" - PosHintFormat* = "$1($2, $3) Hint: $4" - PosContextFormat = "$1($2, $3) Info: $4" - RawErrorFormat* = "Error: $1" - RawWarningFormat* = "Warning: $1" - RawHintFormat* = "Hint: $1" + PosFormat = "$1($2, $3) " + KindFormat = " [$1]" + KindColor = fgCyan + ErrorTitle = "Error: " + ErrorColor = fgRed + WarningTitle = "Warning: " + WarningColor = fgYellow + HintTitle = "Hint: " + HintColor = fgGreen + InfoTitle = "Info: " + InfoColor = fgCyan proc getInfoContextLen*(): int = return msgContext.len proc setInfoContextLen*(L: int) = setLen(msgContext, L) @@ -678,21 +727,79 @@ proc `??`* (info: TLineInfo, filename: string): bool = var gTrackPos*: TLineInfo -proc outWriteln*(s: string) = - ## Writes to stdout. Always. - if eStdOut in errorOutputs: writeln(stdout, s) +type + MsgFlag* = enum ## flags altering msgWriteln behavior + msgStdout, ## force writing to stdout, even stderr is default + msgSkipHook ## skip message hook even if it is present + MsgFlags* = set[MsgFlag] -proc msgWriteln*(s: string) = - ## Writes to stdout. If --stdout option is given, writes to stderr instead. +proc msgWriteln*(s: string, flags: MsgFlags = {}) = + ## Writes given message string to stderr by default. + ## If ``--stdout`` option is given, writes to stdout instead. If message hook + ## is present, then it is used to output message rather than stderr/stdout. + ## This behavior can be altered by given optional flags. #if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return - if not isNil(writelnHook): + if not isNil(writelnHook) and msgSkipHook notin flags: writelnHook(s) + elif optStdout in gGlobalOptions or msgStdout in flags: + if eStdOut in errorOutputs: + writeLine(stdout, s) + flushFile(stdout) + else: + if eStdErr in errorOutputs: + writeLine(stderr, s) + # On Windows stderr is fully-buffered when piped, regardless of C std. + when defined(windows): + flushFile(stderr) + +macro callIgnoringStyle(theProc: typed, first: typed, + args: varargs[expr]): stmt = + let typForegroundColor = bindSym"ForegroundColor".getType + let typBackgroundColor = bindSym"BackgroundColor".getType + let typStyle = bindSym"Style".getType + let typTerminalCmd = bindSym"TerminalCmd".getType + result = newCall(theProc) + if first.kind != nnkNilLit: result.add(first) + for arg in children(args[0][1]): + if arg.kind == nnkNilLit: continue + let typ = arg.getType + if typ.kind != nnkEnumTy or + typ != typForegroundColor and + typ != typBackgroundColor and + typ != typStyle and + typ != typTerminalCmd: + result.add(arg) + +macro callStyledWriteLineStderr(args: varargs[expr]): stmt = + result = newCall(bindSym"styledWriteLine") + result.add(bindSym"stderr") + for arg in children(args[0][1]): + result.add(arg) + +template callWritelnHook(args: varargs[string, `$`]) = + var s = "" + for arg in args: + s.add arg + writelnHook s + +template styledMsgWriteln*(args: varargs[expr]) = + if not isNil(writelnHook): + callIgnoringStyle(callWritelnHook, nil, args) elif optStdout in gGlobalOptions: - if eStdErr in errorOutputs: writeln(stderr, s) + if eStdOut in errorOutputs: + callIgnoringStyle(writeLine, stdout, args) + flushFile(stdout) else: - if eStdOut in errorOutputs: writeln(stdout, s) + if eStdErr in errorOutputs: + if optUseColors in gGlobalOptions: + callStyledWriteLineStderr(args) + else: + callIgnoringStyle(writeLine, stderr, args) + # On Windows stderr is fully-buffered when piped, regardless of C std. + when defined(windows): + flushFile(stderr) proc coordToStr(coord: int): string = if coord == -1: result = "???" @@ -710,11 +817,11 @@ type proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) = template quit = - if defined(debug) or gVerbosity >= 3 or msg == errInternal: + if defined(debug) or msg == errInternal or hintStackTrace in gNotes: if stackTraceAvailable() and isNil(writelnHook): writeStackTrace() else: - msgWriteln("No stack traceback available\nTo create a stacktrace, rerun compilation with ./koch temp " & options.command & " <file>") + styledMsgWriteln(fgRed, "No stack traceback available\nTo create a stacktrace, rerun compilation with ./koch temp " & options.command & " <file>") quit 1 if msg >= fatalMin and msg <= fatalMax: @@ -736,61 +843,85 @@ proc writeContext(lastinfo: TLineInfo) = var info = lastinfo for i in countup(0, len(msgContext) - 1): if msgContext[i] != lastinfo and msgContext[i] != info: - msgWriteln(PosContextFormat % [toMsgFilename(msgContext[i]), - coordToStr(msgContext[i].line), - coordToStr(msgContext[i].col+1), - getMessageStr(errInstantiationFrom, "")]) + styledMsgWriteln(styleBright, + PosFormat % [toMsgFilename(msgContext[i]), + coordToStr(msgContext[i].line), + coordToStr(msgContext[i].col+1)], + resetStyle, + getMessageStr(errInstantiationFrom, "")) info = msgContext[i] proc ignoreMsgBecauseOfIdeTools(msg: TMsgKind): bool = msg >= errGenerated and gCmd == cmdIdeTools and optIdeDebug notin gGlobalOptions proc rawMessage*(msg: TMsgKind, args: openArray[string]) = - var frmt: string + var + title: string + color: ForegroundColor + kind: string case msg of errMin..errMax: writeContext(unknownLineInfo()) - frmt = RawErrorFormat + title = ErrorTitle + color = ErrorColor of warnMin..warnMax: if optWarns notin gOptions: return if msg notin gNotes: return writeContext(unknownLineInfo()) - frmt = RawWarningFormat + title = WarningTitle + color = WarningColor + kind = WarningsToStr[ord(msg) - ord(warnMin)] inc(gWarnCounter) of hintMin..hintMax: if optHints notin gOptions: return if msg notin gNotes: return - frmt = RawHintFormat + title = HintTitle + color = HintColor + kind = HintsToStr[ord(msg) - ord(hintMin)] inc(gHintCounter) - let s = `%`(frmt, `%`(msgKindToString(msg), args)) + let s = `%`(msgKindToString(msg), args) if not ignoreMsgBecauseOfIdeTools(msg): - msgWriteln(s) + if kind != nil: + styledMsgWriteln(color, title, resetStyle, s, + KindColor, `%`(KindFormat, kind)) + else: + styledMsgWriteln(color, title, resetStyle, s) handleError(msg, doAbort, s) proc rawMessage*(msg: TMsgKind, arg: string) = rawMessage(msg, [arg]) +proc resetAttributes* = + if {optUseColors, optStdout} * gGlobalOptions == {optUseColors}: + terminal.resetAttributes(stderr) + proc writeSurroundingSrc(info: TLineInfo) = const indent = " " msgWriteln(indent & $info.sourceLine) msgWriteln(indent & spaces(info.col) & '^') proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string = - let frmt = case msg - of warnMin..warnMax: PosWarningFormat - of hintMin..hintMax: PosHintFormat - else: PosErrorFormat - result = frmt % [toMsgFilename(info), coordToStr(info.line), - coordToStr(info.col+1), getMessageStr(msg, arg)] + let title = case msg + of warnMin..warnMax: WarningTitle + of hintMin..hintMax: HintTitle + else: ErrorTitle + result = PosFormat % [toMsgFilename(info), coordToStr(info.line), + coordToStr(info.col+1)] & + title & + getMessageStr(msg, arg) proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, eh: TErrorHandling) = - var frmt: string - var ignoreMsg = false + var + title: string + color: ForegroundColor + kind: string + ignoreMsg = false case msg of errMin..errMax: writeContext(info) - frmt = PosErrorFormat + title = ErrorTitle + color = ErrorColor # we try to filter error messages so that not two error message # in the same file and line are produced: #ignoreMsg = lastError == info and eh != doAbort @@ -798,20 +929,29 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, of warnMin..warnMax: ignoreMsg = optWarns notin gOptions or msg notin gNotes if not ignoreMsg: writeContext(info) - frmt = PosWarningFormat + title = WarningTitle + color = WarningColor + kind = WarningsToStr[ord(msg) - ord(warnMin)] inc(gWarnCounter) of hintMin..hintMax: ignoreMsg = optHints notin gOptions or msg notin gNotes - frmt = PosHintFormat + title = HintTitle + color = HintColor + kind = HintsToStr[ord(msg) - ord(hintMin)] inc(gHintCounter) # NOTE: currently line info line numbers start with 1, # but column numbers start with 0, however most editors expect # first column to be 1, so we need to +1 here - let s = frmt % [toMsgFilename(info), coordToStr(info.line), - coordToStr(info.col+1), getMessageStr(msg, arg)] + let x = PosFormat % [toMsgFilename(info), coordToStr(info.line), + coordToStr(info.col+1)] + let s = getMessageStr(msg, arg) if not ignoreMsg and not ignoreMsgBecauseOfIdeTools(msg): - msgWriteln(s) - if optPrintSurroundingSrc and msg in errMin..errMax: + if kind != nil: + styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s, + KindColor, `%`(KindFormat, kind)) + else: + styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s) + if msg in errMin..errMax and hintSource in gNotes: info.writeSurroundingSrc handleError(msg, eh, s) @@ -882,3 +1022,22 @@ ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) = of rCannotOpenFile: rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg) +proc listWarnings*() = + msgWriteln("Warnings:") + for warn in warnMin..warnMax: + msgWriteln(" [$1] $2" % [ + if warn in gNotes: "x" else: " ", + msgs.WarningsToStr[ord(warn) - ord(warnMin)] + ]) + +proc listHints*() = + msgWriteln("Hints:") + for hint in hintMin..hintMax: + msgWriteln(" [$1] $2" % [ + if hint in gNotes: "x" else: " ", + msgs.HintsToStr[ord(hint) - ord(hintMin)] + ]) + +# enable colors by default on terminals +if terminal.isatty(stderr): + incl(gGlobalOptions, optUseColors) diff --git a/compiler/nim.nim.cfg b/compiler/nim.cfg index 64631a437..4f9962ea8 100644 --- a/compiler/nim.nim.cfg +++ b/compiler/nim.cfg @@ -17,5 +17,4 @@ define:useStdoutAsStdmsg cs:partial #define:useNodeIds -symbol:nimfix #gc:markAndSweep diff --git a/compiler/nim.nim b/compiler/nim.nim index b8ba2c6da..1293ec922 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -1,7 +1,7 @@ # # # The Nim Compiler -# (c) Copyright 2013 Andreas Rumpf +# (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -16,7 +16,7 @@ when defined(gcc) and defined(windows): import commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes, extccomp, strutils, os, osproc, platform, main, parseopt, service, - nodejs + nodejs, scriptconfig when hasTinyCBackend: import tccgen @@ -38,31 +38,44 @@ proc handleCmdLine() = else: # Process command line arguments: processCmdLine(passCmd1, "") - if gProjectName != "": + if gProjectName == "-": + gProjectName = "stdinfile" + gProjectFull = "stdinfile" + gProjectPath = getCurrentDir() + gProjectIsStdin = true + elif gProjectName != "": try: gProjectFull = canonicalizePath(gProjectName) except OSError: gProjectFull = gProjectName - var p = splitFile(gProjectFull) + let p = splitFile(gProjectFull) gProjectPath = p.dir gProjectName = p.name else: gProjectPath = getCurrentDir() loadConfigs(DefaultConfig) # load all config files + let scriptFile = gProjectFull.changeFileExt("nims") + if fileExists(scriptFile): + runNimScript(scriptFile) + # 'nim foo.nims' means to just run the NimScript file and do nothing more: + if scriptFile == gProjectFull: return + elif fileExists(gProjectPath / "config.nims"): + # directory wide NimScript file + runNimScript(gProjectPath / "config.nims") # now process command line arguments again, because some options in the # command line can overwite the config file's settings extccomp.initVars() processCmdLine(passCmd2, "") + if options.command == "": + rawMessage(errNoCommand, command) mainCommand() - if gVerbosity >= 2: echo(GC_getStatistics()) + if optHints in gOptions and hintGCStats in gNotes: echo(GC_getStatistics()) #echo(GC_getStatistics()) if msgs.gErrorCounter == 0: when hasTinyCBackend: if gCmd == cmdRun: tccgen.run(commands.arguments) if optRun in gGlobalOptions: - if gProjectName == "-": - gProjectFull = "stdinfile" if gCmd == cmdCompileToJS: var ex: string if options.outFile.len > 0: diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim index 23f3331a2..c6c2ab058 100644 --- a/compiler/nimblecmd.nim +++ b/compiler/nimblecmd.nim @@ -11,8 +11,8 @@ import parseutils, strutils, strtabs, os, options, msgs, lists -proc addPath*(path: string, info: TLineInfo) = - if not contains(options.searchPaths, path): +proc addPath*(path: string, info: TLineInfo) = + if not contains(options.searchPaths, path): lists.prependStr(options.searchPaths, path) proc versionSplitPos(s: string): int = @@ -23,7 +23,7 @@ proc versionSplitPos(s: string): int = const latest = "head" -proc `<.`(a, b: string): bool = +proc `<.`(a, b: string): bool = # wether a has a smaller version than b: if a == latest: return false var i = 0 @@ -48,7 +48,7 @@ proc addPackage(packages: StringTableRef, p: string) = let name = p.substr(0, x-1) if x < p.len: let version = p.substr(x+1) - if packages[name] <. version: + if packages.getOrDefault(name) <. version: packages[name] = version else: packages[name] = latest @@ -60,7 +60,7 @@ iterator chosen(packages: StringTableRef): string = proc addNimblePath(p: string, info: TLineInfo) = if not contains(options.searchPaths, p): - if gVerbosity >= 1: message(info, hintPath, p) + message(info, hintPath, p) lists.prependStr(options.lazyPaths, p) proc addPathWithNimFiles(p: string, info: TLineInfo) = diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 711b476e6..496bd0123 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -9,106 +9,106 @@ # This module handles the reading of the config file. -import - llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer, +import + llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer, options, idents, wordrecg, strtabs # ---------------- configuration file parser ----------------------------- # we use Nim's scanner here to save space and work -proc ppGetTok(L: var TLexer, tok: var TToken) = +proc ppGetTok(L: var TLexer, tok: var TToken) = # simple filter rawGetTok(L, tok) while tok.tokType in {tkComment}: rawGetTok(L, tok) - + proc parseExpr(L: var TLexer, tok: var TToken): bool -proc parseAtom(L: var TLexer, tok: var TToken): bool = - if tok.tokType == tkParLe: +proc parseAtom(L: var TLexer, tok: var TToken): bool = + if tok.tokType == tkParLe: ppGetTok(L, tok) result = parseExpr(L, tok) if tok.tokType == tkParRi: ppGetTok(L, tok) else: lexMessage(L, errTokenExpected, "\')\'") - elif tok.ident.id == ord(wNot): + elif tok.ident.id == ord(wNot): ppGetTok(L, tok) result = not parseAtom(L, tok) else: result = isDefined(tok.ident) ppGetTok(L, tok) -proc parseAndExpr(L: var TLexer, tok: var TToken): bool = +proc parseAndExpr(L: var TLexer, tok: var TToken): bool = result = parseAtom(L, tok) - while tok.ident.id == ord(wAnd): + while tok.ident.id == ord(wAnd): ppGetTok(L, tok) # skip "and" var b = parseAtom(L, tok) result = result and b -proc parseExpr(L: var TLexer, tok: var TToken): bool = +proc parseExpr(L: var TLexer, tok: var TToken): bool = result = parseAndExpr(L, tok) - while tok.ident.id == ord(wOr): + while tok.ident.id == ord(wOr): ppGetTok(L, tok) # skip "or" var b = parseAndExpr(L, tok) result = result or b -proc evalppIf(L: var TLexer, tok: var TToken): bool = +proc evalppIf(L: var TLexer, tok: var TToken): bool = ppGetTok(L, tok) # skip 'if' or 'elif' result = parseExpr(L, tok) if tok.tokType == tkColon: ppGetTok(L, tok) else: lexMessage(L, errTokenExpected, "\':\'") - + var condStack: seq[bool] = @[] -proc doEnd(L: var TLexer, tok: var TToken) = +proc doEnd(L: var TLexer, tok: var TToken) = if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if") ppGetTok(L, tok) # skip 'end' setLen(condStack, high(condStack)) -type - TJumpDest = enum +type + TJumpDest = enum jdEndif, jdElseEndif proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) -proc doElse(L: var TLexer, tok: var TToken) = +proc doElse(L: var TLexer, tok: var TToken) = if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if") ppGetTok(L, tok) if tok.tokType == tkColon: ppGetTok(L, tok) if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif) - -proc doElif(L: var TLexer, tok: var TToken) = + +proc doElif(L: var TLexer, tok: var TToken) = if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if") var res = evalppIf(L, tok) if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif) else: condStack[high(condStack)] = true - -proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) = + +proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) = var nestedIfs = 0 - while true: + while true: if tok.ident != nil and tok.ident.s == "@": ppGetTok(L, tok) case whichKeyword(tok.ident) - of wIf: + of wIf: inc(nestedIfs) - of wElse: + of wElse: if dest == jdElseEndif and nestedIfs == 0: doElse(L, tok) - break - of wElif: + break + of wElif: if dest == jdElseEndif and nestedIfs == 0: doElif(L, tok) - break - of wEnd: - if nestedIfs == 0: + break + of wEnd: + if nestedIfs == 0: doEnd(L, tok) - break + break if nestedIfs > 0: dec(nestedIfs) - else: + else: discard ppGetTok(L, tok) elif tok.tokType == tkEof: lexMessage(L, errTokenExpected, "@end") else: ppGetTok(L, tok) - -proc parseDirective(L: var TLexer, tok: var TToken) = + +proc parseDirective(L: var TLexer, tok: var TToken) = ppGetTok(L, tok) # skip @ case whichKeyword(tok.ident) of wIf: @@ -126,13 +126,13 @@ proc parseDirective(L: var TLexer, tok: var TToken) = ppGetTok(L, tok) else: case tok.ident.s.normalize - of "putenv": + of "putenv": ppGetTok(L, tok) var key = tokToStr(tok) ppGetTok(L, tok) os.putEnv(key, tokToStr(tok)) ppGetTok(L, tok) - of "prependenv": + of "prependenv": ppGetTok(L, tok) var key = tokToStr(tok) ppGetTok(L, tok) @@ -145,17 +145,17 @@ proc parseDirective(L: var TLexer, tok: var TToken) = os.putEnv(key, os.getEnv(key) & tokToStr(tok)) ppGetTok(L, tok) else: lexMessage(L, errInvalidDirectiveX, tokToStr(tok)) - -proc confTok(L: var TLexer, tok: var TToken) = + +proc confTok(L: var TLexer, tok: var TToken) = ppGetTok(L, tok) - while tok.ident != nil and tok.ident.s == "@": + while tok.ident != nil and tok.ident.s == "@": parseDirective(L, tok) # else: give the token to the parser - -proc checkSymbol(L: TLexer, tok: TToken) = - if tok.tokType notin {tkSymbol..pred(tkIntLit), tkStrLit..tkTripleStrLit}: + +proc checkSymbol(L: TLexer, tok: TToken) = + if tok.tokType notin {tkSymbol..pred(tkIntLit), tkStrLit..tkTripleStrLit}: lexMessage(L, errIdentifierExpected, tokToStr(tok)) - -proc parseAssignment(L: var TLexer, tok: var TToken) = + +proc parseAssignment(L: var TLexer, tok: var TToken) = if tok.ident.id == getIdent("-").id or tok.ident.id == getIdent("--").id: confTok(L, tok) # skip unnecessary prefix var info = getLineInfo(L, tok) # save for later in case of an error @@ -163,13 +163,13 @@ proc parseAssignment(L: var TLexer, tok: var TToken) = var s = tokToStr(tok) confTok(L, tok) # skip symbol var val = "" - while tok.tokType == tkDot: + while tok.tokType == tkDot: add(s, '.') confTok(L, tok) checkSymbol(L, tok) add(s, tokToStr(tok)) confTok(L, tok) - if tok.tokType == tkBracketLe: + if tok.tokType == tkBracketLe: # BUGFIX: val, not s! # BUGFIX: do not copy '['! confTok(L, tok) @@ -180,7 +180,7 @@ proc parseAssignment(L: var TLexer, tok: var TToken) = else: lexMessage(L, errTokenExpected, "']'") add(val, ']') let percent = tok.ident.id == getIdent("%=").id - if tok.tokType in {tkColon, tkEquals} or percent: + if tok.tokType in {tkColon, tkEquals} or percent: if len(val) > 0: add(val, ':') confTok(L, tok) # skip ':' or '=' or '%' checkSymbol(L, tok) @@ -211,7 +211,7 @@ proc readConfigFile(filename: string) = while tok.tokType != tkEof: parseAssignment(L, tok) if len(condStack) > 0: lexMessage(L, errTokenExpected, "@end") closeLexer(L) - if gVerbosity >= 1: rawMessage(hintConf, filename) + rawMessage(hintConf, filename) proc getUserConfigPath(filename: string): string = result = joinPath(getConfigDir(), filename) @@ -226,15 +226,7 @@ proc getSystemConfigPath(filename: string): string = if not existsFile(result): result = "/etc/" & filename proc loadConfigs*(cfg: string) = - # set default value (can be overwritten): - if libpath == "": - # choose default libpath: - var prefix = getPrefixDir() - when defined(posix): - if prefix == "/usr": libpath = "/usr/lib/nim" - elif prefix == "/usr/local": libpath = "/usr/local/lib/nim" - else: libpath = joinPath(prefix, "lib") - else: libpath = joinPath(prefix, "lib") + setDefaultLibpath() if optSkipConfigFile notin gGlobalOptions: readConfigFile(getSystemConfigPath(cfg)) @@ -246,10 +238,10 @@ proc loadConfigs*(cfg: string) = if optSkipParentConfigFiles notin gGlobalOptions: for dir in parentDirs(pd, fromRoot=true, inclusive=false): readConfigFile(dir / cfg) - + if optSkipProjConfigFile notin gGlobalOptions: readConfigFile(pd / cfg) - + if gProjectName.len != 0: # new project wide config file: var projectConfig = changeFileExt(gProjectFull, "nimcfg") diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index 197e8bf86..2bddb76e7 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -10,7 +10,7 @@ ## exposes the Nim VM to clients. import - ast, modules, passes, passaux, condsyms, + ast, modules, passes, passaux, condsyms, options, nimconf, lists, sem, semdata, llstream, vm proc execute*(program: string) = diff --git a/compiler/nimfix/nimfix.nim b/compiler/nimfix/nimfix.nim index 8caa23ee3..39436702f 100644 --- a/compiler/nimfix/nimfix.nim +++ b/compiler/nimfix/nimfix.nim @@ -10,8 +10,10 @@ ## Nimfix is a tool that helps to convert old-style Nimrod code to Nim code. import strutils, os, parseopt -import options, commands, modules, sem, passes, passaux, pretty, msgs, nimconf, - extccomp, condsyms, lists +import compiler/options, compiler/commands, compiler/modules, compiler/sem, + compiler/passes, compiler/passaux, compiler/nimfix/pretty, + compiler/msgs, compiler/nimconf, + compiler/extccomp, compiler/condsyms, compiler/lists const Usage = """ Nimfix - Tool to patch Nim code @@ -24,7 +26,7 @@ Options: --wholeProject overwrite every processed file. --checkExtern:on|off style check also extern names --styleCheck:on|off|auto performs style checking for identifiers - and suggests an alternative spelling; + and suggests an alternative spelling; 'auto' corrects the spelling. --bestEffort try to fix the code even when there are errors. @@ -48,11 +50,11 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) = var p = parseopt.initOptParser(cmd) var argsCount = 0 gOnlyMainfile = true - while true: + while true: parseopt.next(p) case p.kind - of cmdEnd: break - of cmdLongoption, cmdShortOption: + of cmdEnd: break + of cmdLongoption, cmdShortOption: case p.key.normalize of "overwritefiles": case p.val.normalize @@ -80,7 +82,7 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) = proc handleCmdLine() = if paramCount() == 0: - stdout.writeln(Usage) + stdout.writeLine(Usage) else: processCmdLine(passCmd1, "") if gProjectName != "": diff --git a/compiler/nimfix/pretty.nim b/compiler/nimfix/pretty.nim index d2d5b5e83..1123afb9e 100644 --- a/compiler/nimfix/pretty.nim +++ b/compiler/nimfix/pretty.nim @@ -10,9 +10,11 @@ ## This module implements the code "prettifier". This is part of the toolchain ## to convert Nim code into a consistent style. -import - strutils, os, options, ast, astalgo, msgs, ropes, idents, - intsets, strtabs, semdata, prettybase +import + strutils, os, intsets, strtabs + +import compiler/options, compiler/ast, compiler/astalgo, compiler/msgs, + compiler/semdata, compiler/nimfix/prettybase, compiler/ropes, compiler/idents type StyleCheck* {.pure.} = enum None, Warn, Auto @@ -92,7 +94,7 @@ proc beautifyName(s: string, k: TSymKind): string = proc replaceInFile(info: TLineInfo; newName: string) = loadFile(info) - + let line = gSourceFiles[info.fileIndex].lines[info.line-1] var first = min(info.col.int, line.len) if first < 0: return @@ -100,18 +102,18 @@ proc replaceInFile(info: TLineInfo; newName: string) = while first > 0 and line[first-1] in prettybase.Letters: dec first if first < 0: return if line[first] == '`': inc first - + let last = first+identLen(line, first)-1 if differ(line, first, last, newName): - # last-first+1 != newName.len or - var x = line.substr(0, first-1) & newName & line.substr(last+1) + # last-first+1 != newName.len or + var x = line.substr(0, first-1) & newName & line.substr(last+1) system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x) gSourceFiles[info.fileIndex].dirty = true proc checkStyle(info: TLineInfo, s: string, k: TSymKind; sym: PSym) = let beau = beautifyName(s, k) if s != beau: - if gStyleCheck == StyleCheck.Auto: + if gStyleCheck == StyleCheck.Auto: sym.name = getIdent(beau) replaceInFile(info, beau) else: @@ -137,7 +139,7 @@ proc styleCheckUseImpl(info: TLineInfo; s: PSym) = if info.fileIndex < 0: return # we simply convert it to what it looks like in the definition # for consistency - + # operators stay as they are: if s.kind in {skResult, skTemp} or s.name.s[0] notin prettybase.Letters: return diff --git a/compiler/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim index 5130d1863..0f17cbcb1 100644 --- a/compiler/nimfix/prettybase.nim +++ b/compiler/nimfix/prettybase.nim @@ -7,7 +7,8 @@ # distribution, for details about the copyright. # -import ast, msgs, strutils, idents, lexbase, streams +import strutils, lexbase, streams +import compiler/ast, compiler/msgs, compiler/idents from os import splitFile type @@ -39,7 +40,7 @@ proc loadFile*(info: TLineInfo) = var pos = lex.bufpos while true: case lex.buf[pos] - of '\c': + of '\c': gSourceFiles[i].newline = "\c\L" break of '\L', '\0': @@ -70,7 +71,7 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) = while first > 0 and line[first-1] in Letters: dec first if first < 0: return if line[first] == '`': inc first - + let last = first+identLen(line, first)-1 if cmpIgnoreStyle(line[first..last], oldSym.s) == 0: var x = line.substr(0, first-1) & newSym.s & line.substr(last+1) diff --git a/compiler/nimlexbase.nim b/compiler/nimlexbase.nim index f5db5ca4f..047890c44 100644 --- a/compiler/nimlexbase.nim +++ b/compiler/nimlexbase.nim @@ -12,10 +12,10 @@ # handling that exists! Only at line endings checks are necessary # if the buffer needs refilling. -import +import llstream, strutils -const +const Lrz* = ' ' Apo* = '\'' Tabulator* = '\x09' @@ -27,7 +27,7 @@ const BACKSPACE* = '\x08' VT* = '\x0B' -const +const EndOfFile* = '\0' # end of file marker # A little picture makes everything clear :-) # buf: @@ -36,7 +36,7 @@ const # NewLines* = {CR, LF} -type +type TBaseLexer* = object of RootObj bufpos*: int buf*: cstring @@ -46,9 +46,9 @@ type # private data: sentinel*: int lineStart*: int # index of last line start in buffer - -proc openBaseLexer*(L: var TBaseLexer, inputstream: PLLStream, + +proc openBaseLexer*(L: var TBaseLexer, inputstream: PLLStream, bufLen: int = 8192) # 8K is a reasonable buffer size proc closeBaseLexer*(L: var TBaseLexer) @@ -64,15 +64,15 @@ proc handleLF*(L: var TBaseLexer, pos: int): int # of the LF. # implementation -const +const chrSize = sizeof(char) -proc closeBaseLexer(L: var TBaseLexer) = +proc closeBaseLexer(L: var TBaseLexer) = dealloc(L.buf) llStreamClose(L.stream) -proc fillBuffer(L: var TBaseLexer) = - var +proc fillBuffer(L: var TBaseLexer) = + var charsRead, toCopy, s: int # all are in characters, # not bytes (in case this # is not the same) @@ -82,68 +82,68 @@ proc fillBuffer(L: var TBaseLexer) = assert(L.sentinel < L.bufLen) toCopy = L.bufLen - L.sentinel - 1 assert(toCopy >= 0) - if toCopy > 0: - moveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize) + if toCopy > 0: + moveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize) # "moveMem" handles overlapping regions - charsRead = llStreamRead(L.stream, addr(L.buf[toCopy]), + charsRead = llStreamRead(L.stream, addr(L.buf[toCopy]), (L.sentinel + 1) * chrSize) div chrSize s = toCopy + charsRead - if charsRead < L.sentinel + 1: + if charsRead < L.sentinel + 1: L.buf[s] = EndOfFile # set end marker L.sentinel = s - else: + else: # compute sentinel: dec(s) # BUGFIX (valgrind) - while true: + while true: assert(s < L.bufLen) while (s >= 0) and not (L.buf[s] in NewLines): dec(s) - if s >= 0: + if s >= 0: # we found an appropriate character for a sentinel: L.sentinel = s - break - else: + break + else: # rather than to give up here because the line is too long, # double the buffer's size and try again: oldBufLen = L.bufLen L.bufLen = L.bufLen * 2 L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize)) assert(L.bufLen - oldBufLen == oldBufLen) - charsRead = llStreamRead(L.stream, addr(L.buf[oldBufLen]), + charsRead = llStreamRead(L.stream, addr(L.buf[oldBufLen]), oldBufLen * chrSize) div chrSize - if charsRead < oldBufLen: + if charsRead < oldBufLen: L.buf[oldBufLen + charsRead] = EndOfFile L.sentinel = oldBufLen + charsRead - break + break s = L.bufLen - 1 -proc fillBaseLexer(L: var TBaseLexer, pos: int): int = +proc fillBaseLexer(L: var TBaseLexer, pos: int): int = assert(pos <= L.sentinel) - if pos < L.sentinel: + if pos < L.sentinel: result = pos + 1 # nothing to do - else: + else: fillBuffer(L) L.bufpos = 0 # XXX: is this really correct? result = 0 L.lineStart = result -proc handleCR(L: var TBaseLexer, pos: int): int = +proc handleCR(L: var TBaseLexer, pos: int): int = assert(L.buf[pos] == CR) inc(L.lineNumber) result = fillBaseLexer(L, pos) - if L.buf[result] == LF: + if L.buf[result] == LF: result = fillBaseLexer(L, result) -proc handleLF(L: var TBaseLexer, pos: int): int = +proc handleLF(L: var TBaseLexer, pos: int): int = assert(L.buf[pos] == LF) inc(L.lineNumber) result = fillBaseLexer(L, pos) #L.lastNL := result-1; // BUGFIX: was: result; - -proc skipUTF8BOM(L: var TBaseLexer) = + +proc skipUTF8BOM(L: var TBaseLexer) = if L.buf[0] == '\xEF' and L.buf[1] == '\xBB' and L.buf[2] == '\xBF': inc(L.bufpos, 3) inc(L.lineStart, 3) -proc openBaseLexer(L: var TBaseLexer, inputstream: PLLStream, bufLen = 8192) = +proc openBaseLexer(L: var TBaseLexer, inputstream: PLLStream, bufLen = 8192) = assert(bufLen > 0) L.bufpos = 0 L.bufLen = bufLen @@ -155,15 +155,15 @@ proc openBaseLexer(L: var TBaseLexer, inputstream: PLLStream, bufLen = 8192) = fillBuffer(L) skipUTF8BOM(L) -proc getColNumber(L: TBaseLexer, pos: int): int = +proc getColNumber(L: TBaseLexer, pos: int): int = result = abs(pos - L.lineStart) -proc getCurrentLine(L: TBaseLexer, marker: bool = true): string = +proc getCurrentLine(L: TBaseLexer, marker: bool = true): string = result = "" var i = L.lineStart - while not (L.buf[i] in {CR, LF, EndOfFile}): + while not (L.buf[i] in {CR, LF, EndOfFile}): add(result, L.buf[i]) inc(i) result.add("\n") - if marker: + if marker: result.add(spaces(getColNumber(L, L.bufpos)) & '^' & "\n") diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim index aa7686d30..055bae909 100644 --- a/compiler/nimsets.nim +++ b/compiler/nimsets.nim @@ -9,7 +9,7 @@ # this unit handles Nim sets; it implements symbolic sets -import +import ast, astalgo, trees, nversion, msgs, platform, bitsets, types, renderer proc toBitSet*(s: PNode, b: var TBitSet) @@ -30,17 +30,17 @@ proc equalSets*(a, b: PNode): bool proc cardSet*(s: PNode): BiggestInt # implementation -proc inSet(s: PNode, elem: PNode): bool = - if s.kind != nkCurly: +proc inSet(s: PNode, elem: PNode): bool = + if s.kind != nkCurly: internalError(s.info, "inSet") return false - for i in countup(0, sonsLen(s) - 1): - if s.sons[i].kind == nkRange: + for i in countup(0, sonsLen(s) - 1): + if s.sons[i].kind == nkRange: if leValue(s.sons[i].sons[0], elem) and - leValue(elem, s.sons[i].sons[1]): + leValue(elem, s.sons[i].sons[1]): return true - else: - if sameValue(s.sons[i], elem): + else: + if sameValue(s.sons[i], elem): return true result = false @@ -58,37 +58,37 @@ proc overlap(a, b: PNode): bool = else: result = sameValue(a, b) -proc someInSet(s: PNode, a, b: PNode): bool = +proc someInSet(s: PNode, a, b: PNode): bool = # checks if some element of a..b is in the set s if s.kind != nkCurly: internalError(s.info, "SomeInSet") return false - for i in countup(0, sonsLen(s) - 1): - if s.sons[i].kind == nkRange: + for i in countup(0, sonsLen(s) - 1): + if s.sons[i].kind == nkRange: if leValue(s.sons[i].sons[0], b) and leValue(b, s.sons[i].sons[1]) or - leValue(s.sons[i].sons[0], a) and leValue(a, s.sons[i].sons[1]): + leValue(s.sons[i].sons[0], a) and leValue(a, s.sons[i].sons[1]): return true - else: + else: # a <= elem <= b - if leValue(a, s.sons[i]) and leValue(s.sons[i], b): + if leValue(a, s.sons[i]) and leValue(s.sons[i], b): return true result = false -proc toBitSet(s: PNode, b: var TBitSet) = +proc toBitSet(s: PNode, b: var TBitSet) = var first, j: BiggestInt first = firstOrd(s.typ.sons[0]) bitSetInit(b, int(getSize(s.typ))) - for i in countup(0, sonsLen(s) - 1): - if s.sons[i].kind == nkRange: + for i in countup(0, sonsLen(s) - 1): + if s.sons[i].kind == nkRange: j = getOrdValue(s.sons[i].sons[0]) - while j <= getOrdValue(s.sons[i].sons[1]): + while j <= getOrdValue(s.sons[i].sons[1]): bitSetIncl(b, j - first) inc(j) - else: + else: bitSetIncl(b, getOrdValue(s.sons[i]) - first) - -proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode = - var + +proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode = + var a, b, e, first: BiggestInt # a, b are interval borders elemType: PType n: PNode @@ -98,17 +98,17 @@ proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode = result.typ = settype result.info = info e = 0 - while e < len(s) * ElemSize: - if bitSetIn(s, e): + while e < len(s) * ElemSize: + if bitSetIn(s, e): a = e b = e - while true: + while true: inc(b) - if (b >= len(s) * ElemSize) or not bitSetIn(s, b): break + if (b >= len(s) * ElemSize) or not bitSetIn(s, b): break dec(b) - if a == b: + if a == b: addSon(result, newIntTypeNode(nkIntLit, a + first, elemType)) - else: + else: n = newNodeI(nkRange, info) n.typ = elemType addSon(n, newIntTypeNode(nkIntLit, a + first, elemType)) @@ -117,7 +117,7 @@ proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode = e = b inc(e) -template nodeSetOp(a, b: PNode, op: expr) {.dirty.} = +template nodeSetOp(a, b: PNode, op: expr) {.dirty.} = var x, y: TBitSet toBitSet(a, x) toBitSet(b, y) @@ -129,13 +129,13 @@ proc diffSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetDiff) proc intersectSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetIntersect) proc symdiffSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff) -proc containsSets(a, b: PNode): bool = +proc containsSets(a, b: PNode): bool = var x, y: TBitSet toBitSet(a, x) toBitSet(b, y) result = bitSetContains(x, y) -proc equalSets(a, b: PNode): bool = +proc equalSets(a, b: PNode): bool = var x, y: TBitSet toBitSet(a, x) toBitSet(b, y) @@ -147,26 +147,26 @@ proc complement*(a: PNode): PNode = for i in countup(0, high(x)): x[i] = not x[i] result = toTreeSet(x, a.typ, a.info) -proc cardSet(s: PNode): BiggestInt = +proc cardSet(s: PNode): BiggestInt = # here we can do better than converting it into a compact set # we just count the elements directly result = 0 - for i in countup(0, sonsLen(s) - 1): - if s.sons[i].kind == nkRange: + for i in countup(0, sonsLen(s) - 1): + if s.sons[i].kind == nkRange: result = result + getOrdValue(s.sons[i].sons[1]) - getOrdValue(s.sons[i].sons[0]) + 1 - else: + else: inc(result) - -proc setHasRange(s: PNode): bool = + +proc setHasRange(s: PNode): bool = if s.kind != nkCurly: internalError(s.info, "SetHasRange") return false - for i in countup(0, sonsLen(s) - 1): - if s.sons[i].kind == nkRange: + for i in countup(0, sonsLen(s) - 1): + if s.sons[i].kind == nkRange: return true result = false -proc emptyRange(a, b: PNode): bool = +proc emptyRange(a, b: PNode): bool = result = not leValue(a, b) # a > b iff not (a <= b) - + diff --git a/compiler/nimsuggest/nimsuggest.nim b/compiler/nimsuggest/nimsuggest.nim index 8285d81d9..2be368d68 100644 --- a/compiler/nimsuggest/nimsuggest.nim +++ b/compiler/nimsuggest/nimsuggest.nim @@ -7,192 +7,6 @@ # distribution, for details about the copyright. # -## Nimsuggest is a tool that helps to give editors IDE like capabilities. +## Nimsuggest has been moved to https://github.com/nim-lang/nimsuggest -import strutils, os, parseopt, parseUtils -import options, commands, modules, sem, passes, passaux, msgs, nimconf, - extccomp, condsyms, lists, net, rdstdin - -const Usage = """ -Nimsuggest - Tool to give every editor IDE like capabilities for Nim -Usage: - nimsuggest [options] projectfile.nim - -Options: - --port:PORT port, by default 6000 - --address:HOST binds to that address, by default "" - --stdin read commands from stdin and write results to - stdout instead of using sockets - -The server then listens to the connection and takes line-based commands. - -In addition, all command line options of Nim that do not affect code generation -are supported. -""" - -var - gPort = 6000.Port - gAddress = "" - gUseStdin: bool - -const - seps = {':', ';', ' ', '\t'} - Help = "usage: sug|con|def|use file.nim[;dirtyfile.nim]:line:col\n"& - "type 'quit' to quit\n" & - "type 'debug' to toggle debug mode on/off\n" & - "type 'terse' to toggle terse mode on/off" - -proc parseQuoted(cmd: string; outp: var string; start: int): int = - var i = start - i += skipWhitespace(cmd, i) - if cmd[i] == '"': - i += parseUntil(cmd, outp, '"', i+1)+2 - else: - i += parseUntil(cmd, outp, seps, i) - result = i - -proc action(cmd: string) = - template toggle(sw) = - if sw in gGlobalOptions: - excl(gGlobalOptions, sw) - else: - incl(gGlobalOptions, sw) - return - - template err() = - echo Help - return - - var opc = "" - var i = parseIdent(cmd, opc, 0) - case opc.normalize - of "sug": gIdeCmd = ideSug - of "con": gIdeCmd = ideCon - of "def": gIdeCmd = ideDef - of "use": - modules.resetAllModules() - gIdeCmd = ideUse - of "quit": quit() - of "debug": toggle optIdeDebug - of "terse": toggle optIdeTerse - else: err() - var dirtyfile = "" - var orig = "" - i = parseQuoted(cmd, orig, i) - if cmd[i] == ';': - i = parseQuoted(cmd, dirtyfile, i+1) - i += skipWhile(cmd, seps, i) - var line = -1 - var col = 0 - i += parseInt(cmd, line, i) - i += skipWhile(cmd, seps, i) - i += parseInt(cmd, col, i) - - var isKnownFile = true - if orig.len == 0: err() - let dirtyIdx = orig.fileInfoIdx(isKnownFile) - - if dirtyfile.len != 0: msgs.setDirtyFile(dirtyIdx, dirtyfile) - else: msgs.setDirtyFile(dirtyIdx, nil) - - resetModule dirtyIdx - if dirtyIdx != gProjectMainIdx: - resetModule gProjectMainIdx - gTrackPos = newLineInfo(dirtyIdx, line, col-1) - #echo dirtyfile, gDirtyBufferIdx, " project ", gProjectMainIdx - gErrorCounter = 0 - if not isKnownFile: - compileProject(dirtyIdx) - else: - compileProject() - -proc serve() = - # do not stop after the first error: - msgs.gErrorMax = high(int) - if gUseStdin: - echo Help - var line = "" - while readLineFromStdin("> ", line): - action line - echo "" - flushFile(stdout) - else: - var server = newSocket() - server.bindAddr(gPort, gAddress) - var inp = "".TaintedString - server.listen() - - while true: - var stdoutSocket = newSocket() - msgs.writelnHook = proc (line: string) = - stdoutSocket.send(line & "\c\L") - - accept(server, stdoutSocket) - - stdoutSocket.readLine(inp) - action inp.string - - stdoutSocket.send("\c\L") - stdoutSocket.close() - -proc mainCommand = - registerPass verbosePass - registerPass semPass - gCmd = cmdIdeTools - incl gGlobalOptions, optCaasEnabled - isServing = true - wantMainModule() - appendStr(searchPaths, options.libpath) - if gProjectFull.len != 0: - # current path is always looked first for modules - prependStr(searchPaths, gProjectPath) - - serve() - -proc processCmdLine*(pass: TCmdLinePass, cmd: string) = - var p = parseopt.initOptParser(cmd) - while true: - parseopt.next(p) - case p.kind - of cmdEnd: break - of cmdLongoption, cmdShortOption: - case p.key.normalize - of "port": gPort = parseInt(p.val).Port - of "address": gAddress = p.val - of "stdin": gUseStdin = true - else: processSwitch(pass, p) - of cmdArgument: - options.gProjectName = unixToNativePath(p.key) - # if processArgument(pass, p, argsCount): break - -proc handleCmdLine() = - if paramCount() == 0: - stdout.writeln(Usage) - else: - processCmdLine(passCmd1, "") - if gProjectName != "": - try: - gProjectFull = canonicalizePath(gProjectName) - except OSError: - gProjectFull = gProjectName - var p = splitFile(gProjectFull) - gProjectPath = p.dir - gProjectName = p.name - else: - gProjectPath = getCurrentDir() - loadConfigs(DefaultConfig) # load all config files - # now process command line arguments again, because some options in the - # command line can overwite the config file's settings - extccomp.initVars() - processCmdLine(passCmd2, "") - mainCommand() - -when false: - proc quitCalled() {.noconv.} = - writeStackTrace() - - addQuitProc(quitCalled) - -condsyms.initDefines() -defineSymbol "nimsuggest" -handleCmdline() +{.error: "This project has moved to the following repo: https://github.com/nim-lang/nimsuggest".} diff --git a/compiler/nimsuggest/nimsuggest.nim.cfg b/compiler/nimsuggest/nimsuggest.nim.cfg deleted file mode 100644 index acca17396..000000000 --- a/compiler/nimsuggest/nimsuggest.nim.cfg +++ /dev/null @@ -1,17 +0,0 @@ -# Special configuration file for the Nim project - -gc:markAndSweep - -hint[XDeclaredButNotUsed]:off -path:"$projectPath/../.." - -path:"$lib/packages/docutils" -path:"../../compiler" - -define:useStdoutAsStdmsg -define:nimsuggest - -cs:partial -#define:useNodeIds -define:booting -#define:noDocgen diff --git a/compiler/nodejs.nim b/compiler/nodejs.nim index e2b79df19..7f9f28aaf 100644 --- a/compiler/nodejs.nim +++ b/compiler/nodejs.nim @@ -4,3 +4,5 @@ proc findNodeJs*(): string = result = findExe("nodejs") if result == "": result = findExe("node") + if result == "": + result = findExe("iojs") diff --git a/compiler/nversion.nim b/compiler/nversion.nim index 4dea62876..adeb0fb6d 100644 --- a/compiler/nversion.nim +++ b/compiler/nversion.nim @@ -10,7 +10,7 @@ # This module contains Nim's version. It is the only place where it needs # to be changed. -const +const MaxSetElements* = 1 shl 16 # (2^16) to support unicode character sets? VersionAsString* = system.NimVersion RodFileVersion* = "1215" # modify this if the rod-format changes! diff --git a/compiler/options.nim b/compiler/options.nim index 998ab7781..6dd917ad4 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -13,6 +13,7 @@ import const hasTinyCBackend* = defined(tinyc) useEffectSystem* = true + useWriteTracking* = false hasFFI* = defined(useFFI) newScopeForIf* = true useCaas* = not defined(noCaas) @@ -39,7 +40,7 @@ type # please make sure we have under 32 options TGlobalOption* = enum # **keep binary compatible** gloptNone, optForceFullMake, optDeadCodeElim, optListCmd, optCompileOnly, optNoLinking, - optSafeCode, # only allow safe code + optReportConceptFailures, # report 'compiles' or 'concept' matching failures optCDebug, # turn on debugging information optGenDynLib, # generate a dynamic library optGenStaticLib, # generate a static library @@ -54,6 +55,7 @@ type # please make sure we have under 32 options optSkipUserConfigFile, # skip the users's config file optSkipParentConfigFiles, # skip parent dir's config files optNoMain, # do not generate a "main" proc + optUseColors, # use colors for hints, warnings, and errors optThreads, # support for multi-threading optStdout, # output to stdout optThreadAnalysis, # thread analysis pass @@ -81,13 +83,13 @@ type # please make sure we have under 32 options cmdRun # run the project via TCC backend TStringSeq* = seq[string] TGCMode* = enum # the selected GC - gcNone, gcBoehm, gcMarkAndSweep, gcRefc, gcV2, gcGenerational + gcNone, gcBoehm, gcGo, gcMarkAndSweep, gcRefc, gcV2, gcGenerational - TIdeCmd* = enum - ideNone, ideSug, ideCon, ideDef, ideUse + IdeCmd* = enum + ideNone, ideSug, ideCon, ideDef, ideUse, ideDus var - gIdeCmd*: TIdeCmd + gIdeCmd*: IdeCmd const ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck, @@ -126,9 +128,6 @@ template compilationCachePresent*: expr = template optPreserveOrigSource*: expr = optEmbedOrigSrc in gGlobalOptions -template optPrintSurroundingSrc*: expr = - gVerbosity >= 2 - const genSubDir* = "nimcache" NimExt* = "nim" @@ -145,10 +144,12 @@ const var gConfigVars* = newStringTable(modeStyleInsensitive) gDllOverrides = newStringTable(modeCaseInsensitive) + gPrefixDir* = "" # Overrides the default prefix dir in getPrefixDir proc. libpath* = "" gProjectName* = "" # holds a name like 'nimrod' gProjectPath* = "" # holds a path like /home/alice/projects/nimrod/compiler/ gProjectFull* = "" # projectPath/projectName + gProjectIsStdin* = false # whether we're compiling from stdin gProjectMainIdx*: int32 # the canonical path id of the main module nimcacheDir* = "" command* = "" # the main command (e.g. cc, check, scan, etc) @@ -172,7 +173,7 @@ proc existsConfigVar*(key: string): bool = result = hasKey(gConfigVars, key) proc getConfigVar*(key: string): string = - result = gConfigVars[key] + result = gConfigVars.getOrDefault key proc setConfigVar*(key, val: string) = gConfigVars[key] = val @@ -182,8 +183,24 @@ proc getOutFile*(filename, ext: string): string = else: result = changeFileExt(filename, ext) proc getPrefixDir*(): string = - ## gets the application directory - result = splitPath(getAppDir()).head + ## Gets the prefix dir, usually the parent directory where the binary resides. + ## + ## This is overrided by some tools (namely nimsuggest) via the ``gPrefixDir`` + ## global. + if gPrefixDir != "": result = gPrefixDir + else: + result = splitPath(getAppDir()).head + +proc setDefaultLibpath*() = + # set default value (can be overwritten): + if libpath == "": + # choose default libpath: + var prefix = getPrefixDir() + when defined(posix): + if prefix == "/usr": libpath = "/usr/lib/nim" + elif prefix == "/usr/local": libpath = "/usr/local/lib/nim" + else: libpath = joinPath(prefix, "lib") + else: libpath = joinPath(prefix, "lib") proc canonicalizePath*(path: string): string = when not FileSystemCaseSensitive: result = path.expandFilename.toLower @@ -205,7 +222,7 @@ proc removeTrailingDirSep*(path: string): string = else: result = path -proc getGeneratedPath: string = +proc getNimcacheDir*: string = result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir / genSubDir @@ -261,7 +278,7 @@ proc toGeneratedFile*(path, ext: string): string = ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod" var (head, tail) = splitPath(path) #if len(head) > 0: head = shortenDir(head & dirSep) - result = joinPath([getGeneratedPath(), changeFileExt(tail, ext)]) + result = joinPath([getNimcacheDir(), changeFileExt(tail, ext)]) #echo "toGeneratedFile(", path, ", ", ext, ") = ", result when noTimeMachine: @@ -289,14 +306,14 @@ when noTimeMachine: proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string = var (head, tail) = splitPath(f) #if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep)) - var subdir = getGeneratedPath() # / head + var subdir = getNimcacheDir() # / head if createSubDir: try: createDir(subdir) when noTimeMachine: - excludeDirFromTimeMachine(subdir) + excludeDirFromTimeMachine(subdir) except OSError: - writeln(stdout, "cannot create directory: " & subdir) + writeLine(stdout, "cannot create directory: " & subdir) quit(1) result = joinPath(subdir, tail) #echo "completeGeneratedFilePath(", f, ") = ", result @@ -325,13 +342,16 @@ proc rawFindFile2(f: string): string = result = "" proc findFile*(f: string): string {.procvar.} = - result = f.rawFindFile - if result.len == 0: - result = f.toLower.rawFindFile + if f.isAbsolute: + result = if f.existsFile: f else: "" + else: + result = f.rawFindFile if result.len == 0: - result = f.rawFindFile2 + result = f.toLower.rawFindFile if result.len == 0: - result = f.toLower.rawFindFile2 + result = f.rawFindFile2 + if result.len == 0: + result = f.toLower.rawFindFile2 proc findModule*(modulename, currentModule: string): string = # returns path to module @@ -395,3 +415,20 @@ template cnimdbg*: expr = p.module.module.fileIdx == gProjectMainIdx template pnimdbg*: expr = p.lex.fileIdx == gProjectMainIdx template lnimdbg*: expr = L.fileIdx == gProjectMainIdx +proc parseIdeCmd*(s: string): IdeCmd = + case s: + of "sug": ideSug + of "con": ideCon + of "def": ideDef + of "use": ideUse + of "dus": ideDus + else: ideNone + +proc `$`*(c: IdeCmd): string = + case c: + of ideSug: "sug" + of ideCon: "con" + of ideDef: "def" + of ideUse: "use" + of ideDus: "dus" + of ideNone: "none" diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index b7fe269df..978583c14 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -178,13 +178,14 @@ type arDiscriminant, # is a discriminant arStrange # it is a strange beast like 'typedesc[var T]' -proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = +proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult = ## 'owner' can be nil! result = arNone case n.kind of nkSym: - # don't list 'skLet' here: - if n.sym.kind in {skVar, skResult, skTemp}: + let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet} + else: {skVar, skResult, skTemp} + if n.sym.kind in kinds: if owner != nil and owner.id == n.sym.owner.id and sfGlobal notin n.sym.flags: result = arLocalLValue @@ -200,7 +201,7 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = {tyVar, tyPtr, tyRef}: result = arLValue else: - result = isAssignable(owner, n.sons[0]) + result = isAssignable(owner, n.sons[0], isUnsafeAddr) if result != arNone and sfDiscriminant in n.sons[1].sym.flags: result = arDiscriminant of nkBracketExpr: @@ -208,23 +209,27 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = {tyVar, tyPtr, tyRef}: result = arLValue else: - result = isAssignable(owner, n.sons[0]) + result = isAssignable(owner, n.sons[0], isUnsafeAddr) of nkHiddenStdConv, nkHiddenSubConv, nkConv: # Object and tuple conversions are still addressable, so we skip them # XXX why is 'tyOpenArray' allowed here? if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in {tyOpenArray, tyTuple, tyObject}: - result = isAssignable(owner, n.sons[1]) + result = isAssignable(owner, n.sons[1], isUnsafeAddr) elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct): # types that are equal modulo distinction preserve l-value: - result = isAssignable(owner, n.sons[1]) + result = isAssignable(owner, n.sons[1], isUnsafeAddr) of nkHiddenDeref, nkDerefExpr, nkHiddenAddr: result = arLValue of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: - result = isAssignable(owner, n.sons[0]) + result = isAssignable(owner, n.sons[0], isUnsafeAddr) of nkCallKinds: # builtin slice keeps lvalue-ness: - if getMagic(n) == mSlice: result = isAssignable(owner, n.sons[1]) + if getMagic(n) in {mArrGet, mSlice}: + result = isAssignable(owner, n.sons[1], isUnsafeAddr) + of nkStmtList, nkStmtListExpr: + if n.typ != nil: + result = isAssignable(owner, n.lastSon, isUnsafeAddr) else: discard diff --git a/compiler/parser.nim b/compiler/parser.nim index 0d2ba7cfc..dbf9706ea 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -64,6 +64,7 @@ proc setBaseFlags*(n: PNode, base: TNumericalBase) proc parseSymbol*(p: var TParser, allowNil = false): PNode proc parseTry(p: var TParser; isExpr: bool): PNode proc parseCase(p: var TParser): PNode +proc parseStmtPragma(p: var TParser): PNode # implementation proc getTok(p: var TParser) = @@ -124,6 +125,9 @@ proc rawSkipComment(p: var TParser, node: PNode) = proc skipComment(p: var TParser, node: PNode) = if p.tok.indent < 0: rawSkipComment(p, node) +proc flexComment(p: var TParser, node: PNode) = + if p.tok.indent < 0 or realInd(p): rawSkipComment(p, node) + proc skipInd(p: var TParser) = if p.tok.indent >= 0: if not realInd(p): parMessage(p, errInvalidIndentation) @@ -499,10 +503,13 @@ proc parsePar(p: var TParser): PNode = #| parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try' #| | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let' #| | 'when' | 'var' | 'mixin' - #| par = '(' optInd (&parKeyw complexOrSimpleStmt ^+ ';' - #| | simpleExpr ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )? - #| | (':' expr)? (',' (exprColonEqExpr comma?)*)? )? - #| optPar ')' + #| par = '(' optInd + #| ( &parKeyw complexOrSimpleStmt ^+ ';' + #| | ';' complexOrSimpleStmt ^+ ';' + #| | pragmaStmt + #| | simpleExpr ( ('=' expr (';' complexOrSimpleStmt ^+ ';' )? ) + #| | (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) ) + #| optPar ')' # # unfortunately it's ambiguous: (expr: expr) vs (exprStmt); however a # leading ';' could be used to enforce a 'stmt' context ... @@ -521,6 +528,8 @@ proc parsePar(p: var TParser): PNode = getTok(p) optInd(p, result) semiStmtList(p, result) + elif p.tok.tokType == tkCurlyDotLe: + result.add(parseStmtPragma(p)) elif p.tok.tokType != tkParRi: var a = simpleExpr(p) if p.tok.tokType == tkEquals: @@ -881,12 +890,13 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode = skipComment(p, result) if realInd(p): withInd(p): - skipComment(p, result) + rawSkipComment(p, result) while true: case p.tok.tokType of tkSymbol, tkAccent: var a = parseIdentColonEquals(p, {}) - skipComment(p, a) + if p.tok.indent < 0 or p.tok.indent >= p.currInd: + rawSkipComment(p, a) addSon(result, a) of tkEof: break else: @@ -1602,6 +1612,7 @@ proc parseEnum(p: var TParser): PNode = getTok(p) addSon(result, ast.emptyNode) optInd(p, result) + flexComment(p, result) while true: var a = parseSymbol(p) if a.kind == nkEmpty: return @@ -1615,12 +1626,14 @@ proc parseEnum(p: var TParser): PNode = a = newNodeP(nkEnumFieldDef, p) addSon(a, b) addSon(a, parseExpr(p)) - skipComment(p, a) + if p.tok.indent < 0 or p.tok.indent >= p.currInd: + rawSkipComment(p, a) if p.tok.tokType == tkComma and p.tok.indent < 0: getTok(p) rawSkipComment(p, a) else: - skipComment(p, a) + if p.tok.indent < 0 or p.tok.indent >= p.currInd: + rawSkipComment(p, a) addSon(result, a) if p.tok.indent >= 0 and p.tok.indent <= p.currInd or p.tok.tokType == tkEof: @@ -1641,7 +1654,7 @@ proc parseObjectWhen(p: var TParser): PNode = addSon(branch, parseExpr(p)) colcom(p, branch) addSon(branch, parseObjectPart(p)) - skipComment(p, branch) + flexComment(p, branch) addSon(result, branch) if p.tok.tokType != tkElif: break if p.tok.tokType == tkElse and sameInd(p): @@ -1649,7 +1662,7 @@ proc parseObjectWhen(p: var TParser): PNode = eat(p, tkElse) colcom(p, branch) addSon(branch, parseObjectPart(p)) - skipComment(p, branch) + flexComment(p, branch) addSon(result, branch) proc parseObjectCase(p: var TParser): PNode = @@ -1669,7 +1682,7 @@ proc parseObjectCase(p: var TParser): PNode = addSon(a, ast.emptyNode) addSon(result, a) if p.tok.tokType == tkColon: getTok(p) - skipComment(p, result) + flexComment(p, result) var wasIndented = false let oldInd = p.currInd if realInd(p): @@ -1718,7 +1731,8 @@ proc parseObjectPart(p: var TParser): PNode = result = parseObjectCase(p) of tkSymbol, tkAccent: result = parseIdentColonEquals(p, {withPragma}) - skipComment(p, result) + if p.tok.indent < 0 or p.tok.indent >= p.currInd: + rawSkipComment(p, result) of tkNil, tkDiscard: result = newNodeP(nkNilLit, p) getTok(p) diff --git a/compiler/passaux.nim b/compiler/passaux.nim index f754c80e5..9b9fdca4e 100644 --- a/compiler/passaux.nim +++ b/compiler/passaux.nim @@ -9,38 +9,38 @@ ## implements some little helper passes -import +import strutils, ast, astalgo, passes, msgs, options, idgen proc verboseOpen(s: PSym): PPassContext = #MessageOut('compiling ' + s.name.s); result = nil # we don't need a context - if gVerbosity > 0: rawMessage(hintProcessing, s.name.s) - -proc verboseProcess(context: PPassContext, n: PNode): PNode = + rawMessage(hintProcessing, s.name.s) + +proc verboseProcess(context: PPassContext, n: PNode): PNode = result = n if context != nil: internalError("logpass: context is not nil") - if gVerbosity == 3: + if gVerbosity == 3: # system.nim deactivates all hints, for verbosity:3 we want the processing # messages nonetheless, so we activate them again unconditionally: incl(msgs.gNotes, hintProcessing) message(n.info, hintProcessing, $idgen.gBackendId) - + const verbosePass* = makePass(open = verboseOpen, process = verboseProcess) -proc cleanUp(c: PPassContext, n: PNode): PNode = +proc cleanUp(c: PPassContext, n: PNode): PNode = result = n # we cannot clean up if dead code elimination is activated - if optDeadCodeElim in gGlobalOptions or n == nil: return + if optDeadCodeElim in gGlobalOptions or n == nil: return case n.kind - of nkStmtList: + of nkStmtList: for i in countup(0, sonsLen(n) - 1): discard cleanUp(c, n.sons[i]) - of nkProcDef, nkMethodDef: - if n.sons[namePos].kind == nkSym: + of nkProcDef, nkMethodDef: + if n.sons[namePos].kind == nkSym: var s = n.sons[namePos].sym - if sfDeadCodeElim notin getModule(s).flags and not astNeeded(s): + if sfDeadCodeElim notin getModule(s).flags and not astNeeded(s): s.ast.sons[bodyPos] = ast.emptyNode # free the memory - else: + else: discard const cleanupPass* = makePass(process = cleanUp, close = cleanUp) diff --git a/compiler/passes.nim b/compiler/passes.nim index 129d8ad47..ceb3e2b8a 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -170,11 +170,7 @@ proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) = openPasses(a, module) if stream == nil: let filename = fileIdx.toFullPathConsiderDirty - if module.name.s == "-": - module.name.s = "stdinfile" - s = llStreamOpen(stdin) - else: - s = llStreamOpen(filename, fmRead) + s = llStreamOpen(filename, fmRead) if s == nil: rawMessage(errCannotOpenFile, filename) return @@ -194,8 +190,17 @@ proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) = while true: var n = parseTopLevelStmt(p) if n.kind == nkEmpty: break - if not processTopLevelStmt(n, a): break - + if sfNoForward in module.flags: + # read everything, no streaming possible + var sl = newNodeI(nkStmtList, n.info) + sl.add n + while true: + var n = parseTopLevelStmt(p) + if n.kind == nkEmpty: break + sl.add n + discard processTopLevelStmt(sl, a) + break + elif not processTopLevelStmt(n, a): break closeParsers(p) if s.kind != llsStdIn: break closePasses(a) diff --git a/compiler/patterns.nim b/compiler/patterns.nim index 368b0b37b..604d3521d 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -68,7 +68,7 @@ proc inSymChoice(sc, x: PNode): bool = elif sc.kind == nkOpenSymChoice: # same name suffices for open sym choices! result = sc.sons[0].sym.name.id == x.sym.name.id - + proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool = # check param constraints first here as this is quite optimized: if p.constraint != nil: @@ -115,13 +115,13 @@ proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool = if rpn: arglist.add(n.sons[0]) elif n.kind == nkHiddenStdConv and n.sons[1].kind == nkBracket: let n = n.sons[1] - for i in 0.. <n.len: + for i in 0.. <n.len: if not matchStarAux(c, op, n[i], arglist, rpn): return false elif checkTypes(c, p.sons[2].sym, n): add(arglist, n) else: result = false - + if n.kind notin nkCallKinds: return false if matches(c, p.sons[1], n.sons[0]): var arglist = newNodeI(nkArgList, n.info) @@ -130,7 +130,9 @@ proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool = proc matches(c: PPatternContext, p, n: PNode): bool = # hidden conversions (?) - if isPatternParam(c, p): + if nfNoRewrite in n.flags: + result = false + elif isPatternParam(c, p): result = bindOrCheck(c, p.sym, n) elif n.kind == nkSym and p.kind == nkIdent: result = p.ident.id == n.sym.name.id @@ -149,7 +151,7 @@ proc matches(c: PPatternContext, p, n: PNode): bool = of "**": result = matchNested(c, p, n, rpn=true) of "~": result = not matches(c, p.sons[1], n) else: internalError(p.info, "invalid pattern") - # template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) = + # template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) = # add(a, b) elif p.kind == nkCurlyExpr: if p.sons[1].kind == nkPrefix: @@ -210,7 +212,7 @@ proc matchStmtList(c: PPatternContext, p, n: PNode): PNode = if not isNil(c.mapping): c.mapping = nil return false result = true - + if p.kind == nkStmtList and n.kind == p.kind and p.len < n.len: let n = flattenStmts(n) # no need to flatten 'p' here as that has already been done diff --git a/compiler/pbraces.nim b/compiler/pbraces.nim index cf4dbffc5..00f83a11e 100644 --- a/compiler/pbraces.nim +++ b/compiler/pbraces.nim @@ -7,12 +7,12 @@ # distribution, for details about the copyright. # -import +import llstream, lexer, parser, idents, strutils, ast, msgs -proc parseAll*(p: var TParser): PNode = +proc parseAll*(p: var TParser): PNode = result = nil -proc parseTopLevelStmt*(p: var TParser): PNode = +proc parseTopLevelStmt*(p: var TParser): PNode = result = nil diff --git a/compiler/platform.nim b/compiler/platform.nim index 4dd5d8836..8376c2b32 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -158,8 +158,8 @@ type TSystemCPU* = enum # Also add CPU for in initialization section and # alias conditionals to condsyms (end of module). cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64, - cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuArm, - cpuJS, cpuNimrodVM, cpuAVR + cpuPowerpc64el, cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuMipsel, + cpuArm, cpuArm64, cpuJS, cpuNimrodVM, cpuAVR, cpuMSP430 type TEndian* = enum @@ -175,15 +175,19 @@ const (name: "alpha", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), (name: "powerpc", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), (name: "powerpc64", intSize: 64, endian: bigEndian, floatSize: 64,bit: 64), + (name: "powerpc64el", intSize: 64, endian: littleEndian, floatSize: 64,bit: 64), (name: "sparc", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), (name: "vm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), (name: "ia64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), (name: "amd64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), (name: "mips", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), + (name: "mipsel", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), (name: "arm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), + (name: "arm64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), (name: "js", intSize: 32, endian: bigEndian,floatSize: 64,bit: 32), (name: "nimrodvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), - (name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16)] + (name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16), + (name: "msp430", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16)] var targetCPU*, hostCPU*: TSystemCPU diff --git a/compiler/plugins/locals/locals.nim b/compiler/plugins/locals/locals.nim index d89149f33..59e3d677d 100644 --- a/compiler/plugins/locals/locals.nim +++ b/compiler/plugins/locals/locals.nim @@ -9,7 +9,8 @@ ## The builtin 'system.locals' implemented as a plugin. -import plugins, ast, astalgo, magicsys, lookups, semdata, lowerings +import compiler/plugins, compiler/ast, compiler/astalgo, compiler/magicsys, + compiler/lookups, compiler/semdata, compiler/lowerings proc semLocals(c: PContext, n: PNode): PNode = var counter = 0 diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index c048d78e9..79d7884fa 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -25,19 +25,20 @@ const wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl, wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe, - wOverride, wConstructor} + wOverride, wConstructor, wExportNims} converterPragmas* = procPragmas - methodPragmas* = procPragmas + methodPragmas* = procPragmas+{wBase} templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty, - wDelegator} + wDelegator, wExportNims} macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc, wNodecl, wMagic, wNosideeffect, wCompilerproc, wDeprecated, wExtern, - wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator} + wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator, + wExportNims} iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect, wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern, wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises, - wTags, wLocks, wGcSafe} - exprPragmas* = {wLine, wLocks} + wTags, wLocks, wGcSafe, wExportNims} + exprPragmas* = {wLine, wLocks, wNoRewrite} stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError, @@ -54,15 +55,15 @@ const wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow, wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef, wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked, - wBorrow, wGcSafe} + wBorrow, wGcSafe, wExportNims} fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, - wImportCpp, wImportObjC, wError, wGuard} + wImportCpp, wImportObjC, wError, wGuard, wBitsize} varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern, wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal, - wGensym, wInject, wCodegenDecl, wGuard, wGoto} + wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims} constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl, - wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject} + wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims} letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect, wThread, wRaises, wLocks, wTags, wGcSafe} @@ -276,7 +277,8 @@ proc processNote(c: PContext, n: PNode) = if (n.kind == nkExprColonExpr) and (sonsLen(n) == 2) and (n.sons[0].kind == nkBracketExpr) and (n.sons[0].sons[1].kind == nkIdent) and - (n.sons[0].sons[0].kind == nkIdent) and (n.sons[1].kind == nkIdent): + (n.sons[0].sons[0].kind == nkIdent): + #and (n.sons[1].kind == nkIdent): var nk: TNoteKind case whichKeyword(n.sons[0].sons[0].ident) of wHint: @@ -394,6 +396,8 @@ proc processCompile(c: PContext, n: PNode) = var found = findFile(s) if found == "": found = s var trunc = changeFileExt(found, "") + if not isAbsolute(found): + found = parentDir(n.info.toFullPath) / found extccomp.addExternalFileToCompile(found) extccomp.addFileToLink(completeCFilePath(trunc, false)) @@ -590,278 +594,293 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, validPragmas: TSpecialWords): bool = var it = n.sons[i] var key = if it.kind == nkExprColonExpr: it.sons[0] else: it - if key.kind == nkIdent: - var userPragma = strTableGet(c.userPragmas, key.ident) - if userPragma != nil: - inc c.instCounter - if c.instCounter > 100: - globalError(it.info, errRecursiveDependencyX, userPragma.name.s) - pragma(c, sym, userPragma.ast, validPragmas) - # ensure the pragma is also remember for generic instantiations in other - # modules: - n.sons[i] = userPragma.ast - dec c.instCounter - else: - var k = whichKeyword(key.ident) - if k in validPragmas: - case k - of wExportc: - makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info) - incl(sym.flags, sfUsed) # avoid wrong hints - of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1")) - of wImportCompilerProc: - processImportCompilerProc(sym, getOptionalStr(c, it, "$1")) - of wExtern: setExternName(sym, expectStrLit(c, it)) - of wImmediate: - if sym.kind in {skTemplate, skMacro}: incl(sym.flags, sfImmediate) - else: invalidPragma(it) - of wDirty: - if sym.kind == skTemplate: incl(sym.flags, sfDirty) - else: invalidPragma(it) - of wImportCpp: - processImportCpp(sym, getOptionalStr(c, it, "$1")) - of wImportObjC: - processImportObjC(sym, getOptionalStr(c, it, "$1")) - of wAlign: - if sym.typ == nil: invalidPragma(it) - var align = expectIntLit(c, it) - if (not isPowerOfTwo(align) and align != 0) or align >% high(int16): - localError(it.info, errPowerOfTwoExpected) - else: - sym.typ.align = align.int16 - of wSize: - if sym.typ == nil: invalidPragma(it) - var size = expectIntLit(c, it) - if not isPowerOfTwo(size) or size <= 0 or size > 8: - localError(it.info, errPowerOfTwoExpected) - else: - sym.typ.size = size - of wNodecl: - noVal(it) - incl(sym.loc.flags, lfNoDecl) - of wPure, wAsmNoStackFrame: - noVal(it) - if sym != nil: - if k == wPure and sym.kind in routineKinds: invalidPragma(it) - else: incl(sym.flags, sfPure) - of wVolatile: - noVal(it) - incl(sym.flags, sfVolatile) - of wRegister: - noVal(it) - incl(sym.flags, sfRegister) - of wThreadVar: - noVal(it) - incl(sym.flags, sfThread) - of wDeadCodeElim: pragmaDeadCodeElim(c, it) - of wNoForward: pragmaNoForward(c, it) - of wMagic: processMagic(c, it, sym) - of wCompileTime: - noVal(it) - incl(sym.flags, sfCompileTime) - incl(sym.loc.flags, lfNoDecl) - of wGlobal: - noVal(it) - incl(sym.flags, sfGlobal) - incl(sym.flags, sfPure) - of wMerge: - # only supported for backwards compat, doesn't do anything anymore - noVal(it) - of wConstructor: - noVal(it) - incl(sym.flags, sfConstructor) - of wHeader: - var lib = getLib(c, libHeader, getStrLitNode(c, it)) - addToLib(lib, sym) - incl(sym.flags, sfImportc) - incl(sym.loc.flags, lfHeader) - incl(sym.loc.flags, lfNoDecl) - # implies nodecl, because otherwise header would not make sense - if sym.loc.r == nil: sym.loc.r = rope(sym.name.s) - of wDestructor: - sym.flags.incl sfOverriden - if sym.name.s.normalize != "destroy": - localError(n.info, errGenerated, "destructor has to be named 'destroy'") - of wOverride: - sym.flags.incl sfOverriden - of wNosideeffect: - noVal(it) - incl(sym.flags, sfNoSideEffect) - if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect) - of wSideeffect: - noVal(it) - incl(sym.flags, sfSideEffect) - of wNoreturn: - noVal(it) - incl(sym.flags, sfNoReturn) - of wDynlib: - processDynLib(c, it, sym) - of wCompilerproc: - noVal(it) # compilerproc may not get a string! - if sfFromGeneric notin sym.flags: markCompilerProc(sym) - of wProcVar: - noVal(it) - incl(sym.flags, sfProcvar) - of wDeprecated: - if it.kind == nkExprColonExpr: deprecatedStmt(c, it) - elif sym != nil: incl(sym.flags, sfDeprecated) - else: incl(c.module.flags, sfDeprecated) - of wVarargs: - noVal(it) - if sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfVarargs) - of wBorrow: - if sym.kind == skType: - typeBorrow(sym, it) - else: - noVal(it) - incl(sym.flags, sfBorrow) - of wFinal: - noVal(it) - if sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfFinal) - of wInheritable: - noVal(it) - if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(it) - else: incl(sym.typ.flags, tfInheritable) - of wAcyclic: - noVal(it) - if sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfAcyclic) - of wShallow: - noVal(it) - if sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfShallow) - of wThread: - noVal(it) - incl(sym.flags, sfThread) - incl(sym.flags, sfProcvar) - if sym.typ != nil: incl(sym.typ.flags, tfThread) - of wGcSafe: - noVal(it) - if sym.kind != skType: incl(sym.flags, sfThread) - if sym.typ != nil: incl(sym.typ.flags, tfGcSafe) - else: invalidPragma(it) - of wPacked: - noVal(it) - if sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfPacked) - of wHint: message(it.info, hintUser, expectStrLit(c, it)) - of wWarning: message(it.info, warnUser, expectStrLit(c, it)) - of wError: - if sym != nil and sym.isRoutine: - # This is subtle but correct: the error *statement* is only - # allowed for top level statements. Seems to be easier than - # distinguishing properly between - # ``proc p() {.error}`` and ``proc p() = {.error: "msg".}`` - noVal(it) - incl(sym.flags, sfError) - else: - localError(it.info, errUser, expectStrLit(c, it)) - of wFatal: fatal(it.info, errUser, expectStrLit(c, it)) - of wDefine: processDefine(c, it) - of wUndef: processUndef(c, it) - of wCompile: processCompile(c, it) - of wLink: processCommonLink(c, it, linkNormal) - of wLinksys: processCommonLink(c, it, linkSys) - of wPassl: extccomp.addLinkOption(expectStrLit(c, it)) - of wPassc: extccomp.addCompileOption(expectStrLit(c, it)) - of wBreakpoint: pragmaBreakpoint(c, it) - of wWatchPoint: pragmaWatchpoint(c, it) - of wPush: - processPush(c, n, i + 1) - result = true - of wPop: processPop(c, it) - of wPragma: - processPragma(c, n, i) - result = true - of wDiscardable: - noVal(it) - if sym != nil: incl(sym.flags, sfDiscardable) - of wNoInit: - noVal(it) - if sym != nil: incl(sym.flags, sfNoInit) - of wCodegenDecl: processCodegenDecl(c, it, sym) - of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks, - wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, - wLinedir, wStacktrace, wLinetrace, wOptimization, - wCallconv, - wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks, - wPatterns: - if processOption(c, it): - # calling conventions (boring...): - localError(it.info, errOptionExpected) - of FirstCallConv..LastCallConv: - assert(sym != nil) - if sym.typ == nil: invalidPragma(it) - else: sym.typ.callConv = wordToCallConv(k) - of wEmit: pragmaEmit(c, it) - of wUnroll: pragmaUnroll(c, it) - of wLinearScanEnd, wComputedGoto: noVal(it) - of wEffects: - # is later processed in effect analysis: - noVal(it) - of wIncompleteStruct: - noVal(it) - if sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfIncompleteStruct) - of wUnchecked: - noVal(it) - if sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfUncheckedArray) - of wUnion: - noVal(it) - if sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfUnion) - of wRequiresInit: - noVal(it) - if sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfNeedsInit) - of wByRef: - noVal(it) - if sym == nil or sym.typ == nil: - if processOption(c, it): localError(it.info, errOptionExpected) - else: - incl(sym.typ.flags, tfByRef) - of wByCopy: - noVal(it) - if sym.kind != skType or sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfByCopy) - of wInject, wGensym: - # We check for errors, but do nothing with these pragmas otherwise - # as they are handled directly in 'evalTemplate'. - noVal(it) - if sym == nil: invalidPragma(it) - of wLine: pragmaLine(c, it) - of wRaises, wTags: pragmaRaisesOrTags(c, it) - of wLocks: - if sym == nil: pragmaLockStmt(c, it) - elif sym.typ == nil: invalidPragma(it) - else: sym.typ.lockLevel = pragmaLocks(c, it) - of wGuard: - if sym == nil or sym.kind notin {skVar, skLet, skField}: - invalidPragma(it) - else: - sym.guard = pragmaGuard(c, it, sym.kind) - of wGoto: - if sym == nil or sym.kind notin {skVar, skLet}: - invalidPragma(it) - else: - sym.flags.incl sfGoto - of wInjectStmt: - if it.kind != nkExprColonExpr: - localError(it.info, errExprExpected) - else: - it.sons[1] = c.semExpr(c, it.sons[1]) - of wExperimental: + if key.kind == nkBracketExpr: + processNote(c, it) + return + let ident = considerQuotedIdent(key) + var userPragma = strTableGet(c.userPragmas, ident) + if userPragma != nil: + inc c.instCounter + if c.instCounter > 100: + globalError(it.info, errRecursiveDependencyX, userPragma.name.s) + pragma(c, sym, userPragma.ast, validPragmas) + # ensure the pragma is also remember for generic instantiations in other + # modules: + n.sons[i] = userPragma.ast + dec c.instCounter + else: + var k = whichKeyword(ident) + if k in validPragmas: + case k + of wExportc: + makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info) + incl(sym.flags, sfUsed) # avoid wrong hints + of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1")) + of wImportCompilerProc: + processImportCompilerProc(sym, getOptionalStr(c, it, "$1")) + of wExtern: setExternName(sym, expectStrLit(c, it)) + of wImmediate: + if sym.kind in {skTemplate, skMacro}: incl(sym.flags, sfImmediate) + else: invalidPragma(it) + of wDirty: + if sym.kind == skTemplate: incl(sym.flags, sfDirty) + else: invalidPragma(it) + of wImportCpp: + processImportCpp(sym, getOptionalStr(c, it, "$1")) + of wImportObjC: + processImportObjC(sym, getOptionalStr(c, it, "$1")) + of wAlign: + if sym.typ == nil: invalidPragma(it) + var align = expectIntLit(c, it) + if (not isPowerOfTwo(align) and align != 0) or align >% high(int16): + localError(it.info, errPowerOfTwoExpected) + else: + sym.typ.align = align.int16 + of wSize: + if sym.typ == nil: invalidPragma(it) + var size = expectIntLit(c, it) + if not isPowerOfTwo(size) or size <= 0 or size > 8: + localError(it.info, errPowerOfTwoExpected) + else: + sym.typ.size = size + of wNodecl: + noVal(it) + incl(sym.loc.flags, lfNoDecl) + of wPure, wAsmNoStackFrame: + noVal(it) + if sym != nil: + if k == wPure and sym.kind in routineKinds: invalidPragma(it) + else: incl(sym.flags, sfPure) + of wVolatile: + noVal(it) + incl(sym.flags, sfVolatile) + of wRegister: + noVal(it) + incl(sym.flags, sfRegister) + of wThreadVar: + noVal(it) + incl(sym.flags, sfThread) + of wDeadCodeElim: pragmaDeadCodeElim(c, it) + of wNoForward: pragmaNoForward(c, it) + of wMagic: processMagic(c, it, sym) + of wCompileTime: + noVal(it) + incl(sym.flags, sfCompileTime) + incl(sym.loc.flags, lfNoDecl) + of wGlobal: + noVal(it) + incl(sym.flags, sfGlobal) + incl(sym.flags, sfPure) + of wMerge: + # only supported for backwards compat, doesn't do anything anymore + noVal(it) + of wConstructor: + noVal(it) + incl(sym.flags, sfConstructor) + of wHeader: + var lib = getLib(c, libHeader, getStrLitNode(c, it)) + addToLib(lib, sym) + incl(sym.flags, sfImportc) + incl(sym.loc.flags, lfHeader) + incl(sym.loc.flags, lfNoDecl) + # implies nodecl, because otherwise header would not make sense + if sym.loc.r == nil: sym.loc.r = rope(sym.name.s) + of wDestructor: + sym.flags.incl sfOverriden + if sym.name.s.normalize != "destroy": + localError(n.info, errGenerated, "destructor has to be named 'destroy'") + of wOverride: + sym.flags.incl sfOverriden + of wNosideeffect: + noVal(it) + incl(sym.flags, sfNoSideEffect) + if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect) + of wSideeffect: + noVal(it) + incl(sym.flags, sfSideEffect) + of wNoreturn: + noVal(it) + incl(sym.flags, sfNoReturn) + of wDynlib: + processDynLib(c, it, sym) + of wCompilerproc: + noVal(it) # compilerproc may not get a string! + if sfFromGeneric notin sym.flags: markCompilerProc(sym) + of wProcVar: + noVal(it) + incl(sym.flags, sfProcvar) + of wDeprecated: + if it.kind == nkExprColonExpr: deprecatedStmt(c, it) + elif sym != nil: incl(sym.flags, sfDeprecated) + else: incl(c.module.flags, sfDeprecated) + of wVarargs: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfVarargs) + of wBorrow: + if sym.kind == skType: + typeBorrow(sym, it) + else: noVal(it) - if isTopLevel(c): - c.module.flags.incl sfExperimental - else: - localError(it.info, "'experimental' pragma only valid as toplevel statement") + incl(sym.flags, sfBorrow) + of wFinal: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfFinal) + of wInheritable: + noVal(it) + if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(it) + else: incl(sym.typ.flags, tfInheritable) + of wAcyclic: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfAcyclic) + of wShallow: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfShallow) + of wThread: + noVal(it) + incl(sym.flags, sfThread) + incl(sym.flags, sfProcvar) + if sym.typ != nil: incl(sym.typ.flags, tfThread) + of wGcSafe: + noVal(it) + if sym.kind != skType: incl(sym.flags, sfThread) + if sym.typ != nil: incl(sym.typ.flags, tfGcSafe) else: invalidPragma(it) + of wPacked: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfPacked) + of wHint: message(it.info, hintUser, expectStrLit(c, it)) + of wWarning: message(it.info, warnUser, expectStrLit(c, it)) + of wError: + if sym != nil and sym.isRoutine: + # This is subtle but correct: the error *statement* is only + # allowed for top level statements. Seems to be easier than + # distinguishing properly between + # ``proc p() {.error}`` and ``proc p() = {.error: "msg".}`` + noVal(it) + incl(sym.flags, sfError) + else: + localError(it.info, errUser, expectStrLit(c, it)) + of wFatal: fatal(it.info, errUser, expectStrLit(c, it)) + of wDefine: processDefine(c, it) + of wUndef: processUndef(c, it) + of wCompile: processCompile(c, it) + of wLink: processCommonLink(c, it, linkNormal) + of wLinksys: processCommonLink(c, it, linkSys) + of wPassl: extccomp.addLinkOption(expectStrLit(c, it)) + of wPassc: extccomp.addCompileOption(expectStrLit(c, it)) + of wBreakpoint: pragmaBreakpoint(c, it) + of wWatchPoint: pragmaWatchpoint(c, it) + of wPush: + processPush(c, n, i + 1) + result = true + of wPop: processPop(c, it) + of wPragma: + processPragma(c, n, i) + result = true + of wDiscardable: + noVal(it) + if sym != nil: incl(sym.flags, sfDiscardable) + of wNoInit: + noVal(it) + if sym != nil: incl(sym.flags, sfNoInit) + of wCodegenDecl: processCodegenDecl(c, it, sym) + of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks, + wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, + wLinedir, wStacktrace, wLinetrace, wOptimization, + wCallconv, + wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks, + wPatterns: + if processOption(c, it): + # calling conventions (boring...): + localError(it.info, errOptionExpected) + of FirstCallConv..LastCallConv: + assert(sym != nil) + if sym.typ == nil: invalidPragma(it) + else: sym.typ.callConv = wordToCallConv(k) + of wEmit: pragmaEmit(c, it) + of wUnroll: pragmaUnroll(c, it) + of wLinearScanEnd, wComputedGoto: noVal(it) + of wEffects: + # is later processed in effect analysis: + noVal(it) + of wIncompleteStruct: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfIncompleteStruct) + of wUnchecked: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfUncheckedArray) + of wUnion: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfUnion) + of wRequiresInit: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfNeedsInit) + of wByRef: + noVal(it) + if sym == nil or sym.typ == nil: + if processOption(c, it): localError(it.info, errOptionExpected) + else: + incl(sym.typ.flags, tfByRef) + of wByCopy: + noVal(it) + if sym.kind != skType or sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfByCopy) + of wInject, wGensym: + # We check for errors, but do nothing with these pragmas otherwise + # as they are handled directly in 'evalTemplate'. + noVal(it) + if sym == nil: invalidPragma(it) + of wLine: pragmaLine(c, it) + of wRaises, wTags: pragmaRaisesOrTags(c, it) + of wLocks: + if sym == nil: pragmaLockStmt(c, it) + elif sym.typ == nil: invalidPragma(it) + else: sym.typ.lockLevel = pragmaLocks(c, it) + of wBitsize: + if sym == nil or sym.kind != skField or it.kind != nkExprColonExpr: + invalidPragma(it) + else: + sym.bitsize = expectIntLit(c, it) + of wGuard: + if sym == nil or sym.kind notin {skVar, skLet, skField}: + invalidPragma(it) + else: + sym.guard = pragmaGuard(c, it, sym.kind) + of wGoto: + if sym == nil or sym.kind notin {skVar, skLet}: + invalidPragma(it) + else: + sym.flags.incl sfGoto + of wExportNims: + if sym == nil: invalidPragma(it) + else: magicsys.registerNimScriptSymbol(sym) + of wInjectStmt: + if it.kind != nkExprColonExpr: + localError(it.info, errExprExpected) + else: + it.sons[1] = c.semExpr(c, it.sons[1]) + of wExperimental: + noVal(it) + if isTopLevel(c): + c.module.flags.incl sfExperimental + else: + localError(it.info, "'experimental' pragma only valid as toplevel statement") + of wNoRewrite: + noVal(it) + of wBase: + noVal(it) + sym.flags.incl sfBase else: invalidPragma(it) - else: processNote(c, it) + else: invalidPragma(it) proc implicitPragmas*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = diff --git a/compiler/procfind.nim b/compiler/procfind.nim index 473965a3d..523ea2e2f 100644 --- a/compiler/procfind.nim +++ b/compiler/procfind.nim @@ -87,7 +87,7 @@ proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym = discard result = nextIdentIter(it, scope.symbols) - + return nil proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym = @@ -99,17 +99,17 @@ proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym = debug fn.typ debug if result != nil: result.typ else: nil debug if old != nil: old.typ else: nil - + when false: - proc paramsFitBorrow(child, parent: PNode): bool = + proc paramsFitBorrow(child, parent: PNode): bool = var length = sonsLen(child) result = false - if length == sonsLen(parent): - for i in countup(1, length - 1): + if length == sonsLen(parent): + for i in countup(1, length - 1): var m = child.sons[i].sym var n = parent.sons[i].sym assert((m.kind == skParam) and (n.kind == skParam)) - if not compareTypes(m.typ, n.typ, dcEqOrDistinctOf): return + if not compareTypes(m.typ, n.typ, dcEqOrDistinctOf): return if not compareTypes(child.sons[0].typ, parent.sons[0].typ, dcEqOrDistinctOf): return result = true @@ -120,10 +120,10 @@ when false: var it: TIdentIter for scope in walkScopes(startScope): result = initIdentIter(it, scope.symbols, fn.Name) - while result != nil: + while result != nil: # watchout! result must not be the same as fn! - if (result.Kind == fn.kind) and (result.id != fn.id): - if equalGenericParams(result.ast.sons[genericParamsPos], - fn.ast.sons[genericParamsPos]): - if paramsFitBorrow(fn.typ.n, result.typ.n): return + if (result.Kind == fn.kind) and (result.id != fn.id): + if equalGenericParams(result.ast.sons[genericParamsPos], + fn.ast.sons[genericParamsPos]): + if paramsFitBorrow(fn.typ.n, result.typ.n): return result = NextIdentIter(it, scope.symbols) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index ffdb60696..7cd8e25ee 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -31,6 +31,7 @@ type buf*: string pendingNL*: int # negative if not active; else contains the # indentation value + pendingWhitespace: int comStack*: seq[PNode] # comment stack flags*: TRenderFlags checkAnon: bool # we're in a context that can contain sfAnon @@ -83,6 +84,7 @@ proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags) = g.buf = "" g.flags = renderFlags g.pendingNL = -1 + g.pendingWhitespace = -1 g.checkAnon = false proc addTok(g: var TSrcGen, kind: TTokType, s: string) = @@ -97,12 +99,21 @@ proc addPendingNL(g: var TSrcGen) = addTok(g, tkSpaces, "\n" & spaces(g.pendingNL)) g.lineLen = g.pendingNL g.pendingNL = - 1 + g.pendingWhitespace = -1 + elif g.pendingWhitespace >= 0: + addTok(g, tkSpaces, spaces(g.pendingWhitespace)) + g.pendingWhitespace = -1 proc putNL(g: var TSrcGen, indent: int) = if g.pendingNL >= 0: addPendingNL(g) else: addTok(g, tkSpaces, "\n") g.pendingNL = indent g.lineLen = indent + g.pendingWhitespace = -1 + +proc previousNL(g: TSrcGen): bool = + result = g.pendingNL >= 0 or (g.tokens.len > 0 and + g.tokens[^1].kind == tkSpaces) proc putNL(g: var TSrcGen) = putNL(g, g.indent) @@ -127,10 +138,13 @@ proc dedent(g: var TSrcGen) = dec(g.lineLen, IndentWidth) proc put(g: var TSrcGen, kind: TTokType, s: string) = - addPendingNL(g) - if len(s) > 0: - addTok(g, kind, s) - inc(g.lineLen, len(s)) + if kind != tkSpaces: + addPendingNL(g) + if len(s) > 0: + addTok(g, kind, s) + inc(g.lineLen, len(s)) + else: + g.pendingWhitespace = s.len proc putLong(g: var TSrcGen, kind: TTokType, s: string, lineLen: int) = # use this for tokens over multiple lines. @@ -767,7 +781,8 @@ proc gasm(g: var TSrcGen, n: PNode) = putWithSpace(g, tkAsm, "asm") gsub(g, n.sons[0]) gcoms(g) - gsub(g, n.sons[1]) + if n.sons.len > 1: + gsub(g, n.sons[1]) proc gident(g: var TSrcGen, n: PNode) = if g.checkAnon and n.kind == nkSym and sfAnon in n.sym.flags: return @@ -1195,6 +1210,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = if renderNoPragmas notin g.flags: if g.inPragma <= 0: inc g.inPragma + #if not previousNL(g): put(g, tkSpaces, Space) put(g, tkCurlyDotLe, "{.") gcomma(g, n, emptyContext) diff --git a/compiler/rodread.nim b/compiler/rodread.nim index e92f7ecfa..2a85c8975 100644 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -12,15 +12,15 @@ # Reading and writing binary files are really hard to debug. Therefore we use # a "creative" text/binary hybrid format. ROD-files are more efficient # to process because symbols can be loaded on demand. -# +# # A ROD file consists of: # # - a header: # NIM:$fileversion\n # - the module's id (even if the module changed, its ID will not!): # ID:Ax3\n -# - CRC value of this module: -# CRC:CRC-val\n +# - HASH value of this module: +# HASH:HASH-val\n # - a section containing the compiler options and defines this # module has been compiled with: # OPTIONS:options\n @@ -33,7 +33,7 @@ # ) # - an include file dependency section: # INCLUDES( -# <fileidx> <CRC of myfile.inc>\n # fileidx is the LINE in the file section! +# <fileidx> <Hash of myfile.inc>\n # fileidx is the LINE in the file section! # ) # - a module dependency section: # DEPS: <fileidx> <fileidx>\n @@ -44,7 +44,7 @@ # ) # - a compiler proc section: # COMPILERPROCS( -# identifier1 id\n # id is the symbol's id +# identifier1 id\n # id is the symbol's id # ) # - an index consisting of (ID, linenumber)-pairs: # INDEX( @@ -52,7 +52,7 @@ # id-diff idx-diff\n # ) # -# Since the whole index has to be read in advance, we compress it by +# Since the whole index has to be read in advance, we compress it by # storing the integer differences to the last entry instead of using the # real numbers. # @@ -88,30 +88,30 @@ # by using a mem'mapped file. # -import - os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms, - ropes, idents, crc, idgen, types, rodutils, memfiles +import + os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms, + ropes, idents, securehash, idgen, types, rodutils, memfiles, tables -type +type TReasonForRecompile* = enum ## all the reasons that can trigger recompilation rrEmpty, # dependencies not yet computed rrNone, # no need to recompile rrRodDoesNotExist, # rod file does not exist rrRodInvalid, # rod file is invalid - rrCrcChange, # file has been edited since last recompilation + rrHashChange, # file has been edited since last recompilation rrDefines, # defines have changed rrOptions, # options have changed rrInclDeps, # an include has changed rrModDeps # a module this module depends on has been changed -const - reasonToFrmt*: array[TReasonForRecompile, string] = ["", - "no need to recompile: $1", "symbol file for $1 does not exist", - "symbol file for $1 has the wrong version", - "file edited since last compilation: $1", - "list of conditional symbols changed for: $1", - "list of options changed for: $1", - "an include file edited: $1", +const + reasonToFrmt*: array[TReasonForRecompile, string] = ["", + "no need to recompile: $1", "symbol file for $1 does not exist", + "symbol file for $1 has the wrong version", + "file edited since last compilation: $1", + "list of conditional symbols changed for: $1", + "list of options changed for: $1", + "an include file edited: $1", "a module $1 depends on has changed"] type @@ -120,7 +120,7 @@ type tab*: TIITable r*: string # writers use this offset*: int # readers use this - + TRodReader* = object of RootObj pos: int # position; used for parsing s: cstring # mmap'ed file contents @@ -136,13 +136,13 @@ type readerIndex: int line: int # only used for debugging, but is always in the code moduleID: int - syms: TIdTable # already processed symbols + syms: Table[int, PSym] # already processed symbols memfile: MemFile # unfortunately there is no point in time where we # can close this! XXX methods*: TSymSeq origFile: string inViewMode: bool - + PRodReader* = ref TRodReader var rodCompilerprocs*: TStrTable @@ -161,16 +161,16 @@ proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym # `info` is only used for debugging purposes proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType -proc decodeLineInfo(r: PRodReader, info: var TLineInfo) = - if r.s[r.pos] == '?': +proc decodeLineInfo(r: PRodReader, info: var TLineInfo) = + if r.s[r.pos] == '?': inc(r.pos) if r.s[r.pos] == ',': info.col = -1'i16 else: info.col = int16(decodeVInt(r.s, r.pos)) - if r.s[r.pos] == ',': + if r.s[r.pos] == ',': inc(r.pos) if r.s[r.pos] == ',': info.line = -1'i16 else: info.line = int16(decodeVInt(r.s, r.pos)) - if r.s[r.pos] == ',': + if r.s[r.pos] == ',': inc(r.pos) info = newLineInfo(r.files[decodeVInt(r.s, r.pos)], info.line, info.col) @@ -188,56 +188,56 @@ proc skipNode(r: PRodReader) = inc pos r.pos = pos+1 # skip ')' -proc decodeNodeLazyBody(r: PRodReader, fInfo: TLineInfo, - belongsTo: PSym): PNode = +proc decodeNodeLazyBody(r: PRodReader, fInfo: TLineInfo, + belongsTo: PSym): PNode = result = nil - if r.s[r.pos] == '(': + if r.s[r.pos] == '(': inc(r.pos) - if r.s[r.pos] == ')': + if r.s[r.pos] == ')': inc(r.pos) return # nil node result = newNodeI(TNodeKind(decodeVInt(r.s, r.pos)), fInfo) decodeLineInfo(r, result.info) - if r.s[r.pos] == '$': + if r.s[r.pos] == '$': inc(r.pos) result.flags = cast[TNodeFlags](int32(decodeVInt(r.s, r.pos))) - if r.s[r.pos] == '^': + if r.s[r.pos] == '^': inc(r.pos) var id = decodeVInt(r.s, r.pos) result.typ = rrGetType(r, id, result.info) case result.kind - of nkCharLit..nkInt64Lit: - if r.s[r.pos] == '!': + of nkCharLit..nkInt64Lit: + if r.s[r.pos] == '!': inc(r.pos) result.intVal = decodeVBiggestInt(r.s, r.pos) - of nkFloatLit..nkFloat64Lit: - if r.s[r.pos] == '!': + of nkFloatLit..nkFloat64Lit: + if r.s[r.pos] == '!': inc(r.pos) var fl = decodeStr(r.s, r.pos) result.floatVal = parseFloat(fl) - of nkStrLit..nkTripleStrLit: - if r.s[r.pos] == '!': + of nkStrLit..nkTripleStrLit: + if r.s[r.pos] == '!': inc(r.pos) result.strVal = decodeStr(r.s, r.pos) - else: + else: result.strVal = "" # BUGFIX - of nkIdent: - if r.s[r.pos] == '!': + of nkIdent: + if r.s[r.pos] == '!': inc(r.pos) var fl = decodeStr(r.s, r.pos) result.ident = getIdent(fl) - else: + else: internalError(result.info, "decodeNode: nkIdent") - of nkSym: - if r.s[r.pos] == '!': + of nkSym: + if r.s[r.pos] == '!': inc(r.pos) var id = decodeVInt(r.s, r.pos) result.sym = rrGetSym(r, id, result.info) - else: + else: internalError(result.info, "decodeNode: nkSym") else: var i = 0 - while r.s[r.pos] != ')': + while r.s[r.pos] != ')': if belongsTo != nil and i == bodyPos: addSonNilAllowed(result, nil) belongsTo.offset = r.pos @@ -252,93 +252,93 @@ proc decodeNodeLazyBody(r: PRodReader, fInfo: TLineInfo, proc decodeNode(r: PRodReader, fInfo: TLineInfo): PNode = result = decodeNodeLazyBody(r, fInfo, nil) - -proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) = - if r.s[r.pos] == '<': + +proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) = + if r.s[r.pos] == '<': inc(r.pos) - if r.s[r.pos] in {'0'..'9', 'a'..'z', 'A'..'Z'}: + if r.s[r.pos] in {'0'..'9', 'a'..'z', 'A'..'Z'}: loc.k = TLocKind(decodeVInt(r.s, r.pos)) - else: + else: loc.k = low(loc.k) - if r.s[r.pos] == '*': + if r.s[r.pos] == '*': inc(r.pos) loc.s = TStorageLoc(decodeVInt(r.s, r.pos)) - else: + else: loc.s = low(loc.s) - if r.s[r.pos] == '$': + if r.s[r.pos] == '$': inc(r.pos) loc.flags = cast[TLocFlags](int32(decodeVInt(r.s, r.pos))) - else: + else: loc.flags = {} - if r.s[r.pos] == '^': + if r.s[r.pos] == '^': inc(r.pos) loc.t = rrGetType(r, decodeVInt(r.s, r.pos), info) - else: + else: loc.t = nil - if r.s[r.pos] == '!': + if r.s[r.pos] == '!': inc(r.pos) loc.r = rope(decodeStr(r.s, r.pos)) - else: + else: loc.r = nil if r.s[r.pos] == '>': inc(r.pos) else: internalError(info, "decodeLoc " & r.s[r.pos]) - -proc decodeType(r: PRodReader, info: TLineInfo): PType = + +proc decodeType(r: PRodReader, info: TLineInfo): PType = result = nil - if r.s[r.pos] == '[': + if r.s[r.pos] == '[': inc(r.pos) - if r.s[r.pos] == ']': + if r.s[r.pos] == ']': inc(r.pos) return # nil type new(result) result.kind = TTypeKind(decodeVInt(r.s, r.pos)) - if r.s[r.pos] == '+': + if r.s[r.pos] == '+': inc(r.pos) result.id = decodeVInt(r.s, r.pos) setId(result.id) if debugIds: registerID(result) - else: + else: internalError(info, "decodeType: no id") # here this also avoids endless recursion for recursive type - idTablePut(gTypeTable, result, result) + idTablePut(gTypeTable, result, result) if r.s[r.pos] == '(': result.n = decodeNode(r, unknownLineInfo()) - if r.s[r.pos] == '$': + if r.s[r.pos] == '$': inc(r.pos) result.flags = cast[TTypeFlags](int32(decodeVInt(r.s, r.pos))) - if r.s[r.pos] == '?': + if r.s[r.pos] == '?': inc(r.pos) result.callConv = TCallingConvention(decodeVInt(r.s, r.pos)) - if r.s[r.pos] == '*': + if r.s[r.pos] == '*': inc(r.pos) result.owner = rrGetSym(r, decodeVInt(r.s, r.pos), info) - if r.s[r.pos] == '&': + if r.s[r.pos] == '&': inc(r.pos) result.sym = rrGetSym(r, decodeVInt(r.s, r.pos), info) - if r.s[r.pos] == '/': + if r.s[r.pos] == '/': inc(r.pos) result.size = decodeVInt(r.s, r.pos) - else: + else: result.size = - 1 - if r.s[r.pos] == '=': + if r.s[r.pos] == '=': inc(r.pos) result.align = decodeVInt(r.s, r.pos).int16 - else: + else: result.align = 2 decodeLoc(r, result.loc, info) - while r.s[r.pos] == '^': + while r.s[r.pos] == '^': inc(r.pos) - if r.s[r.pos] == '(': + if r.s[r.pos] == '(': inc(r.pos) if r.s[r.pos] == ')': inc(r.pos) else: internalError(info, "decodeType ^(" & r.s[r.pos]) rawAddSon(result, nil) - else: + else: var d = decodeVInt(r.s, r.pos) rawAddSon(result, rrGetType(r, d, info)) -proc decodeLib(r: PRodReader, info: TLineInfo): PLib = +proc decodeLib(r: PRodReader, info: TLineInfo): PLib = result = nil - if r.s[r.pos] == '|': + if r.s[r.pos] == '|': new(result) inc(r.pos) result.kind = TLibKind(decodeVInt(r.s, r.pos)) @@ -349,34 +349,34 @@ proc decodeLib(r: PRodReader, info: TLineInfo): PLib = inc(r.pos) result.path = decodeNode(r, info) -proc decodeSym(r: PRodReader, info: TLineInfo): PSym = - var +proc decodeSym(r: PRodReader, info: TLineInfo): PSym = + var id: int ident: PIdent result = nil - if r.s[r.pos] == '{': + if r.s[r.pos] == '{': inc(r.pos) - if r.s[r.pos] == '}': + if r.s[r.pos] == '}': inc(r.pos) return # nil sym var k = TSymKind(decodeVInt(r.s, r.pos)) - if r.s[r.pos] == '+': + if r.s[r.pos] == '+': inc(r.pos) id = decodeVInt(r.s, r.pos) setId(id) else: internalError(info, "decodeSym: no id") - if r.s[r.pos] == '&': + if r.s[r.pos] == '&': inc(r.pos) ident = getIdent(decodeStr(r.s, r.pos)) else: internalError(info, "decodeSym: no ident") #echo "decoding: {", ident.s - result = PSym(idTableGet(r.syms, id)) - if result == nil: + result = r.syms.getOrDefault(id) + if result == nil: new(result) result.id = id - idTablePut(r.syms, result, result) + r.syms[result.id] = result if debugIds: registerID(result) elif result.id != id: internalError(info, "decodeSym: wrong id") @@ -388,35 +388,35 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym = result.id = id result.kind = k result.name = ident # read the rest of the symbol description: - if r.s[r.pos] == '^': + if r.s[r.pos] == '^': inc(r.pos) result.typ = rrGetType(r, decodeVInt(r.s, r.pos), info) decodeLineInfo(r, result.info) - if r.s[r.pos] == '*': + if r.s[r.pos] == '*': inc(r.pos) result.owner = rrGetSym(r, decodeVInt(r.s, r.pos), result.info) - if r.s[r.pos] == '$': + if r.s[r.pos] == '$': inc(r.pos) result.flags = cast[TSymFlags](int32(decodeVInt(r.s, r.pos))) - if r.s[r.pos] == '@': + if r.s[r.pos] == '@': inc(r.pos) result.magic = TMagic(decodeVInt(r.s, r.pos)) - if r.s[r.pos] == '!': + if r.s[r.pos] == '!': inc(r.pos) result.options = cast[TOptions](int32(decodeVInt(r.s, r.pos))) - else: + else: result.options = r.options - if r.s[r.pos] == '%': + if r.s[r.pos] == '%': inc(r.pos) result.position = decodeVInt(r.s, r.pos) elif result.kind notin routineKinds + {skModule}: result.position = 0 # this may have been misused as reader index! But we still # need it for routines as the body is loaded lazily. - if r.s[r.pos] == '`': + if r.s[r.pos] == '`': inc(r.pos) result.offset = decodeVInt(r.s, r.pos) - else: + else: result.offset = - 1 decodeLoc(r, result.loc, result.info) result.annex = decodeLib(r, info) @@ -433,35 +433,35 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym = result.ast = decodeNode(r, result.info) #echo "decoded: ", ident.s, "}" -proc skipSection(r: PRodReader) = - if r.s[r.pos] == ':': +proc skipSection(r: PRodReader) = + if r.s[r.pos] == ':': while r.s[r.pos] > '\x0A': inc(r.pos) - elif r.s[r.pos] == '(': + elif r.s[r.pos] == '(': var c = 0 # count () pairs inc(r.pos) - while true: + while true: case r.s[r.pos] of '\x0A': inc(r.line) of '(': inc(c) - of ')': - if c == 0: + of ')': + if c == 0: inc(r.pos) - break - elif c > 0: + break + elif c > 0: dec(c) of '\0': break # end of file else: discard inc(r.pos) - else: + else: internalError("skipSection " & $r.line) - -proc rdWord(r: PRodReader): string = + +proc rdWord(r: PRodReader): string = result = "" - while r.s[r.pos] in {'A'..'Z', '_', 'a'..'z', '0'..'9'}: + while r.s[r.pos] in {'A'..'Z', '_', 'a'..'z', '0'..'9'}: add(result, r.s[r.pos]) inc(r.pos) -proc newStub(r: PRodReader, name: string, id: int): PSym = +proc newStub(r: PRodReader, name: string, id: int): PSym = new(result) result.kind = skStub result.id = id @@ -469,11 +469,11 @@ proc newStub(r: PRodReader, name: string, id: int): PSym = result.position = r.readerIndex setId(id) #MessageOut(result.name.s); if debugIds: registerID(result) - -proc processInterf(r: PRodReader, module: PSym) = + +proc processInterf(r: PRodReader, module: PSym) = if r.interfIdx == 0: internalError("processInterf") r.pos = r.interfIdx - while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'): + while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'): var w = decodeStr(r.s, r.pos) inc(r.pos) var key = decodeVInt(r.s, r.pos) @@ -481,30 +481,30 @@ proc processInterf(r: PRodReader, module: PSym) = var s = newStub(r, w, key) s.owner = module strTableAdd(module.tab, s) - idTablePut(r.syms, s, s) + r.syms[s.id] = s -proc processCompilerProcs(r: PRodReader, module: PSym) = +proc processCompilerProcs(r: PRodReader, module: PSym) = if r.compilerProcsIdx == 0: internalError("processCompilerProcs") r.pos = r.compilerProcsIdx - while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'): + while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'): var w = decodeStr(r.s, r.pos) inc(r.pos) var key = decodeVInt(r.s, r.pos) inc(r.pos) # #10 - var s = PSym(idTableGet(r.syms, key)) - if s == nil: + var s = r.syms.getOrDefault(key) + if s == nil: s = newStub(r, w, key) s.owner = module - idTablePut(r.syms, s, s) + r.syms[s.id] = s strTableAdd(rodCompilerprocs, s) -proc processIndex(r: PRodReader; idx: var TIndex; outf: File = nil) = +proc processIndex(r: PRodReader; idx: var TIndex; outf: File = nil) = var key, val, tmp: int inc(r.pos, 2) # skip "(\10" inc(r.line) - while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'): + while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'): tmp = decodeVInt(r.s, r.pos) - if r.s[r.pos] == ' ': + if r.s[r.pos] == ' ': inc(r.pos) key = idx.lastIdxKey + tmp val = decodeVInt(r.s, r.pos) + idx.lastIdxVal @@ -516,11 +516,11 @@ proc processIndex(r: PRodReader; idx: var TIndex; outf: File = nil) = idx.lastIdxKey = key idx.lastIdxVal = val setId(key) # ensure that this id will not be used - if r.s[r.pos] == '\x0A': + if r.s[r.pos] == '\x0A': inc(r.pos) inc(r.line) if r.s[r.pos] == ')': inc(r.pos) - + proc cmdChangeTriggersRecompilation(old, new: TCommands): bool = if old == new: return false # we use a 'case' statement without 'else' so that addition of a @@ -532,32 +532,34 @@ proc cmdChangeTriggersRecompilation(old, new: TCommands): bool = cmdInteractive}: return false of cmdNone, cmdDoc, cmdInterpret, cmdPretty, cmdGenDepend, cmdDump, - cmdCheck, cmdParse, cmdScan, cmdIdeTools, cmdDef, + cmdCheck, cmdParse, cmdScan, cmdIdeTools, cmdDef, cmdRst2html, cmdRst2tex, cmdInteractive, cmdRun: discard # else: trigger recompilation: result = true - -proc processRodFile(r: PRodReader, crc: TCrc32) = - var + +proc processRodFile(r: PRodReader, hash: SecureHash) = + var w: string - d, inclCrc: int - while r.s[r.pos] != '\0': + d: int + var inclHash: SecureHash + while r.s[r.pos] != '\0': var section = rdWord(r) - if r.reason != rrNone: + if r.reason != rrNone: break # no need to process this file further - case section - of "CRC": + case section + of "HASH": inc(r.pos) # skip ':' - if int(crc) != decodeVInt(r.s, r.pos): r.reason = rrCrcChange - of "ID": + if hash != parseSecureHash(decodeStr(r.s, r.pos)): + r.reason = rrHashChange + of "ID": inc(r.pos) # skip ':' r.moduleID = decodeVInt(r.s, r.pos) setId(r.moduleID) of "ORIGFILE": inc(r.pos) r.origFile = decodeStr(r.s, r.pos) - of "OPTIONS": + of "OPTIONS": inc(r.pos) # skip ':' r.options = cast[TOptions](int32(decodeVInt(r.s, r.pos))) if options.gOptions != r.options: r.reason = rrOptions @@ -572,14 +574,14 @@ proc processRodFile(r: PRodReader, crc: TCrc32) = of "DEFINES": inc(r.pos) # skip ':' d = 0 - while r.s[r.pos] > '\x0A': + while r.s[r.pos] > '\x0A': w = decodeStr(r.s, r.pos) inc(d) - if not condsyms.isDefined(getIdent(w)): + if not condsyms.isDefined(getIdent(w)): r.reason = rrDefines #MessageOut('not defined, but should: ' + w); if r.s[r.pos] == ' ': inc(r.pos) if (d != countDefinedSymbols()): r.reason = rrDefines - of "FILES": + of "FILES": inc(r.pos, 2) # skip "(\10" inc(r.line) while r.s[r.pos] != ')': @@ -590,17 +592,17 @@ proc processRodFile(r: PRodReader, crc: TCrc32) = inc(r.pos) # skip #10 inc(r.line) if r.s[r.pos] == ')': inc(r.pos) - of "INCLUDES": + of "INCLUDES": inc(r.pos, 2) # skip "(\10" inc(r.line) - while r.s[r.pos] != ')': + while r.s[r.pos] != ')': w = r.files[decodeVInt(r.s, r.pos)].toFullPath inc(r.pos) # skip ' ' - inclCrc = decodeVInt(r.s, r.pos) - if r.reason == rrNone: - if not existsFile(w) or (inclCrc != int(crcFromFile(w))): + inclHash = parseSecureHash(decodeStr(r.s, r.pos)) + if r.reason == rrNone: + if not existsFile(w) or (inclHash != secureHashFile(w)): r.reason = rrInclDeps - if r.s[r.pos] == '\x0A': + if r.s[r.pos] == '\x0A': inc(r.pos) inc(r.line) if r.s[r.pos] == ')': inc(r.pos) @@ -609,28 +611,28 @@ proc processRodFile(r: PRodReader, crc: TCrc32) = while r.s[r.pos] > '\x0A': r.modDeps.add(r.files[int32(decodeVInt(r.s, r.pos))]) if r.s[r.pos] == ' ': inc(r.pos) - of "INTERF": + of "INTERF": r.interfIdx = r.pos + 2 skipSection(r) - of "COMPILERPROCS": + of "COMPILERPROCS": r.compilerProcsIdx = r.pos + 2 skipSection(r) - of "INDEX": + of "INDEX": processIndex(r, r.index) - of "IMPORTS": + of "IMPORTS": processIndex(r, r.imports) - of "CONVERTERS": + of "CONVERTERS": r.convertersIdx = r.pos + 1 skipSection(r) of "METHODS": r.methodsIdx = r.pos + 1 skipSection(r) - of "DATA": + of "DATA": r.dataIdx = r.pos + 2 # "(\10" # We do not read the DATA section here! We read the needed objects on # demand. And the DATA section comes last in the file, so we stop here: break - of "INIT": + of "INIT": r.initIdx = r.pos + 2 # "(\10" skipSection(r) else: @@ -639,7 +641,7 @@ proc processRodFile(r: PRodReader, crc: TCrc32) = #MsgWriteln("skipping section: " & section & # " at " & $r.line & " in " & r.filename) skipSection(r) - if r.s[r.pos] == '\x0A': + if r.s[r.pos] == '\x0A': inc(r.pos) inc(r.line) @@ -649,8 +651,8 @@ proc startsWith(buf: cstring, token: string, pos = 0): bool = while s < token.len and buf[pos+s] == token[s]: inc s result = s == token.len -proc newRodReader(modfilename: string, crc: TCrc32, - readerIndex: int): PRodReader = +proc newRodReader(modfilename: string, hash: SecureHash, + readerIndex: int): PRodReader = new(result) try: result.memfile = memfiles.open(modfilename) @@ -665,11 +667,11 @@ proc newRodReader(modfilename: string, crc: TCrc32, r.line = 1 r.readerIndex = readerIndex r.filename = modfilename - initIdTable(r.syms) + r.syms = initTable[int, PSym]() # we terminate the file explicitly with ``\0``, so the cast to `cstring` # is safe: r.s = cast[cstring](r.memfile.mem) - if startsWith(r.s, "NIM:"): + if startsWith(r.s, "NIM:"): initIiTable(r.index.tab) initIiTable(r.imports.tab) # looks like a ROD file inc(r.pos, 4) @@ -678,16 +680,16 @@ proc newRodReader(modfilename: string, crc: TCrc32, add(version, r.s[r.pos]) inc(r.pos) if r.s[r.pos] == '\x0A': inc(r.pos) - if version != RodFileVersion: + if version != RodFileVersion: # since ROD files are only for caching, no backwards compatibility is # needed result = nil else: result = nil - -proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType = + +proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType = result = PType(idTableGet(gTypeTable, id)) - if result == nil: + if result == nil: # load the type: var oldPos = r.pos var d = iiTableGet(r.index.tab, id) @@ -696,19 +698,19 @@ proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType = result = decodeType(r, info) r.pos = oldPos -type - TFileModuleRec{.final.} = object +type + TFileModuleRec{.final.} = object filename*: string reason*: TReasonForRecompile rd*: PRodReader - crc*: TCrc32 - crcDone*: bool + hash*: SecureHash + hashDone*: bool TFileModuleMap = seq[TFileModuleRec] var gMods*: TFileModuleMap = @[] -proc decodeSymSafePos(rd: PRodReader, offset: int, info: TLineInfo): PSym = +proc decodeSymSafePos(rd: PRodReader, offset: int, info: TLineInfo): PSym = # all compiled modules if rd.dataIdx == 0: internalError(info, "dataIdx == 0") var oldPos = rd.pos @@ -717,9 +719,9 @@ proc decodeSymSafePos(rd: PRodReader, offset: int, info: TLineInfo): PSym = rd.pos = oldPos proc findSomeWhere(id: int) = - for i in countup(0, high(gMods)): + for i in countup(0, high(gMods)): var rd = gMods[i].rd - if rd != nil: + if rd != nil: var d = iiTableGet(rd.index.tab, id) if d != InvalidKey: echo "found id ", id, " in ", gMods[i].filename @@ -734,12 +736,12 @@ proc getReader(moduleId: int): PRodReader = if result != nil and result.moduleID == moduleId: return result return nil -proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym = - result = PSym(idTableGet(r.syms, id)) - if result == nil: +proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym = + result = r.syms.getOrDefault(id) + if result == nil: # load the symbol: var d = iiTableGet(r.index.tab, id) - if d == InvalidKey: + if d == InvalidKey: # import from other module: var moduleID = iiTableGet(r.imports.tab, id) if moduleID < 0: @@ -748,24 +750,24 @@ proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym = internalError(info, "missing from both indexes: +" & x) var rd = getReader(moduleID) d = iiTableGet(rd.index.tab, id) - if d != InvalidKey: + if d != InvalidKey: result = decodeSymSafePos(rd, d, info) else: var x = "" encodeVInt(id, x) when false: findSomeWhere(id) internalError(info, "rrGetSym: no reader found: +" & x) - else: + else: # own symbol: result = decodeSymSafePos(r, d, info) if result != nil and result.kind == skStub: rawLoadStub(result) - -proc loadInitSection(r: PRodReader): PNode = + +proc loadInitSection(r: PRodReader): PNode = if r.initIdx == 0 or r.dataIdx == 0: internalError("loadInitSection") var oldPos = r.pos r.pos = r.initIdx result = newNode(nkStmtList) - while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')': + while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')': var d = decodeVInt(r.s, r.pos) inc(r.pos) # #10 var p = r.pos @@ -774,13 +776,13 @@ proc loadInitSection(r: PRodReader): PNode = r.pos = p r.pos = oldPos -proc loadConverters(r: PRodReader) = +proc loadConverters(r: PRodReader) = # We have to ensure that no exported converter is a stub anymore, and the # import mechanism takes care of the rest. - if r.convertersIdx == 0 or r.dataIdx == 0: + if r.convertersIdx == 0 or r.dataIdx == 0: internalError("importConverters") r.pos = r.convertersIdx - while r.s[r.pos] > '\x0A': + while r.s[r.pos] > '\x0A': var d = decodeVInt(r.s, r.pos) discard rrGetSym(r, d, unknownLineInfo()) if r.s[r.pos] == ' ': inc(r.pos) @@ -794,14 +796,14 @@ proc loadMethods(r: PRodReader) = r.methods.add(rrGetSym(r, d, unknownLineInfo())) if r.s[r.pos] == ' ': inc(r.pos) -proc getCRC*(fileIdx: int32): TCrc32 = +proc getHash*(fileIdx: int32): SecureHash = internalAssert fileIdx >= 0 and fileIdx < gMods.len - if gMods[fileIdx].crcDone: - return gMods[fileIdx].crc - - result = crcFromFile(fileIdx.toFilename) - gMods[fileIdx].crc = result + if gMods[fileIdx].hashDone: + return gMods[fileIdx].hash + + result = secureHashFile(fileIdx.toFullPath) + gMods[fileIdx].hash = result template growCache*(cache, pos) = if cache.len <= pos: cache.setLen(pos+1) @@ -809,22 +811,22 @@ template growCache*(cache, pos) = proc checkDep(fileIdx: int32): TReasonForRecompile = assert fileIdx != InvalidFileIDX growCache gMods, fileIdx - if gMods[fileIdx].reason != rrEmpty: + if gMods[fileIdx].reason != rrEmpty: # reason has already been computed for this module: return gMods[fileIdx].reason let filename = fileIdx.toFilename - var crc = getCRC(fileIdx) + var hash = getHash(fileIdx) gMods[fileIdx].reason = rrNone # we need to set it here to avoid cycles result = rrNone var r: PRodReader = nil var rodfile = toGeneratedFile(filename.withPackageName, RodExt) - r = newRodReader(rodfile, crc, fileIdx) - if r == nil: + r = newRodReader(rodfile, hash, fileIdx) + if r == nil: result = (if existsFile(rodfile): rrRodInvalid else: rrRodDoesNotExist) else: - processRodFile(r, crc) + processRodFile(r, hash) result = r.reason - if result == rrNone: + if result == rrNone: # check modules it depends on # NOTE: we need to process the entire module graph so that no ID will # be used twice! However, compilation speed does not suffer much from @@ -836,7 +838,7 @@ proc checkDep(fileIdx: int32): TReasonForRecompile = if res != rrNone: result = rrModDeps # we cannot break here, because of side-effects of `checkDep` - if result != rrNone and gVerbosity > 0: + if result != rrNone: rawMessage(hintProcessing, reasonToFrmt[result] % filename) if result != rrNone or optForceFullMake in gGlobalOptions: # recompilation is necessary: @@ -844,10 +846,10 @@ proc checkDep(fileIdx: int32): TReasonForRecompile = r = nil gMods[fileIdx].rd = r gMods[fileIdx].reason = result # now we know better - -proc handleSymbolFile(module: PSym): PRodReader = + +proc handleSymbolFile(module: PSym): PRodReader = let fileIdx = module.fileIdx - if optSymbolFiles notin gGlobalOptions: + if optSymbolFiles notin gGlobalOptions: module.id = getID() return nil idgen.loadMaxIds(options.gProjectPath / options.gProjectName) @@ -855,9 +857,9 @@ proc handleSymbolFile(module: PSym): PRodReader = discard checkDep(fileIdx) if gMods[fileIdx].reason == rrEmpty: internalError("handleSymbolFile") result = gMods[fileIdx].rd - if result != nil: + if result != nil: module.id = result.moduleID - idTablePut(result.syms, module, module) + result.syms[module.id] = module processInterf(result, module) processCompilerProcs(result, module) loadConverters(result) @@ -876,13 +878,13 @@ proc rawLoadStub(s: PSym) = #echo "rs: ", toHex(cast[int](rs.position), int.sizeof * 2), # "\ns: ", toHex(cast[int](s.position), int.sizeof * 2) internalError(rs.info, "loadStub: wrong symbol") - elif rs.id != theId: - internalError(rs.info, "loadStub: wrong ID") + elif rs.id != theId: + internalError(rs.info, "loadStub: wrong ID") #MessageOut('loaded stub: ' + s.name.s); - + proc loadStub*(s: PSym) = ## loads the stub symbol `s`. - + # deactivate the GC here because we do a deep recursion and generate no # garbage when restoring parts of the object graph anyway. # Since we die with internal errors if this fails, no try-finally is @@ -890,12 +892,14 @@ proc loadStub*(s: PSym) = GC_disable() rawLoadStub(s) GC_enable() - + proc getBody*(s: PSym): PNode = ## retrieves the AST's body of `s`. If `s` has been loaded from a rod-file ## it may perform an expensive reload operation. Otherwise it's a simple ## accessor. assert s.kind in routineKinds + # prevent crashes due to incorrect macro transformations (bug #2377) + if s.ast.isNil or bodyPos >= s.ast.len: return ast.emptyNode result = s.ast.sons[bodyPos] if result == nil: assert s.offset != 0 @@ -906,7 +910,7 @@ proc getBody*(s: PSym): PNode = r.pos = oldPos s.ast.sons[bodyPos] = result s.offset = 0 - + initIdTable(gTypeTable) initStrTable(rodCompilerprocs) @@ -919,16 +923,16 @@ proc writeNode(f: File; n: PNode) = f.write('^') f.write(n.typ.id) case n.kind - of nkCharLit..nkInt64Lit: + of nkCharLit..nkInt64Lit: if n.intVal != 0: f.write('!') f.write(n.intVal) - of nkFloatLit..nkFloat64Lit: - if n.floatVal != 0.0: + of nkFloatLit..nkFloat64Lit: + if n.floatVal != 0.0: f.write('!') f.write($n.floatVal) of nkStrLit..nkTripleStrLit: - if n.strVal != "": + if n.strVal != "": f.write('!') f.write(n.strVal.escape) of nkIdent: @@ -938,7 +942,7 @@ proc writeNode(f: File; n: PNode) = f.write('!') f.write(n.sym.id) else: - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): writeNode(f, n.sons[i]) f.write(")") @@ -964,10 +968,10 @@ proc writeSym(f: File; s: PSym) = if s.magic != mNone: f.write('@') f.write($s.magic) - if s.options != gOptions: + if s.options != gOptions: f.write('!') f.write($s.options) - if s.position != 0: + if s.position != 0: f.write('%') f.write($s.position) if s.offset != -1: @@ -988,12 +992,12 @@ proc writeType(f: File; t: PType) = f.write($t.kind) f.write('+') f.write($t.id) - if t.n != nil: + if t.n != nil: f.writeNode(t.n) if t.flags != {}: f.write('$') f.write($t.flags) - if t.callConv != low(t.callConv): + if t.callConv != low(t.callConv): f.write('?') f.write($t.callConv) if t.owner != nil: @@ -1008,16 +1012,16 @@ proc writeType(f: File; t: PType) = if t.align != 2: f.write('=') f.write($t.align) - for i in countup(0, sonsLen(t) - 1): - if t.sons[i] == nil: + for i in countup(0, sonsLen(t) - 1): + if t.sons[i] == nil: f.write("^()") else: - f.write('^') + f.write('^') f.write($t.sons[i].id) f.write("]\n") proc viewFile(rodfile: string) = - var r = newRodReader(rodfile, 0, 0) + var r = newRodReader(rodfile, secureHash(""), 0) if r == nil: rawMessage(errGenerated, "cannot open file (or maybe wrong version):" & rodfile) @@ -1027,30 +1031,30 @@ proc viewFile(rodfile: string) = while r.s[r.pos] != '\0': let section = rdWord(r) case section - of "CRC": + of "HASH": inc(r.pos) # skip ':' - outf.writeln("CRC:", $decodeVInt(r.s, r.pos)) - of "ID": + outf.writeLine("HASH:", $decodeVInt(r.s, r.pos)) + of "ID": inc(r.pos) # skip ':' r.moduleID = decodeVInt(r.s, r.pos) setId(r.moduleID) - outf.writeln("ID:", $r.moduleID) + outf.writeLine("ID:", $r.moduleID) of "ORIGFILE": inc(r.pos) r.origFile = decodeStr(r.s, r.pos) - outf.writeln("ORIGFILE:", r.origFile) + outf.writeLine("ORIGFILE:", r.origFile) of "OPTIONS": inc(r.pos) # skip ':' r.options = cast[TOptions](int32(decodeVInt(r.s, r.pos))) - outf.writeln("OPTIONS:", $r.options) + outf.writeLine("OPTIONS:", $r.options) of "GOPTIONS": inc(r.pos) # skip ':' let dep = cast[TGlobalOptions](int32(decodeVInt(r.s, r.pos))) - outf.writeln("GOPTIONS:", $dep) + outf.writeLine("GOPTIONS:", $dep) of "CMD": inc(r.pos) # skip ':' let dep = cast[TCommands](int32(decodeVInt(r.s, r.pos))) - outf.writeln("CMD:", $dep) + outf.writeLine("CMD:", $dep) of "DEFINES": inc(r.pos) # skip ':' var d = 0 @@ -1072,27 +1076,27 @@ proc viewFile(rodfile: string) = r.files.add(finalPath.fileInfoIdx) inc(r.pos) # skip #10 inc(r.line) - outf.writeln finalPath + outf.writeLine finalPath if r.s[r.pos] == ')': inc(r.pos) outf.write(")\n") - of "INCLUDES": + of "INCLUDES": inc(r.pos, 2) # skip "(\10" inc(r.line) outf.write("INCLUDES(\n") - while r.s[r.pos] != ')': + while r.s[r.pos] != ')': let w = r.files[decodeVInt(r.s, r.pos)] inc(r.pos) # skip ' ' - let inclCrc = decodeVInt(r.s, r.pos) - if r.s[r.pos] == '\x0A': + let inclHash = decodeVInt(r.s, r.pos) + if r.s[r.pos] == '\x0A': inc(r.pos) inc(r.line) - outf.write(w, " ", inclCrc, "\n") + outf.write(w, " ", inclHash, "\n") if r.s[r.pos] == ')': inc(r.pos) outf.write(")\n") of "DEPS": inc(r.pos) # skip ':' outf.write("DEPS:") - while r.s[r.pos] > '\x0A': + while r.s[r.pos] > '\x0A': let v = int32(decodeVInt(r.s, r.pos)) r.modDeps.add(r.files[v]) if r.s[r.pos] == ' ': inc(r.pos) @@ -1124,7 +1128,7 @@ proc viewFile(rodfile: string) = if section == "METHODS": r.methodsIdx = r.pos else: r.convertersIdx = r.pos outf.write(section, ":") - while r.s[r.pos] > '\x0A': + while r.s[r.pos] > '\x0A': let d = decodeVInt(r.s, r.pos) outf.write(" ", $d) if r.s[r.pos] == ' ': inc(r.pos) @@ -1150,7 +1154,7 @@ proc viewFile(rodfile: string) = outf.write("INIT(\n") inc r.pos, 2 r.initIdx = r.pos - while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')': + while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')': let d = decodeVInt(r.s, r.pos) inc(r.pos) # #10 #let p = r.pos diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim index e0ef3c397..91f93d279 100644 --- a/compiler/rodutils.nim +++ b/compiler/rodutils.nim @@ -12,7 +12,7 @@ import strutils proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", header: "<stdio.h>", nodecl, varargs.} -proc toStrMaxPrecision*(f: BiggestFloat): string = +proc toStrMaxPrecision*(f: BiggestFloat): string = if f != f: result = "NAN" elif f == 0.0: @@ -21,17 +21,17 @@ proc toStrMaxPrecision*(f: BiggestFloat): string = if f > 0.0: result = "INF" else: result = "-INF" else: - var buf: array [0..80, char] - c_sprintf(buf, "%#.16e", f) + var buf: array [0..80, char] + c_sprintf(buf, "%#.16e", f) result = $buf proc encodeStr*(s: string, result: var string) = - for i in countup(0, len(s) - 1): + for i in countup(0, len(s) - 1): case s[i] of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i]) else: add(result, '\\' & toHex(ord(s[i]), 2)) -proc hexChar(c: char, xi: var int) = +proc hexChar(c: char, xi: var int) = case c of '0'..'9': xi = (xi shl 4) or (ord(c) - ord('0')) of 'a'..'f': xi = (xi shl 4) or (ord(c) - ord('a') + 10) @@ -41,18 +41,18 @@ proc hexChar(c: char, xi: var int) = proc decodeStr*(s: cstring, pos: var int): string = var i = pos result = "" - while true: + while true: case s[i] - of '\\': + of '\\': inc(i, 3) var xi = 0 hexChar(s[i-2], xi) hexChar(s[i-1], xi) add(result, chr(xi)) - of 'a'..'z', 'A'..'Z', '0'..'9', '_': + of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i]) inc(i) - else: break + else: break pos = i const @@ -68,11 +68,11 @@ template encodeIntImpl(self: expr) = var d: char var v = x var rem = v mod 190 - if rem < 0: + if rem < 0: add(result, '-') v = - (v div 190) rem = - rem - else: + else: v = v div 190 var idx = int(rem) if idx < 62: d = chars[idx] @@ -89,11 +89,11 @@ proc encodeVBiggestInt*(x: BiggestInt, result: var string) = encodeVBiggestIntAux(x +% vintDelta, result) # encodeIntImpl(encodeVBiggestInt) -proc encodeVIntAux(x: int, result: var string) = +proc encodeVIntAux(x: int, result: var string) = ## encode an int as a variable length base 190 int. encodeIntImpl(encodeVIntAux) - -proc encodeVInt*(x: int, result: var string) = + +proc encodeVInt*(x: int, result: var string) = ## encode an int as a variable length base 190 int. encodeVIntAux(x +% vintDelta, result) @@ -101,11 +101,11 @@ template decodeIntImpl() = var i = pos var sign = - 1 assert(s[i] in {'a'..'z', 'A'..'Z', '0'..'9', '-', '\x80'..'\xFF'}) - if s[i] == '-': + if s[i] == '-': inc(i) sign = 1 result = 0 - while true: + while true: case s[i] of '0'..'9': result = result * 190 - (ord(s[i]) - ord('0')) of 'a'..'z': result = result * 190 - (ord(s[i]) - ord('a') + 10) @@ -116,7 +116,7 @@ template decodeIntImpl() = result = result * sign -% vintDelta pos = i -proc decodeVInt*(s: cstring, pos: var int): int = +proc decodeVInt*(s: cstring, pos: var int): int = decodeIntImpl() proc decodeVBiggestInt*(s: cstring, pos: var int): BiggestInt = diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index e178b7ce6..d48a9ba40 100644 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -11,16 +11,17 @@ # rod files is a pass, reading of rod files is not! This is why reading and # writing of rod files is split into two different modules. -import +import intsets, os, options, strutils, nversion, ast, astalgo, msgs, platform, - condsyms, ropes, idents, crc, rodread, passes, importer, idgen, rodutils + condsyms, ropes, idents, securehash, rodread, passes, importer, idgen, + rodutils # implementation -type +type TRodWriter = object of TPassContext module: PSym - crc: TCrc32 + hash: SecureHash options: TOptions defines: string inclDeps: string @@ -38,22 +39,22 @@ type PRodWriter = ref TRodWriter -proc newRodWriter(crc: TCrc32, module: PSym): PRodWriter +proc newRodWriter(hash: SecureHash, module: PSym): PRodWriter proc addModDep(w: PRodWriter, dep: string) proc addInclDep(w: PRodWriter, dep: string) proc addInterfaceSym(w: PRodWriter, s: PSym) proc addStmt(w: PRodWriter, n: PNode) proc writeRod(w: PRodWriter) -proc getDefines(): string = +proc getDefines(): string = result = "" for d in definedSymbolNames(): if result.len != 0: add(result, " ") add(result, d) -proc fileIdx(w: PRodWriter, filename: string): int = - for i in countup(0, high(w.files)): - if w.files[i] == filename: +proc fileIdx(w: PRodWriter, filename: string): int = + for i in countup(0, high(w.files)): + if w.files[i] == filename: return i result = len(w.files) setLen(w.files, result + 1) @@ -62,7 +63,7 @@ proc fileIdx(w: PRodWriter, filename: string): int = template filename*(w: PRodWriter): string = w.module.filename -proc newRodWriter(crc: TCrc32, module: PSym): PRodWriter = +proc newRodWriter(hash: SecureHash, module: PSym): PRodWriter = new(result) result.sstack = @[] result.tstack = @[] @@ -70,7 +71,7 @@ proc newRodWriter(crc: TCrc32, module: PSym): PRodWriter = initIiTable(result.imports.tab) result.index.r = "" result.imports.r = "" - result.crc = crc + result.hash = hash result.module = module result.defines = getDefines() result.options = options.gOptions @@ -84,19 +85,19 @@ proc newRodWriter(crc: TCrc32, module: PSym): PRodWriter = result.init = "" result.origFile = module.info.toFilename result.data = newStringOfCap(12_000) - + proc addModDep(w: PRodWriter, dep: string) = if w.modDeps.len != 0: add(w.modDeps, ' ') encodeVInt(fileIdx(w, dep), w.modDeps) -const +const rodNL = "\x0A" proc addInclDep(w: PRodWriter, dep: string) = var resolved = dep.findModule(w.module.info.toFullPath) encodeVInt(fileIdx(w, dep), w.inclDeps) add(w.inclDeps, " ") - encodeVInt(crcFromFile(resolved), w.inclDeps) + encodeStr($secureHashFile(resolved), w.inclDeps) add(w.inclDeps, rodNL) proc pushType(w: PRodWriter, t: PType) = @@ -109,18 +110,18 @@ proc pushSym(w: PRodWriter, s: PSym) = if iiTableGet(w.index.tab, s.id) == InvalidKey: w.sstack.add(s) -proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, - result: var string) = - if n == nil: +proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, + result: var string) = + if n == nil: # nil nodes have to be stored too: result.add("()") return result.add('(') - encodeVInt(ord(n.kind), result) + encodeVInt(ord(n.kind), result) # we do not write comments for now # Line information takes easily 20% or more of the filesize! Therefore we # omit line information if it is the same as the father's line information: - if fInfo.fileIndex != n.info.fileIndex: + if fInfo.fileIndex != n.info.fileIndex: result.add('?') encodeVInt(n.info.col, result) result.add(',') @@ -138,7 +139,7 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, # No need to output the file index, as this is the serialization of one # file. var f = n.flags * PersistentNodeFlags - if f != {}: + if f != {}: result.add('$') encodeVInt(cast[int32](f), result) if n.typ != nil: @@ -146,16 +147,16 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, encodeVInt(n.typ.id, result) pushType(w, n.typ) case n.kind - of nkCharLit..nkInt64Lit: + of nkCharLit..nkInt64Lit: if n.intVal != 0: result.add('!') encodeVBiggestInt(n.intVal, result) - of nkFloatLit..nkFloat64Lit: - if n.floatVal != 0.0: + of nkFloatLit..nkFloat64Lit: + if n.floatVal != 0.0: result.add('!') encodeStr($n.floatVal, result) of nkStrLit..nkTripleStrLit: - if n.strVal != "": + if n.strVal != "": result.add('!') encodeStr(n.strVal, result) of nkIdent: @@ -166,25 +167,25 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, encodeVInt(n.sym.id, result) pushSym(w, n.sym) else: - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): encodeNode(w, n.info, n.sons[i], result) add(result, ')') -proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) = +proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) = var oldLen = result.len result.add('<') if loc.k != low(loc.k): encodeVInt(ord(loc.k), result) - if loc.s != low(loc.s): + if loc.s != low(loc.s): add(result, '*') encodeVInt(ord(loc.s), result) - if loc.flags != {}: + if loc.flags != {}: add(result, '$') encodeVInt(cast[int32](loc.flags), result) if loc.t != nil: add(result, '^') encodeVInt(cast[int32](loc.t.id), result) pushType(w, loc.t) - if loc.r != nil: + if loc.r != nil: add(result, '!') encodeStr($loc.r, result) if oldLen + 1 == result.len: @@ -192,9 +193,9 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) = setLen(result, oldLen) else: add(result, '>') - -proc encodeType(w: PRodWriter, t: PType, result: var string) = - if t == nil: + +proc encodeType(w: PRodWriter, t: PType, result: var string) = + if t == nil: # nil nodes have to be stored too: result.add("[]") return @@ -206,38 +207,38 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) = encodeVInt(ord(t.kind), result) add(result, '+') encodeVInt(t.id, result) - if t.n != nil: + if t.n != nil: encodeNode(w, unknownLineInfo(), t.n, result) - if t.flags != {}: + if t.flags != {}: add(result, '$') encodeVInt(cast[int32](t.flags), result) - if t.callConv != low(t.callConv): + if t.callConv != low(t.callConv): add(result, '?') encodeVInt(ord(t.callConv), result) - if t.owner != nil: + if t.owner != nil: add(result, '*') encodeVInt(t.owner.id, result) pushSym(w, t.owner) - if t.sym != nil: + if t.sym != nil: add(result, '&') encodeVInt(t.sym.id, result) pushSym(w, t.sym) - if t.size != - 1: + if t.size != - 1: add(result, '/') encodeVBiggestInt(t.size, result) - if t.align != 2: + if t.align != 2: add(result, '=') encodeVInt(t.align, result) encodeLoc(w, t.loc, result) - for i in countup(0, sonsLen(t) - 1): - if t.sons[i] == nil: + for i in countup(0, sonsLen(t) - 1): + if t.sons[i] == nil: add(result, "^()") - else: - add(result, '^') + else: + add(result, '^') encodeVInt(t.sons[i].id, result) pushType(w, t.sons[i]) -proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) = +proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) = add(result, '|') encodeVInt(ord(lib.kind), result) add(result, '|') @@ -276,10 +277,10 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) = if s.magic != mNone: result.add('@') encodeVInt(ord(s.magic), result) - if s.options != w.options: + if s.options != w.options: result.add('!') encodeVInt(cast[int32](s.options), result) - if s.position != 0: + if s.position != 0: result.add('%') encodeVInt(s.position, result) if s.offset != - 1: @@ -307,7 +308,7 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) = if codeAst != nil: # resore the AST: s.ast.sons[codePos] = codeAst - + proc addToIndex(w: var TIndex, key, val: int) = if key - w.lastIdxKey == 1: # we do not store a key-diff of 1 to safe space @@ -328,24 +329,25 @@ when debugWrittenIds: proc symStack(w: PRodWriter): int = var i = 0 - while i < len(w.sstack): + while i < len(w.sstack): var s = w.sstack[i] if sfForward in s.flags: w.sstack[result] = s inc result elif iiTableGet(w.index.tab, s.id) == InvalidKey: var m = getModule(s) - if m == nil: internalError("symStack: module nil: " & s.name.s) - if (m.id == w.module.id) or (sfFromGeneric in s.flags): + if m == nil and s.kind != skPackage: + internalError("symStack: module nil: " & s.name.s) + if s.kind == skPackage or m.id == w.module.id or sfFromGeneric in s.flags: # put definition in here var L = w.data.len - addToIndex(w.index, s.id, L) + addToIndex(w.index, s.id, L) when debugWrittenIds: incl(debugWritten, s.id) encodeSym(w, s, w.data) add(w.data, rodNL) # put into interface section if appropriate: - if {sfExported, sfFromGeneric} * s.flags == {sfExported} and - s.kind in ExportableSymKinds: + if {sfExported, sfFromGeneric} * s.flags == {sfExported} and + s.kind in ExportableSymKinds: encodeStr(s.name.s, w.interf) add(w.interf, ' ') encodeVInt(s.id, w.interf) @@ -361,26 +363,26 @@ proc symStack(w: PRodWriter): int = if s.kind == skMethod and sfDispatcher notin s.flags: if w.methods.len != 0: add(w.methods, ' ') encodeVInt(s.id, w.methods) - elif iiTableGet(w.imports.tab, s.id) == InvalidKey: + elif iiTableGet(w.imports.tab, s.id) == InvalidKey: addToIndex(w.imports, s.id, m.id) when debugWrittenIds: - if not Contains(debugWritten, s.id): + if not contains(debugWritten, s.id): echo(w.filename) debug(s) debug(s.owner) debug(m) - InternalError("Symbol referred to but never written") + internalError("Symbol referred to but never written") inc(i) setLen(w.sstack, result) -proc typeStack(w: PRodWriter): int = +proc typeStack(w: PRodWriter): int = var i = 0 - while i < len(w.tstack): + while i < len(w.tstack): var t = w.tstack[i] if t.kind == tyForward: w.tstack[result] = t inc result - elif iiTableGet(w.index.tab, t.id) == InvalidKey: + elif iiTableGet(w.index.tab, t.id) == InvalidKey: var L = w.data.len addToIndex(w.index, t.id, L) encodeType(w, t, w.data) @@ -400,24 +402,24 @@ proc processStacks(w: PRodWriter, finalPass: bool) = if finalPass and (oldS != 0 or oldT != 0): internalError("could not serialize some forwarded symbols/types") -proc rawAddInterfaceSym(w: PRodWriter, s: PSym) = +proc rawAddInterfaceSym(w: PRodWriter, s: PSym) = pushSym(w, s) processStacks(w, false) -proc addInterfaceSym(w: PRodWriter, s: PSym) = - if w == nil: return - if s.kind in ExportableSymKinds and - {sfExported, sfCompilerProc} * s.flags != {}: +proc addInterfaceSym(w: PRodWriter, s: PSym) = + if w == nil: return + if s.kind in ExportableSymKinds and + {sfExported, sfCompilerProc} * s.flags != {}: rawAddInterfaceSym(w, s) -proc addStmt(w: PRodWriter, n: PNode) = +proc addStmt(w: PRodWriter, n: PNode) = encodeVInt(w.data.len, w.init) add(w.init, rodNL) encodeNode(w, unknownLineInfo(), n, w.data) add(w.data, rodNL) processStacks(w, false) -proc writeRod(w: PRodWriter) = +proc writeRod(w: PRodWriter) = processStacks(w, true) var f: File if not open(f, completeGeneratedFilePath(changeFileExt( @@ -438,12 +440,12 @@ proc writeRod(w: PRodWriter) = encodeStr(w.origFile, orig) f.write(orig) f.write(rodNL) - - var crc = "CRC:" - encodeVInt(w.crc, crc) - f.write(crc) + + var hash = "HASH:" + encodeStr($w.hash, hash) + f.write(hash) f.write(rodNL) - + var options = "OPTIONS:" encodeVInt(cast[int32](w.options), options) f.write(options) @@ -457,31 +459,31 @@ proc writeRod(w: PRodWriter) = var cmd = "CMD:" encodeVInt(cast[int32](gCmd), cmd) f.write(cmd) - f.write(rodNL) - + f.write(rodNL) + f.write("DEFINES:") f.write(w.defines) f.write(rodNL) - + var files = "FILES(" & rodNL - for i in countup(0, high(w.files)): + for i in countup(0, high(w.files)): encodeStr(w.files[i], files) files.add(rodNL) f.write(files) f.write(')' & rodNL) - + f.write("INCLUDES(" & rodNL) f.write(w.inclDeps) f.write(')' & rodNL) - + f.write("DEPS:") f.write(w.modDeps) f.write(rodNL) - + f.write("INTERF(" & rodNL) f.write(w.interf) f.write(')' & rodNL) - + f.write("COMPILERPROCS(" & rodNL) f.write(w.compilerProcs) f.write(')' & rodNL) @@ -489,11 +491,11 @@ proc writeRod(w: PRodWriter) = f.write("INDEX(" & rodNL) f.write(w.index.r) f.write(')' & rodNL) - + f.write("IMPORTS(" & rodNL) f.write(w.imports.r) f.write(')' & rodNL) - + f.write("CONVERTERS:") f.write(w.converters) f.write(rodNL) @@ -501,11 +503,11 @@ proc writeRod(w: PRodWriter) = f.write("METHODS:") f.write(w.methods) f.write(rodNL) - + f.write("INIT(" & rodNL) f.write(w.init) f.write(')' & rodNL) - + f.write("DATA(" & rodNL) f.write(w.data) f.write(')' & rodNL) @@ -513,23 +515,23 @@ proc writeRod(w: PRodWriter) = # for reading: f.write("\0") f.close() - + #echo "interf: ", w.interf.len #echo "index: ", w.index.r.len #echo "init: ", w.init.len #echo "data: ", w.data.len -proc process(c: PPassContext, n: PNode): PNode = +proc process(c: PPassContext, n: PNode): PNode = result = n - if c == nil: return + if c == nil: return var w = PRodWriter(c) case n.kind of nkStmtList: for i in countup(0, sonsLen(n) - 1): discard process(c, n.sons[i]) #var s = n.sons[namePos].sym #addInterfaceSym(w, s) - of nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef, - nkTemplateDef, nkMacroDef: + of nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef, + nkTemplateDef, nkMacroDef: var s = n.sons[namePos].sym if s == nil: internalError(n.info, "rodwrite.process") if n.sons[bodyPos] == nil: @@ -538,17 +540,17 @@ proc process(c: PPassContext, n: PNode): PNode = sfForward notin s.flags: addInterfaceSym(w, s) of nkVarSection, nkLetSection, nkConstSection: - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue addInterfaceSym(w, a.sons[0].sym) - of nkTypeSection: - for i in countup(0, sonsLen(n) - 1): + of nkTypeSection: + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] - if a.kind == nkCommentStmt: continue + if a.kind == nkCommentStmt: continue if a.sons[0].kind != nkSym: internalError(a.info, "rodwrite.process") var s = a.sons[0].sym - addInterfaceSym(w, s) + addInterfaceSym(w, s) # this takes care of enum fields too # Note: The check for ``s.typ.kind = tyEnum`` is wrong for enum # type aliasing! Otherwise the same enum symbol would be included @@ -556,29 +558,29 @@ proc process(c: PPassContext, n: PNode): PNode = # # if (a.sons[2] <> nil) and (a.sons[2].kind = nkEnumTy) then begin # a := s.typ.n; - # for j := 0 to sonsLen(a)-1 do - # addInterfaceSym(w, a.sons[j].sym); - # end - of nkImportStmt: + # for j := 0 to sonsLen(a)-1 do + # addInterfaceSym(w, a.sons[j].sym); + # end + of nkImportStmt: for i in countup(0, sonsLen(n) - 1): addModDep(w, getModuleName(n.sons[i])) addStmt(w, n) - of nkFromStmt: + of nkFromStmt: addModDep(w, getModuleName(n.sons[0])) addStmt(w, n) - of nkIncludeStmt: + of nkIncludeStmt: for i in countup(0, sonsLen(n) - 1): addInclDep(w, getModuleName(n.sons[i])) - of nkPragma: + of nkPragma: addStmt(w, n) - else: + else: discard proc myOpen(module: PSym): PPassContext = if module.id < 0: internalError("rodwrite: module ID not set") - var w = newRodWriter(module.fileIdx.getCRC, module) + var w = newRodWriter(module.fileIdx.getHash, module) rawAddInterfaceSym(w, module) result = w -proc myClose(c: PPassContext, n: PNode): PNode = +proc myClose(c: PPassContext, n: PNode): PNode = result = process(c, n) var w = PRodWriter(c) writeRod(w) diff --git a/compiler/ropes.nim b/compiler/ropes.nim index edac8e9d0..bfae7aaa4 100644 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -306,7 +306,7 @@ const proc equalsFile*(r: Rope, f: File): bool = ## returns true if the contents of the file `f` equal `r`. - var + var buf: array[bufSize, char] bpos = buf.len blen = buf.len diff --git a/compiler/saturate.nim b/compiler/saturate.nim index f4fe29a20..065cb5128 100644 --- a/compiler/saturate.nim +++ b/compiler/saturate.nim @@ -72,7 +72,7 @@ proc `|*|`*(a, b: BiggestInt): BiggestInt = # 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough" if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd): return result - + if floatProd >= 0.0: result = high(result) else: diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim new file mode 100644 index 000000000..22cd282fd --- /dev/null +++ b/compiler/scriptconfig.nim @@ -0,0 +1,146 @@ +# +# +# The Nim Compiler +# (c) Copyright 2015 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Implements the new configuration system for Nim. Uses Nim as a scripting +## language. + +import + ast, modules, passes, passaux, condsyms, + options, nimconf, lists, sem, semdata, llstream, vm, vmdef, commands, msgs, + os, times, osproc + +# we support 'cmpIgnoreStyle' natively for efficiency: +from strutils import cmpIgnoreStyle + +proc listDirs(a: VmArgs, filter: set[PathComponent]) = + let dir = getString(a, 0) + var result: seq[string] = @[] + for kind, path in walkDir(dir): + if kind in filter: result.add path + setResult(a, result) + +proc setupVM*(module: PSym; scriptName: string): PEvalContext = + # For Nimble we need to export 'setupVM'. + result = newCtx(module) + result.mode = emRepl + registerAdditionalOps(result) + + # captured vars: + var errorMsg: string + var vthisDir = scriptName.splitFile.dir + + template cbconf(name, body) {.dirty.} = + result.registerCallback "stdlib.system." & astToStr(name), + proc (a: VmArgs) = + body + + template cbos(name, body) {.dirty.} = + result.registerCallback "stdlib.system." & astToStr(name), + proc (a: VmArgs) = + try: + body + except OSError: + errorMsg = getCurrentExceptionMsg() + + # Idea: Treat link to file as a file, but ignore link to directory to prevent + # endless recursions out of the box. + cbos listFiles: + listDirs(a, {pcFile, pcLinkToFile}) + cbos listDirs: + listDirs(a, {pcDir}) + cbos removeDir: + os.removeDir getString(a, 0) + cbos removeFile: + os.removeFile getString(a, 0) + cbos createDir: + os.createDir getString(a, 0) + cbos getOsError: + setResult(a, errorMsg) + cbos setCurrentDir: + os.setCurrentDir getString(a, 0) + cbos getCurrentDir: + setResult(a, os.getCurrentDir()) + cbos moveFile: + os.moveFile(getString(a, 0), getString(a, 1)) + cbos copyFile: + os.copyFile(getString(a, 0), getString(a, 1)) + cbos getLastModificationTime: + setResult(a, toSeconds(getLastModificationTime(getString(a, 0)))) + + cbos rawExec: + setResult(a, osproc.execCmd getString(a, 0)) + + cbconf getEnv: + setResult(a, os.getEnv(a.getString 0)) + cbconf existsEnv: + setResult(a, os.existsEnv(a.getString 0)) + cbconf dirExists: + setResult(a, os.dirExists(a.getString 0)) + cbconf fileExists: + setResult(a, os.fileExists(a.getString 0)) + + cbconf thisDir: + setResult(a, vthisDir) + cbconf put: + options.setConfigVar(getString(a, 0), getString(a, 1)) + cbconf get: + setResult(a, options.getConfigVar(a.getString 0)) + cbconf exists: + setResult(a, options.existsConfigVar(a.getString 0)) + cbconf nimcacheDir: + setResult(a, options.getNimcacheDir()) + cbconf paramStr: + setResult(a, os.paramStr(int a.getInt 0)) + cbconf paramCount: + setResult(a, os.paramCount()) + cbconf cmpIgnoreStyle: + setResult(a, strutils.cmpIgnoreStyle(a.getString 0, a.getString 1)) + cbconf cmpIgnoreCase: + setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1)) + cbconf setCommand: + options.command = a.getString 0 + let arg = a.getString 1 + if arg.len > 0: + gProjectName = arg + try: + gProjectFull = canonicalizePath(gProjectPath / gProjectName) + except OSError: + gProjectFull = gProjectName + cbconf getCommand: + setResult(a, options.command) + cbconf switch: + processSwitch(a.getString 0, a.getString 1, passPP, unknownLineInfo()) + + +proc runNimScript*(scriptName: string) = + passes.gIncludeFile = includeModule + passes.gImportModule = importModule + initDefines() + + defineSymbol("nimscript") + defineSymbol("nimconfig") + registerPass(semPass) + registerPass(evalPass) + + appendStr(searchPaths, options.libpath) + + var m = makeModule(scriptName) + incl(m.flags, sfMainModule) + vm.globalCtx = setupVM(m, scriptName) + + compileSystemModule() + processModule(m, llStreamOpen(scriptName, fmRead), nil) + + # ensure we load 'system.nim' again for the real non-config stuff! + resetAllModulesHard() + vm.globalCtx = nil + # do not remove the defined symbols + #initDefines() + undefSymbol("nimscript") + undefSymbol("nimconfig") diff --git a/compiler/sem.nim b/compiler/sem.nim index 346a17df1..041524f84 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -171,11 +171,15 @@ proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym = result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info) proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = + proc `$`(kind: TSymKind): string = substr(system.`$`(kind), 2).toLower + # like newSymS, but considers gensym'ed symbols if n.kind == nkSym: # and sfGenSym in n.sym.flags: result = n.sym - internalAssert result.kind == kind + if result.kind != kind: + localError(n.info, "cannot use symbol of kind '" & + $result.kind & "' as a '" & $kind & "'") # when there is a nested proc inside a template, semtmpl # will assign a wrong owner during the first pass over the # template; we must fix it here: see #909 @@ -421,7 +425,11 @@ proc myOpenCached(module: PSym, rd: PRodReader): PPassContext = for m in items(rd.methods): methodDef(m, true) proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = - result = semStmt(c, n) + if sfNoForward in c.module.flags: + result = semAllTypeSections(c, n) + else: + result = n + result = semStmt(c, result) # BUGFIX: process newly generated generics here, not at the end! if c.lastGenericIdx < c.generics.len: var a = newNodeI(nkStmtList, n.info) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index c48e761e3..d8838e347 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -95,7 +95,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = # Gives a detailed error message; this is separated from semOverloadedCall, # as semOverlodedCall is already pretty slow (and we need this information # only in case of an error). - if c.inCompilesContext > 0: + if c.compilesContextId > 0 and optReportConceptFailures notin gGlobalOptions: # fail fast: globalError(n.info, errTypeMismatch, "") if errors.isNil or errors.len == 0: @@ -133,7 +133,10 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = add(candidates, "\n") if candidates != "": add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates) - localError(n.info, errGenerated, result) + if c.compilesContextId > 0 and optReportConceptFailures in gGlobalOptions: + globalError(n.info, errGenerated, result) + else: + localError(n.info, errGenerated, result) proc gatherUsedSyms(c: PContext, usedSyms: var seq[PNode]) = for scope in walkScopes(c.currentScope): @@ -209,7 +212,10 @@ proc resolveOverloads(c: PContext, n, orig: PNode, pickBest(callOp) if overloadsState == csEmpty and result.state == csEmpty: - localError(n.info, errUndeclaredIdentifier, considerQuotedIdent(f).s) + if nfDotField in n.flags and nfExplicitCall notin n.flags: + localError(n.info, errUndeclaredField, considerQuotedIdent(f).s) + else: + localError(n.info, errUndeclaredRoutine, considerQuotedIdent(f).s) return elif result.state != csMatch: if nfExprCall in n.flags: @@ -232,7 +238,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, internalAssert result.state == csMatch #writeMatches(result) #writeMatches(alt) - if c.inCompilesContext > 0: + if c.compilesContextId > 0: # quick error message for performance of 'compiles' built-in: globalError(n.info, errGenerated, "ambiguous call") elif gErrorCounter == 0: @@ -302,8 +308,25 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = if containsGenericType(result.typ) or x.fauxMatch == tyUnknown: result.typ = newTypeS(x.fauxMatch, c) return - if finalCallee.ast.sons[genericParamsPos].kind != nkEmpty: - finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info) + let gp = finalCallee.ast.sons[genericParamsPos] + if gp.kind != nkEmpty: + if x.calleeSym.kind notin {skMacro, skTemplate}: + if x.calleeSym.magic in {mArrGet, mArrPut}: + finalCallee = x.calleeSym + else: + finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info) + else: + # For macros and templates, the resolved generic params + # are added as normal params. + for s in instantiateGenericParamList(c, gp, x.bindings): + case s.kind + of skConst: + x.call.add s.ast + of skType: + x.call.add newSymNode(s, n.info) + else: + internalAssert false + result = x.call instGenericConvertersSons(c, result, x) result.sons[0] = newSymNode(finalCallee, result.sons[0].info) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 345a8c0d1..9b2f2e2ce 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -45,7 +45,8 @@ type TExprFlag* = enum efLValue, efWantIterator, efInTypeof, efWantStmt, efAllowStmt, efDetermineType, - efAllowDestructor, efWantValue, efOperand, efNoSemCheck + efAllowDestructor, efWantValue, efOperand, efNoSemCheck, + efNoProcvarCheck TExprFlags* = set[TExprFlag] TTypeAttachedOp* = enum @@ -70,7 +71,8 @@ type inTypeClass*: int # > 0 if we are in a user-defined type class inGenericContext*: int # > 0 if we are in a generic type inUnrolledContext*: int # > 0 if we are unrolling a loop - inCompilesContext*: int # > 0 if we are in a ``compiles`` magic + compilesContextId*: int # > 0 if we are in a ``compiles`` magic + compilesContextIdGenerator*: int inGenericInst*: int # > 0 if we are instantiating a generic converters*: TSymSeq # sequence of converters patterns*: TSymSeq # sequence of pattern matchers diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim index aaab49a10..1261dd460 100644 --- a/compiler/semdestruct.nim +++ b/compiler/semdestruct.nim @@ -24,7 +24,6 @@ var destructorName = getIdent"destroy_" destructorParam = getIdent"this_" destructorPragma = newIdentNode(getIdent"destructor", unknownLineInfo()) - rangeDestructorProc*: PSym proc instantiateDestructor(c: PContext, typ: PType): PType @@ -141,9 +140,7 @@ proc instantiateDestructor(c: PContext, typ: PType): PType = case t.kind of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs: if instantiateDestructor(c, t.sons[0]) != nil: - if rangeDestructorProc == nil: - rangeDestructorProc = searchInScopes(c, getIdent"nimDestroyRange") - t.destructor = rangeDestructorProc + t.destructor = getCompilerProc"nimDestroyRange" return t else: return nil @@ -177,6 +174,15 @@ proc instantiateDestructor(c: PContext, typ: PType): PType = else: return nil +proc createDestructorCall(c: PContext, s: PSym): PNode = + let varTyp = s.typ + if varTyp == nil or sfGlobal in s.flags: return + let destructableT = instantiateDestructor(c, varTyp) + if destructableT != nil: + let call = semStmt(c, newNode(nkCall, s.info, @[ + useSym(destructableT.destructor), useSym(s)])) + result = newNode(nkDefer, s.info, @[call]) + proc insertDestructors(c: PContext, varSection: PNode): tuple[outer, inner: PNode] = # Accepts a var or let section. diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index cd6ba3753..f1016595a 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -33,6 +33,7 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = if result.typ.kind == tyVar: result = newDeref(result) elif {efWantStmt, efAllowStmt} * flags != {}: result.typ = newTypeS(tyEmpty, c) + result.typ.flags.incl tfVoid else: localError(n.info, errExprXHasNoType, renderTree(result, {renderNoComments})) @@ -51,7 +52,7 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result.typ = errorType(c) else: # XXX tyGenericInst here? - semProcvarCheck(c, result) + if efNoProcvarCheck notin flags: semProcvarCheck(c, result) if result.typ.kind == tyVar: result = newDeref(result) semDestructorCheck(c, result, flags) @@ -123,6 +124,9 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = return newSymNode(u, n.info) result = newSymNode(s, n.info) of skVar, skLet, skResult, skForVar: + if s.magic == mNimvm: + localError(n.info, "illegal context for 'nimvm' magic") + markUsed(n.info, s) styleCheckUse(n.info, s) # if a proc accesses a global variable, it is not side effect free: @@ -288,8 +292,6 @@ proc semConv(c: PContext, n: PNode): PNode = proc semCast(c: PContext, n: PNode): PNode = ## Semantically analyze a casting ("cast[type](param)") - if optSafeCode in gGlobalOptions: localError(n.info, errCastNotInSafeMode) - #incl(c.p.owner.flags, sfSideEffect) checkSonsLen(n, 2) result = newNodeI(nkCast, n.info) result.typ = semTypeNode(c, n.sons[0], nil) @@ -448,18 +450,18 @@ proc changeType(n: PNode, newType: PType, check: bool) = let tup = newType.skipTypes({tyGenericInst}) if tup.kind != tyTuple: if tup.kind == tyObject: return - internalError(n.info, "changeType: no tuple type for constructor") + globalError(n.info, "no tuple type for constructor") elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr: # named tuple? for i in countup(0, sonsLen(n) - 1): var m = n.sons[i].sons[0] if m.kind != nkSym: - internalError(m.info, "changeType(): invalid tuple constr") + globalError(m.info, "invalid tuple constructor") return if tup.n != nil: var f = getSymFromList(tup.n, m.sym.name) if f == nil: - internalError(m.info, "changeType(): invalid identifier") + globalError(m.info, "unknown identifier: " & m.sym.name.s) return changeType(n.sons[i].sons[1], f.typ, check) else: @@ -474,7 +476,7 @@ proc changeType(n: PNode, newType: PType, check: bool) = addSon(a, m) changeType(m, tup.sons[i], check) of nkCharLit..nkUInt64Lit: - if check: + if check and n.kind != nkUInt64Lit: let value = n.intVal if value < firstOrd(newType) or value > lastOrd(newType): localError(n.info, errGenerated, "cannot convert " & $value & @@ -596,8 +598,8 @@ proc skipObjConv(n: PNode): PNode = of nkObjUpConv, nkObjDownConv: result = n.sons[0] else: result = n -proc isAssignable(c: PContext, n: PNode): TAssignableResult = - result = parampatterns.isAssignable(c.p.owner, n) +proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult = + result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr) proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = if n.kind == nkHiddenDeref and not (gCmd == cmdCompileToCpp or @@ -805,6 +807,9 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = return semExpr(c, result, flags) else: n.sons[0] = semExpr(c, n.sons[0]) + let t = n.sons[0].typ + if t != nil and t.kind == tyVar: + n.sons[0] = newDeref(n.sons[0]) let nOrig = n.copyTree semOpAux(c, n) var t: PType = nil @@ -814,7 +819,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = # This is a proc variable, apply normal overload resolution let m = resolveIndirectCall(c, n, nOrig, t) if m.state != csMatch: - if c.inCompilesContext > 0: + if c.compilesContextId > 0: # speed up error generation: globalError(n.info, errTypeMismatch, "") return emptyNode @@ -1152,7 +1157,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = result.add(x[0]) return checkMinSonsLen(n, 2) - n.sons[0] = semExprWithType(c, n.sons[0]) + n.sons[0] = semExprWithType(c, n.sons[0], {efNoProcvarCheck}) let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef}) case arr.kind of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, tySequence, tyString, @@ -1192,7 +1197,17 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = localError(n.info, errIndexTypesDoNotMatch) result = n else: - c.p.bracketExpr = n.sons[0] + let s = if n.sons[0].kind == nkSym: n.sons[0].sym + elif n[0].kind in nkSymChoices: n.sons[0][0].sym + else: nil + if s != nil and s.kind in {skProc, skMethod, skConverter}+skIterators: + # type parameters: partial generic specialization + n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s) + result = explicitGenericInstantiation(c, n, s) + elif s != nil and s.kind == skType: + result = symNodeFromType(c, semTypeNode(c, n, nil), n.info) + else: + c.p.bracketExpr = n.sons[0] proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = let oldBracketExpr = c.p.bracketExpr @@ -1246,7 +1261,7 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = template resultTypeIsInferrable(typ: PType): expr = typ.isMetaType and typ.kind != tyTypeDesc -proc semAsgn(c: PContext, n: PNode): PNode = +proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = checkSonsLen(n, 2) var a = n.sons[0] case a.kind @@ -1269,18 +1284,29 @@ proc semAsgn(c: PContext, n: PNode): PNode = # --> `[]=`(a, i, x) let oldBracketExpr = c.p.bracketExpr a = semSubscript(c, a, {efLValue}) - if a == nil: + if a == nil and mode != noOverloadedSubscript: result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=") add(result, n[1]) result = semExprNoType(c, result) c.p.bracketExpr = oldBracketExpr return result + elif a == nil: + localError(n.info, "could not resolve: " & $n[0]) + return n c.p.bracketExpr = oldBracketExpr of nkCurlyExpr: # a{i} = x --> `{}=`(a, i, x) result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=") add(result, n[1]) return semExprNoType(c, result) + of nkPar: + if a.len >= 2: + # unfortunately we need to rewrite ``(x, y) = foo()`` already here so + # that overloading of the assignment operator still works. Usually we + # prefer to do these rewritings in transf.nim: + return semStmt(c, lowerTupleUnpackingForAsgn(n, c.p.owner)) + else: + a = semExprWithType(c, a, {efLValue}) else: a = semExprWithType(c, a, {efLValue}) n.sons[0] = a @@ -1311,7 +1337,8 @@ proc semAsgn(c: PContext, n: PNode): PNode = typeMismatch(n, lhs.typ, rhs.typ) n.sons[1] = fitNode(c, le, rhs) - if tfHasAsgn in lhs.typ.flags and not lhsIsResult: + if tfHasAsgn in lhs.typ.flags and not lhsIsResult and + mode != noOverloadedAsgn: return overloadedAsgn(c, lhs, n.sons[1]) fixAbstractType(c, n) @@ -1369,7 +1396,7 @@ proc semProcBody(c: PContext, n: PNode): PNode = c.p.resultSym != nil and c.p.resultSym.typ.isMetaType: if isEmptyType(result.typ): # we inferred a 'void' return type: - c.p.resultSym.typ = nil + c.p.resultSym.typ = errorType(c) c.p.owner.typ.sons[0] = nil else: localError(c.p.resultSym.info, errCannotInferReturnType) @@ -1610,7 +1637,9 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # watch out, hacks ahead: let oldErrorCount = msgs.gErrorCounter let oldErrorMax = msgs.gErrorMax - inc c.inCompilesContext + let oldCompilesId = c.compilesContextId + inc c.compilesContextIdGenerator + c.compilesContextId = c.compilesContextIdGenerator # do not halt after first error: msgs.gErrorMax = high(int) @@ -1628,12 +1657,15 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = let oldInGenericInst = c.inGenericInst let oldProcCon = c.p c.generics = @[] + var err: string try: result = semExpr(c, n, flags) if msgs.gErrorCounter != oldErrorCount: result = nil except ERecoverableError: - discard + if optReportConceptFailures in gGlobalOptions: + err = getCurrentExceptionMsg() # undo symbol table changes (as far as it's possible): + c.compilesContextId = oldCompilesId c.generics = oldGenerics c.inGenericContext = oldInGenericContext c.inUnrolledContext = oldInUnrolledContext @@ -1642,10 +1674,11 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = msgs.setInfoContextLen(oldContextLen) setLen(gOwners, oldOwnerLen) c.currentScope = oldScope - dec c.inCompilesContext errorOutputs = oldErrorOutputs msgs.gErrorCounter = oldErrorCount msgs.gErrorMax = oldErrorMax + if optReportConceptFailures in gGlobalOptions and not err.isNil: + localError(n.info, err) proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode = # we replace this node by a 'true' or 'false' node: @@ -1699,10 +1732,13 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = case s.magic # magics that need special treatment of mAddr: checkSonsLen(n, 2) - result = semAddr(c, n.sons[1]) + result = semAddr(c, n.sons[1], s.name.s == "unsafeAddr") of mTypeOf: checkSonsLen(n, 2) result = semTypeOf(c, n.sons[1]) + #of mArrGet: result = semArrGet(c, n, flags) + #of mArrPut: result = semArrPut(c, n, flags) + #of mAsgn: result = semAsgnOpr(c, n) of mDefined: result = semDefined(c, setMs(n, s), false) of mDefinedInScope: result = semDefined(c, setMs(n, s), true) of mCompiles: result = semCompiles(c, setMs(n, s), flags) @@ -1719,6 +1755,8 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = result = newStrNodeT(renderTree(n[1], {renderNoComments}), n) result.typ = getSysType(tyString) of mParallel: + if not experimentalMode(c): + localError(n.info, "use the {.experimental.} pragma to enable 'parallel'") result = setMs(n, s) var x = n.lastSon if x.kind == nkDo: x = x.sons[bodyPos] @@ -1727,13 +1765,17 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = dec c.inParallelStmt of mSpawn: result = setMs(n, s) - result.sons[1] = semExpr(c, n.sons[1]) - if not result[1].typ.isEmptyType: - if spawnResult(result[1].typ, c.inParallelStmt > 0) == srFlowVar: - result.typ = createFlowVar(c, result[1].typ, n.info) + for i in 1 .. <n.len: + result.sons[i] = semExpr(c, n.sons[i]) + let typ = result[^1].typ + if not typ.isEmptyType: + if spawnResult(typ, c.inParallelStmt > 0) == srFlowVar: + result.typ = createFlowVar(c, typ, n.info) else: - result.typ = result[1].typ - result.add instantiateCreateFlowVarCall(c, result[1].typ, n.info).newSymNode + result.typ = typ + result.add instantiateCreateFlowVarCall(c, typ, n.info).newSymNode + else: + result.add emptyNode of mProcCall: result = setMs(n, s) result.sons[1] = semExpr(c, n.sons[1]) @@ -1749,22 +1791,44 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = if semCheck: result = semStmt(c, e) # do not open a new scope! else: result = e + # Check if the node is "when nimvm" + # when nimvm: + # ... + # else: + # ... + var whenNimvm = false + if n.sons.len == 2 and n.sons[0].kind == nkElifBranch and + n.sons[1].kind == nkElse: + let exprNode = n.sons[0].sons[0] + if exprNode.kind == nkIdent: + whenNimvm = lookUp(c, exprNode).magic == mNimvm + elif exprNode.kind == nkSym: + whenNimvm = exprNode.sym.magic == mNimvm + for i in countup(0, sonsLen(n) - 1): var it = n.sons[i] case it.kind of nkElifBranch, nkElifExpr: checkSonsLen(it, 2) - var e = semConstExpr(c, it.sons[0]) - if e.kind != nkIntLit: - # can happen for cascading errors, assume false - # InternalError(n.info, "semWhen") - discard - elif e.intVal != 0 and result == nil: - setResult(it.sons[1]) + if whenNimvm: + if semCheck: + it.sons[1] = semStmt(c, it.sons[1]) + result = n # when nimvm is not elimited until codegen + else: + var e = semConstExpr(c, it.sons[0]) + if e.kind != nkIntLit: + # can happen for cascading errors, assume false + # InternalError(n.info, "semWhen") + discard + elif e.intVal != 0 and result == nil: + setResult(it.sons[1]) of nkElse, nkElseExpr: checkSonsLen(it, 1) - if result == nil: - setResult(it.sons[0]) + if result == nil or whenNimvm: + if semCheck: + it.sons[0] = semStmt(c, it.sons[0]) + if result == nil: + result = it.sons[0] else: illFormedAst(n) if result == nil: result = newNodeI(nkEmpty, n.info) @@ -2026,6 +2090,19 @@ proc semExport(c: PContext, n: PNode): PNode = c.module.ast.add x result = n +proc shouldBeBracketExpr(n: PNode): bool = + assert n.kind in nkCallKinds + let a = n.sons[0] + if a.kind in nkCallKinds: + let b = a[0] + if b.kind in nkSymChoices: + for i in 0..<b.len: + if b[i].sym.magic == mArrGet: + let be = newNodeI(nkBracketExpr, n.info) + for i in 1..<a.len: be.add(a[i]) + n.sons[0] = be + return true + proc setGenericParams(c: PContext, n: PNode) = for i in 1 .. <n.len: n[i].typ = semTypeNode(c, n[i], nil) @@ -2133,7 +2210,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = else: #liMessage(n.info, warnUser, renderTree(n)); result = semIndirectOp(c, n, flags) - elif n[0].kind == nkBracketExpr and isSymChoice(n[0][0]): + elif (n[0].kind == nkBracketExpr or shouldBeBracketExpr(n)) and + isSymChoice(n[0][0]): # indirectOp can deal with explicit instantiations; the fixes # the 'newSeq[T](x)' bug setGenericParams(c, n.sons[0]) @@ -2147,19 +2225,14 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = semWhen(c, n, true) else: result = semWhen(c, n, false) - result = semExpr(c, result, flags) + if result == n: + # This is a "when nimvm" stmt. + result = semWhen(c, n, true) + else: + result = semExpr(c, result, flags) of nkBracketExpr: checkMinSonsLen(n, 1) - var s = qualifiedLookUp(c, n.sons[0], {checkUndeclared}) - if (s != nil and s.kind in {skProc, skMethod, skConverter}+skIterators) or - n[0].kind in nkSymChoices: - # type parameters: partial generic specialization - n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s) - result = explicitGenericInstantiation(c, n, s) - elif s != nil and s.kind in {skType}: - result = symNodeFromType(c, semTypeNode(c, n, nil), n.info) - else: - result = semArrayAccess(c, n, flags) + result = semArrayAccess(c, n, flags) of nkCurlyExpr: result = semExpr(c, buildOverloadedSubscripts(n, getIdent"{}"), flags) of nkPragmaExpr: @@ -2254,7 +2327,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkStaticStmt: result = semStaticStmt(c, n) of nkDefer: - localError(n.info, errGenerated, "'defer' not allowed in this context") + n.sons[0] = semExpr(c, n.sons[0]) + if not n.sons[0].typ.isEmptyType and not implicitlyDiscardable(n.sons[0]): + localError(n.info, errGenerated, "'defer' takes a 'void' expression") + #localError(n.info, errGenerated, "'defer' not allowed in this context") else: localError(n.info, errInvalidExpressionX, renderTree(n, {renderNoComments})) diff --git a/compiler/semfields.nim b/compiler/semfields.nim index e086e73f8..2e6c6c3ea 100644 --- a/compiler/semfields.nim +++ b/compiler/semfields.nim @@ -76,7 +76,7 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) = let L = forLoop.len let call = forLoop.sons[L-2] if call.len > 2: - localError(forLoop.info, errGenerated, + localError(forLoop.info, errGenerated, "parallel 'fields' iterator does not work for 'case' objects") return # iterate over the selector: @@ -106,7 +106,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = # a 'while true: stmt; break' loop ... result = newNodeI(nkWhileStmt, n.info, 2) var trueSymbol = strTableGet(magicsys.systemModule.tab, getIdent"true") - if trueSymbol == nil: + if trueSymbol == nil: localError(n.info, errSystemNeeds, "true") trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(), n.info) trueSymbol.typ = getSysType(tyBool) @@ -114,13 +114,13 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = result.sons[0] = newSymNode(trueSymbol, n.info) var stmts = newNodeI(nkStmtList, n.info) result.sons[1] = stmts - + var length = sonsLen(n) var call = n.sons[length-2] if length-2 != sonsLen(call)-1 + ord(m==mFieldPairs): localError(n.info, errWrongNumberOfVariables) return result - + var tupleTypeA = skipTypes(call.sons[1].typ, abstractVar-{tyTypeDesc}) if tupleTypeA.kind notin {tyTuple, tyObject}: localError(n.info, errGenerated, "no object or tuple type") @@ -129,7 +129,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = var tupleTypeB = skipTypes(call.sons[i].typ, abstractVar-{tyTypeDesc}) if not sameType(tupleTypeA, tupleTypeB): typeMismatch(call.sons[i], tupleTypeA, tupleTypeB) - + inc(c.p.nestedLoopCounter) if tupleTypeA.kind == tyTuple: var loopBody = n.sons[length-1] diff --git a/compiler/semfold.nim b/compiler/semfold.nim index da24005c2..5fe4e3299 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -179,7 +179,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType = else: result = makeRangeF(a, abs(getFloat(a.n.sons[1])), abs(getFloat(a.n.sons[0]))) - of mAbsI, mAbsI64: + of mAbsI: let a = n.sons[1].typ if isIntRange(a): if a.n[0].intVal <= 0: @@ -200,13 +200,13 @@ proc getIntervalType*(m: TMagic, n: PNode): PType = if isIntRange(a) and isIntLit(b): result = makeRange(a, pickMinInt(n.sons[1]) |-| pickMinInt(n.sons[2]), pickMaxInt(n.sons[1]) |-| pickMaxInt(n.sons[2])) - of mAddI, mAddI64, mAddU: + of mAddI, mAddU: commutativeOp(`|+|`) - of mMulI, mMulI64, mMulU: + of mMulI, mMulU: commutativeOp(`|*|`) - of mSubI, mSubI64, mSubU: + of mSubI, mSubU: binaryOp(`|-|`) - of mBitandI, mBitandI64: + of mBitandI: # since uint64 is still not even valid for 'range' (since it's no ordinal # yet), we exclude it from the list (see bug #1638) for now: var a = n.sons[1] @@ -225,7 +225,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType = result = makeRange(a.typ, 0, b.intVal-1) else: result = makeRange(a.typ, b.intVal+1, 0) - of mModI, mModI64: + of mModI: # so ... if you ever wondered about modulo's signedness; this defines it: let a = n.sons[1] let b = n.sons[2] @@ -234,7 +234,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType = result = makeRange(a.typ, -(b.intVal-1), b.intVal-1) else: result = makeRange(a.typ, b.intVal+1, -(b.intVal+1)) - of mDivI, mDivI64, mDivU: + of mDivI, mDivU: binaryOp(`|div|`) of mMinI: commutativeOp(min) @@ -243,8 +243,8 @@ proc getIntervalType*(m: TMagic, n: PNode): PType = else: discard discard """ - mShlI, mShlI64, - mShrI, mShrI64, mAddF64, mSubF64, mMulF64, mDivF64, mMaxF64, mMinF64 + mShlI, + mShrI, mAddF64, mSubF64, mMulF64, mDivF64, mMaxF64, mMinF64 """ proc evalIs(n, a: PNode): PNode = @@ -285,7 +285,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of mUnaryMinusF64: result = newFloatNodeT(- getFloat(a), n) of mNot: result = newIntNodeT(1 - getInt(a), n) of mCard: result = newIntNodeT(nimsets.cardSet(a), n) - of mBitnotI, mBitnotI64: result = newIntNodeT(not getInt(a), n) + of mBitnotI: result = newIntNodeT(not getInt(a), n) of mLengthStr, mXLenStr: if a.kind == nkNilLit: result = newIntNodeT(0, n) else: result = newIntNodeT(len(getStr(a)), n) @@ -298,7 +298,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = result = newFloatNodeT(toFloat(int(getInt(a))), n) of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n) of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n) - of mAbsI, mAbsI64: + of mAbsI: if getInt(a) >= 0: result = a else: result = newIntNodeT(- getInt(a), n) of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64: @@ -307,19 +307,19 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n) of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n) of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n) - of mUnaryLt: result = newIntNodeT(getOrdValue(a) - 1, n) - of mSucc: result = newIntNodeT(getOrdValue(a) + getInt(b), n) - of mPred: result = newIntNodeT(getOrdValue(a) - getInt(b), n) - of mAddI, mAddI64: result = newIntNodeT(getInt(a) + getInt(b), n) - of mSubI, mSubI64: result = newIntNodeT(getInt(a) - getInt(b), n) - of mMulI, mMulI64: result = newIntNodeT(getInt(a) * getInt(b), n) + of mUnaryLt: result = newIntNodeT(getOrdValue(a) |-| 1, n) + of mSucc: result = newIntNodeT(getOrdValue(a) |+| getInt(b), n) + of mPred: result = newIntNodeT(getOrdValue(a) |-| getInt(b), n) + of mAddI: result = newIntNodeT(getInt(a) |+| getInt(b), n) + of mSubI: result = newIntNodeT(getInt(a) |-| getInt(b), n) + of mMulI: result = newIntNodeT(getInt(a) |*| getInt(b), n) of mMinI: if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n) else: result = newIntNodeT(getInt(a), n) of mMaxI: if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n) else: result = newIntNodeT(getInt(b), n) - of mShlI, mShlI64: + of mShlI: case skipTypes(n.typ, abstractRange).kind of tyInt8: result = newIntNodeT(int8(getInt(a)) shl int8(getInt(b)), n) of tyInt16: result = newIntNodeT(int16(getInt(a)) shl int16(getInt(b)), n) @@ -327,7 +327,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of tyInt64, tyInt, tyUInt..tyUInt64: result = newIntNodeT(`shl`(getInt(a), getInt(b)), n) else: internalError(n.info, "constant folding for shl") - of mShrI, mShrI64: + of mShrI: case skipTypes(n.typ, abstractRange).kind of tyInt8: result = newIntNodeT(int8(getInt(a)) shr int8(getInt(b)), n) of tyInt16: result = newIntNodeT(int16(getInt(a)) shr int16(getInt(b)), n) @@ -335,14 +335,14 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of tyInt64, tyInt, tyUInt..tyUInt64: result = newIntNodeT(`shr`(getInt(a), getInt(b)), n) else: internalError(n.info, "constant folding for shr") - of mDivI, mDivI64: + of mDivI: let y = getInt(b) if y != 0: - result = newIntNodeT(getInt(a) div y, n) - of mModI, mModI64: + result = newIntNodeT(`|div|`(getInt(a), y), n) + of mModI: let y = getInt(b) if y != 0: - result = newIntNodeT(getInt(a) mod y, n) + result = newIntNodeT(`|mod|`(getInt(a), y), n) of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n) of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n) of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n) @@ -359,11 +359,11 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(b), n) else: result = newFloatNodeT(getFloat(a), n) of mIsNil: result = newIntNodeT(ord(a.kind == nkNilLit), n) - of mLtI, mLtI64, mLtB, mLtEnum, mLtCh: + of mLtI, mLtB, mLtEnum, mLtCh: result = newIntNodeT(ord(getOrdValue(a) < getOrdValue(b)), n) - of mLeI, mLeI64, mLeB, mLeEnum, mLeCh: + of mLeI, mLeB, mLeEnum, mLeCh: result = newIntNodeT(ord(getOrdValue(a) <= getOrdValue(b)), n) - of mEqI, mEqI64, mEqB, mEqEnum, mEqCh: + of mEqI, mEqB, mEqEnum, mEqCh: result = newIntNodeT(ord(getOrdValue(a) == getOrdValue(b)), n) of mLtF64: result = newIntNodeT(ord(getFloat(a) < getFloat(b)), n) of mLeF64: result = newIntNodeT(ord(getFloat(a) <= getFloat(b)), n) @@ -375,9 +375,9 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = result = newIntNodeT(ord(`<%`(getOrdValue(a), getOrdValue(b))), n) of mLeU, mLeU64: result = newIntNodeT(ord(`<=%`(getOrdValue(a), getOrdValue(b))), n) - of mBitandI, mBitandI64, mAnd: result = newIntNodeT(a.getInt and b.getInt, n) - of mBitorI, mBitorI64, mOr: result = newIntNodeT(getInt(a) or getInt(b), n) - of mBitxorI, mBitxorI64, mXor: result = newIntNodeT(a.getInt xor b.getInt, n) + of mBitandI, mAnd: result = newIntNodeT(a.getInt and b.getInt, n) + of mBitorI, mOr: result = newIntNodeT(getInt(a) or getInt(b), n) + of mBitxorI, mXor: result = newIntNodeT(a.getInt xor b.getInt, n) of mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n) of mSubU: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n) of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n) @@ -430,14 +430,10 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of mCompileOptionArg: result = newIntNodeT(ord( testCompileOptionArg(getStr(a), getStr(b), n.info)), n) - of mNewString, mNewStringOfCap, - mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh, - mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, - mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, mDotDot, - mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn, - mParallel, mPlugin: - discard - else: internalError(a.info, "evalOp(" & $m & ')') + of mEqProc: + result = newIntNodeT(ord( + exprStructuralEquivalent(a, b, strictSymEquality=true)), n) + else: discard proc getConstIfExpr(c: PSym, n: PNode): PNode = result = nil @@ -540,7 +536,7 @@ proc foldConv*(n, a: PNode; check = false): PNode = of tyFloat..tyFloat64: case skipTypes(a.typ, abstractRange).kind of tyInt..tyInt64, tyEnum, tyBool, tyChar: - result = newFloatNodeT(toFloat(int(getOrdValue(a))), n) + result = newFloatNodeT(toBiggestFloat(getOrdValue(a)), n) else: result = a result.typ = n.typ diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index db910600b..620453277 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -30,6 +30,7 @@ type GenericCtx = object toMixin: IntSet cursorInBody: bool # only for nimsuggest + bracketExpr: PNode type TSemGenericFlag = enum @@ -39,9 +40,9 @@ type proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx): PNode -proc semGenericStmtScope(c: PContext, n: PNode, +proc semGenericStmtScope(c: PContext, n: PNode, flags: TSemGenericFlags, - ctx: var GenericCtx): PNode = + ctx: var GenericCtx): PNode = openScope(c) result = semGenericStmt(c, n, flags, ctx) closeScope(c) @@ -57,7 +58,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, of skUnknown: # Introduced in this pass! Leave it as an identifier. result = n - of skProc, skMethod, skIterators, skConverter: + of skProc, skMethod, skIterators, skConverter, skModule: result = symChoice(c, n, s, scOpen) of skTemplate: if macroToExpand(s): @@ -73,7 +74,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, result = semGenericStmt(c, result, {}, ctx) else: result = symChoice(c, n, s, scOpen) - of skGenericParam: + of skGenericParam: if s.typ != nil and s.typ.kind == tyStatic: if s.typ.n != nil: result = s.typ.n @@ -85,18 +86,18 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, of skParam: result = n styleCheckUse(n.info, s) - of skType: + of skType: if (s.typ != nil) and (s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}): result = newSymNodeTypeDesc(s, n.info) - else: + else: result = n styleCheckUse(n.info, s) else: result = newSymNode(s, n.info) styleCheckUse(n.info, s) -proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, +proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx): PNode = result = n let ident = considerQuotedIdent(n) @@ -118,13 +119,13 @@ proc newDot(n, b: PNode): PNode = result.add(n.sons[0]) result.add(b) -proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, +proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx; isMacro: var bool): PNode = assert n.kind == nkDotExpr semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody) let luf = if withinMixin notin flags: {checkUndeclared} else: {} - + var s = qualifiedLookUp(c, n, luf) if s != nil: result = semGenericStmtSymbol(c, n, s, ctx) @@ -141,18 +142,20 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, elif s.name.id in ctx.toMixin: result = newDot(result, symChoice(c, n, s, scForceOpen)) else: - let sym = semGenericStmtSymbol(c, n, s, ctx) - if sym.kind == nkSym: - result = newDot(result, symChoice(c, n, s, scForceOpen)) + let syms = semGenericStmtSymbol(c, n, s, ctx) + if syms.kind == nkSym: + let choice = symChoice(c, n, s, scForceOpen) + choice.kind = nkClosedSymChoice + result = newDot(result, choice) else: - result = newDot(result, sym) + result = newDot(result, syms) proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) = let s = newSymS(skUnknown, getIdentNode(n), c) addPrelimDecl(c, s) styleCheckDef(n.info, s, kind) -proc semGenericStmt(c: PContext, n: PNode, +proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx): PNode = result = n #if gCmd == cmdIdeTools: suggestStmt(c, n) @@ -181,16 +184,16 @@ proc semGenericStmt(c: PContext, n: PNode, result = semGenericStmt(c, n.sons[0], flags+{withinBind}, ctx) of nkMixinStmt: result = semMixinStmt(c, n, ctx.toMixin) - of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit: + of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit: # check if it is an expression macro: checkMinSonsLen(n, 1) let fn = n.sons[0] var s = qualifiedLookUp(c, fn, {}) if s == nil and withinMixin notin flags and - fn.kind in {nkIdent, nkAccQuoted} and + fn.kind in {nkIdent, nkAccQuoted} and considerQuotedIdent(fn).id notin ctx.toMixin: localError(n.info, errUndeclaredIdentifier, fn.renderTree) - + var first = 0 var mixinContext = false if s != nil: @@ -220,19 +223,23 @@ proc semGenericStmt(c: PContext, n: PNode, # we need to put the ``c`` in ``t(c)`` in a mixin context to prevent # the famous "undeclared identifier: it" bug: mixinContext = true - of skUnknown, skParam: + of skUnknown, skParam: # Leave it as an identifier. discard - of skProc, skMethod, skIterators, skConverter: + of skProc, skMethod, skIterators, skConverter, skModule: result.sons[0] = symChoice(c, fn, s, scOption) + # do not check of 's.magic==mRoof' here because it might be some + # other '^' but after overload resolution the proper one: + if ctx.bracketExpr != nil and n.len == 2 and s.name.s == "^": + result.add ctx.bracketExpr first = 1 of skGenericParam: result.sons[0] = newSymNodeTypeDesc(s, fn.info) styleCheckUse(fn.info, s) first = 1 - of skType: + of skType: # bad hack for generics: - if (s.typ != nil) and (s.typ.kind != tyGenericParam): + if (s.typ != nil) and (s.typ.kind != tyGenericParam): result.sons[0] = newSymNodeTypeDesc(s, fn.info) styleCheckUse(fn.info, s) first = 1 @@ -244,34 +251,68 @@ proc semGenericStmt(c: PContext, n: PNode, result.sons[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext) first = 1 # Consider 'when declared(globalsSlot): ThreadVarSetValue(globalsSlot, ...)' - # in threads.nim: the subtle preprocessing here binds 'globalsSlot' which + # in threads.nim: the subtle preprocessing here binds 'globalsSlot' which # is not exported and yet the generic 'threadProcWrapper' works correctly. let flags = if mixinContext: flags+{withinMixin} else: flags for i in countup(first, sonsLen(result) - 1): result.sons[i] = semGenericStmt(c, result.sons[i], flags, ctx) - of nkIfStmt: - for i in countup(0, sonsLen(n)-1): + of nkCurlyExpr: + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent("{}"), n.info) + for i in 0 ..< n.len: result.add(n[i]) + result = semGenericStmt(c, result, flags, ctx) + of nkBracketExpr: + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent("[]"), n.info) + for i in 0 ..< n.len: result.add(n[i]) + withBracketExpr ctx, n.sons[0]: + result = semGenericStmt(c, result, flags, ctx) + of nkAsgn, nkFastAsgn: + checkSonsLen(n, 2) + let a = n.sons[0] + let b = n.sons[1] + + let k = a.kind + case k + of nkCurlyExpr: + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent("{}="), n.info) + for i in 0 ..< a.len: result.add(a[i]) + result.add(b) + result = semGenericStmt(c, result, flags, ctx) + of nkBracketExpr: + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent("[]="), n.info) + for i in 0 ..< a.len: result.add(a[i]) + result.add(b) + withBracketExpr ctx, a.sons[0]: + result = semGenericStmt(c, result, flags, ctx) + else: + for i in countup(0, sonsLen(n) - 1): + result.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx) + of nkIfStmt: + for i in countup(0, sonsLen(n)-1): n.sons[i] = semGenericStmtScope(c, n.sons[i], flags, ctx) of nkWhenStmt: for i in countup(0, sonsLen(n)-1): n.sons[i] = semGenericStmt(c, n.sons[i], flags+{withinMixin}, ctx) - of nkWhileStmt: + of nkWhileStmt: openScope(c) - for i in countup(0, sonsLen(n)-1): + for i in countup(0, sonsLen(n)-1): n.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx) closeScope(c) - of nkCaseStmt: + of nkCaseStmt: openScope(c) n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx) - for i in countup(1, sonsLen(n)-1): + for i in countup(1, sonsLen(n)-1): var a = n.sons[i] checkMinSonsLen(a, 1) var L = sonsLen(a) - for j in countup(0, L-2): + for j in countup(0, L-2): a.sons[j] = semGenericStmt(c, a.sons[j], flags, ctx) a.sons[L - 1] = semGenericStmtScope(c, a.sons[L-1], flags, ctx) closeScope(c) - of nkForStmt, nkParForStmt: + of nkForStmt, nkParForStmt: var L = sonsLen(n) openScope(c) n.sons[L - 2] = semGenericStmt(c, n.sons[L-2], flags, ctx) @@ -279,27 +320,27 @@ proc semGenericStmt(c: PContext, n: PNode, addTempDecl(c, n.sons[i], skForVar) n.sons[L - 1] = semGenericStmt(c, n.sons[L-1], flags, ctx) closeScope(c) - of nkBlockStmt, nkBlockExpr, nkBlockType: + of nkBlockStmt, nkBlockExpr, nkBlockType: checkSonsLen(n, 2) openScope(c) - if n.sons[0].kind != nkEmpty: + if n.sons[0].kind != nkEmpty: addTempDecl(c, n.sons[0], skLabel) n.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx) closeScope(c) - of nkTryStmt: + of nkTryStmt: checkMinSonsLen(n, 2) n.sons[0] = semGenericStmtScope(c, n.sons[0], flags, ctx) - for i in countup(1, sonsLen(n)-1): + for i in countup(1, sonsLen(n)-1): var a = n.sons[i] checkMinSonsLen(a, 1) var L = sonsLen(a) - for j in countup(0, L-2): + for j in countup(0, L-2): a.sons[j] = semGenericStmt(c, a.sons[j], flags+{withinTypeDesc}, ctx) a.sons[L-1] = semGenericStmtScope(c, a.sons[L-1], flags, ctx) - of nkVarSection, nkLetSection: - for i in countup(0, sonsLen(n) - 1): + of nkVarSection, nkLetSection: + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] - if a.kind == nkCommentStmt: continue + if a.kind == nkCommentStmt: continue if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a) checkMinSonsLen(a, 3) var L = sonsLen(a) @@ -307,79 +348,79 @@ proc semGenericStmt(c: PContext, n: PNode, a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx) for j in countup(0, L-3): addTempDecl(c, getIdentNode(a.sons[j]), skVar) - of nkGenericParams: - for i in countup(0, sonsLen(n) - 1): + of nkGenericParams: + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if (a.kind != nkIdentDefs): illFormedAst(a) checkMinSonsLen(a, 3) var L = sonsLen(a) - a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) - # do not perform symbol lookup for default expressions - for j in countup(0, L-3): + a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) + # do not perform symbol lookup for default expressions + for j in countup(0, L-3): addTempDecl(c, getIdentNode(a.sons[j]), skType) - of nkConstSection: - for i in countup(0, sonsLen(n) - 1): + of nkConstSection: + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] - if a.kind == nkCommentStmt: continue + if a.kind == nkCommentStmt: continue if (a.kind != nkConstDef): illFormedAst(a) checkSonsLen(a, 3) addTempDecl(c, getIdentNode(a.sons[0]), skConst) a.sons[1] = semGenericStmt(c, a.sons[1], flags+{withinTypeDesc}, ctx) a.sons[2] = semGenericStmt(c, a.sons[2], flags, ctx) of nkTypeSection: - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] - if a.kind == nkCommentStmt: continue + if a.kind == nkCommentStmt: continue if (a.kind != nkTypeDef): illFormedAst(a) checkSonsLen(a, 3) addTempDecl(c, getIdentNode(a.sons[0]), skType) - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] - if a.kind == nkCommentStmt: continue + if a.kind == nkCommentStmt: continue if (a.kind != nkTypeDef): illFormedAst(a) checkSonsLen(a, 3) - if a.sons[1].kind != nkEmpty: + if a.sons[1].kind != nkEmpty: openScope(c) a.sons[1] = semGenericStmt(c, a.sons[1], flags, ctx) a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, ctx) closeScope(c) - else: + else: a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, ctx) - of nkEnumTy: + of nkEnumTy: if n.sonsLen > 0: - if n.sons[0].kind != nkEmpty: + if n.sons[0].kind != nkEmpty: n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, ctx) - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): var a: PNode case n.sons[i].kind of nkEnumFieldDef: a = n.sons[i].sons[0] of nkIdent: a = n.sons[i] else: illFormedAst(n) - addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c)) + addDecl(c, newSymS(skUnknown, getIdentNode(a), c)) of nkObjectTy, nkTupleTy, nkTupleClassTy: discard of nkFormalParams: checkMinSonsLen(n, 1) - if n.sons[0].kind != nkEmpty: + if n.sons[0].kind != nkEmpty: n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, ctx) - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): var a = n.sons[i] if (a.kind != nkIdentDefs): illFormedAst(a) checkMinSonsLen(a, 3) var L = sonsLen(a) a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx) - for j in countup(0, L-3): + for j in countup(0, L-3): addTempDecl(c, getIdentNode(a.sons[j]), skParam) - of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, - nkIteratorDef, nkLambdaKinds: + of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, + nkIteratorDef, nkLambdaKinds: checkSonsLen(n, bodyPos + 1) if n.sons[namePos].kind != nkEmpty: addTempDecl(c, getIdentNode(n.sons[0]), skProc) openScope(c) - n.sons[genericParamsPos] = semGenericStmt(c, n.sons[genericParamsPos], + n.sons[genericParamsPos] = semGenericStmt(c, n.sons[genericParamsPos], flags, ctx) - if n.sons[paramsPos].kind != nkEmpty: + if n.sons[paramsPos].kind != nkEmpty: if n.sons[paramsPos].sons[0].kind != nkEmpty: addPrelimDecl(c, newSym(skUnknown, getIdent("result"), nil, n.info)) n.sons[paramsPos] = semGenericStmt(c, n.sons[paramsPos], flags, ctx) @@ -394,7 +435,7 @@ proc semGenericStmt(c: PContext, n: PNode, checkMinSonsLen(n, 2) result.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx) else: - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): result.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx) proc semGenericStmt(c: PContext, n: PNode): PNode = diff --git a/compiler/seminst.nim b/compiler/seminst.nim index b2aef63a8..2c767ffc6 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -10,14 +10,10 @@ # This module implements the instantiation of generic procs. # included from sem.nim -proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, - entry: var TInstantiation) = - if n.kind != nkGenericParams: - internalError(n.info, "instantiateGenericParamList; no generic params") - newSeq(entry.concreteTypes, n.len) +iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym = + internalAssert n.kind == nkGenericParams for i, a in n.pairs: - if a.kind != nkSym: - internalError(a.info, "instantiateGenericParamList; no symbol") + internalAssert a.kind == nkSym var q = a.sym if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses: continue @@ -42,8 +38,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, #t = ReplaceTypeVarsT(cl, t) s.typ = t if t.kind == tyStatic: s.ast = t.n - addDecl(c, s) - entry.concreteTypes[i] = t + yield s proc sameInstantiation(a, b: TInstantiation): bool = if a.concreteTypes.len == b.concreteTypes.len: @@ -52,10 +47,11 @@ proc sameInstantiation(a, b: TInstantiation): bool = flags = {ExactTypeDescValues}): return result = true -proc genericCacheGet(genericSym: PSym, entry: TInstantiation): PSym = +proc genericCacheGet(genericSym: PSym, entry: TInstantiation; + id: CompilesId): PSym = if genericSym.procInstCache != nil: for inst in genericSym.procInstCache: - if sameInstantiation(entry, inst[]): + if inst.compilesId == id and sameInstantiation(entry, inst[]): return inst.sym proc removeDefaultParamValues(n: PNode) = @@ -169,7 +165,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable, addDecl(c, prc) pushInfoContext(info) - var cl = initTypeVars(c, pt, info) + var cl = initTypeVars(c, pt, info, nil) var result = instCopyType(cl, prc.typ) let originalParams = result.n result.n = originalParams.shallowCopy @@ -217,7 +213,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, ## The `pt` parameter is a type-unsafe mapping table used to link generic ## parameters to their concrete types within the generic instance. # no need to instantiate generic templates/macros: - if fn.kind in {skTemplate, skMacro}: return fn + internalAssert fn.kind notin {skMacro, skTemplate} # generates an instantiated proc if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep") inc(c.instCounter) @@ -226,30 +222,49 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, # NOTE: for access of private fields within generics from a different module # we set the friend module: c.friendModules.add(getModule(fn)) - #let oldScope = c.currentScope - #c.currentScope = fn.scope + let oldInTypeClass = c.inTypeClass + c.inTypeClass = 0 + let oldScope = c.currentScope + while not isTopLevel(c): c.currentScope = c.currentScope.parent result = copySym(fn, false) incl(result.flags, sfFromGeneric) result.owner = fn result.ast = n pushOwner(result) + openScope(c) - internalAssert n.sons[genericParamsPos].kind != nkEmpty + let gp = n.sons[genericParamsPos] + internalAssert gp.kind != nkEmpty n.sons[namePos] = newSymNode(result) pushInfoContext(info) var entry = TInstantiation.new entry.sym = result - instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[]) + # we need to compare both the generic types and the concrete types: + # generic[void](), generic[int]() + # see ttypeor.nim test. + var i = 0 + newSeq(entry.concreteTypes, fn.typ.len+gp.len-1) + for s in instantiateGenericParamList(c, gp, pt): + addDecl(c, s) + entry.concreteTypes[i] = s.typ + inc i pushProcCon(c, result) instantiateProcType(c, pt, result, info) + for j in 1 .. result.typ.len-1: + entry.concreteTypes[i] = result.typ.sons[j] + inc i + if tfTriggersCompileTime in result.typ.flags: + incl(result.flags, sfCompileTime) n.sons[genericParamsPos] = ast.emptyNode - var oldPrc = genericCacheGet(fn, entry[]) + var oldPrc = genericCacheGet(fn, entry[], c.compilesContextId) if oldPrc == nil: # we MUST not add potentially wrong instantiations to the caching mechanism. # This means recursive instantiations behave differently when in # a ``compiles`` context but this is the lesser evil. See # bug #1055 (tevilcompiles). - if c.inCompilesContext == 0: fn.procInstCache.safeAdd(entry) + #if c.compilesContextId == 0: + entry.compilesId = c.compilesContextId + fn.procInstCache.safeAdd(entry) c.generics.add(makeInstPair(fn, entry)) if n.sons[pragmasPos].kind != nkEmpty: pragma(c, result, n.sons[pragmasPos], allRoutinePragmas) @@ -264,7 +279,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, popInfoContext() closeScope(c) # close scope for parameters popOwner() - #c.currentScope = oldScope + c.currentScope = oldScope discard c.friendModules.pop() dec(c.instCounter) + c.inTypeClass = oldInTypeClass if result.kind == skMethod: finishMethod(c, result) diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim index bb9814a16..150680af7 100644 --- a/compiler/semmacrosanity.nim +++ b/compiler/semmacrosanity.nim @@ -38,14 +38,16 @@ proc annotateType*(n: PNode, t: PType) = # Note: x can be unequal to t and we need to be careful to use 't' # to not to skip tyGenericInst case n.kind + of nkObjConstr: + n.typ = t + for i in 1 .. <n.len: + let field = x.n.ithField(i - 1) + if field.isNil: globalError n.info, "invalid field at index " & $i + else: + internalAssert(n.sons[i].kind == nkExprColonExpr) + annotateType(n.sons[i].sons[1], field.typ) of nkPar: - if x.kind == tyObject: - n.typ = t - for i in 0 .. <n.len: - let field = x.n.ithField(i) - if field.isNil: globalError n.info, "invalid field at index " & $i - else: annotateType(n.sons[i], field.typ) - elif x.kind == tyTuple: + if x.kind == tyTuple: n.typ = t for i in 0 .. <n.len: if i >= x.len: globalError n.info, "invalid field at index " & $i @@ -53,7 +55,7 @@ proc annotateType*(n: PNode, t: PType) = elif x.kind == tyProc and x.callConv == ccClosure: n.typ = t else: - globalError(n.info, "() must have an object or tuple type") + globalError(n.info, "() must have a tuple type") of nkBracket: if x.kind in {tyArrayConstr, tyArray, tySequence, tyOpenArray}: n.typ = t diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 0a7846f1d..deef38ae3 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -10,10 +10,12 @@ # This include file implements the semantic checking for magics. # included from sem.nim -proc semAddr(c: PContext; n: PNode): PNode = +proc semAddr(c: PContext; n: PNode; isUnsafeAddr=false): PNode = result = newNodeI(nkAddr, n.info) let x = semExprWithType(c, n) - if isAssignable(c, x) notin {arLValue, arLocalLValue}: + if x.kind == nkSym: + x.sym.flags.incl(sfAddrTaken) + if isAssignable(c, x, isUnsafeAddr) notin {arLValue, arLocalLValue}: localError(n.info, errExprHasNoAddress) result.add x result.typ = makePtrType(c, x.typ) @@ -24,6 +26,41 @@ proc semTypeOf(c: PContext; n: PNode): PNode = result.add typExpr result.typ = makeTypeDesc(c, typExpr.typ.skipTypes({tyTypeDesc, tyIter})) +type + SemAsgnMode = enum asgnNormal, noOverloadedSubscript, noOverloadedAsgn + +proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode +proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode + +proc skipAddr(n: PNode): PNode {.inline.} = + (if n.kind == nkHiddenAddr: n.sons[0] else: n) + +proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode = + result = newNodeI(nkBracketExpr, n.info) + for i in 1..<n.len: result.add(n[i]) + let oldBracketExpr = c.p.bracketExpr + result = semSubscript(c, result, flags) + c.p.bracketExpr = oldBracketExpr + if result.isNil: + localError(n.info, "could not resolve: " & $n) + result = n + +proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode = + # rewrite `[]=`(a, i, x) back to ``a[i] = x``. + let b = newNodeI(nkBracketExpr, n.info) + b.add(n[1].skipAddr) + for i in 2..n.len-2: b.add(n[i]) + result = newNodeI(nkAsgn, n.info, 2) + result.sons[0] = b + result.sons[1] = n.lastSon + result = semAsgn(c, result, noOverloadedSubscript) + +proc semAsgnOpr(c: PContext; n: PNode): PNode = + result = newNodeI(nkAsgn, n.info, 2) + result.sons[0] = n[1] + result.sons[1] = n[2] + result = semAsgn(c, result, noOverloadedAsgn) + proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode = var r = isPartOf(n[1], n[2]) result = newIntNodeT(ord(r), n) @@ -119,10 +156,13 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, case n[0].sym.magic of mAddr: checkSonsLen(n, 2) - result = semAddr(c, n.sons[1]) + result = semAddr(c, n.sons[1], n[0].sym.name.s == "unsafeAddr") of mTypeOf: checkSonsLen(n, 2) result = semTypeOf(c, n.sons[1]) + of mArrGet: result = semArrGet(c, n, flags) + of mArrPut: result = semArrPut(c, n, flags) + of mAsgn: result = semAsgnOpr(c, n) of mIsPartOf: result = semIsPartOf(c, n, flags) of mTypeTrait: result = semTypeTraits(c, n) of mAstToStr: @@ -143,25 +183,28 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, if isNegative(n.sons[1]) or (n.len > 2 and isNegative(n.sons[2])): localError(n.info, "use '^' instead of '-'; negative indexing is obsolete") of mRoof: - # error correction: - result = n.sons[1] - if c.p.bracketExpr.isNil: + let bracketExpr = if n.len == 3: n.sons[2] else: c.p.bracketExpr + if bracketExpr.isNil: localError(n.info, "no surrounding array access context for '^'") - elif c.p.bracketExpr.checkForSideEffects != seNoSideEffect: + result = n.sons[1] + elif bracketExpr.checkForSideEffects != seNoSideEffect: localError(n.info, "invalid context for '^' as '$#' has side effects" % - renderTree(c.p.bracketExpr)) - elif c.p.bracketExpr.typ.isStrangeArray: + renderTree(bracketExpr)) + result = n.sons[1] + elif bracketExpr.typ.isStrangeArray: localError(n.info, "invalid context for '^' as len!=high+1 for '$#'" % - renderTree(c.p.bracketExpr)) + renderTree(bracketExpr)) + result = n.sons[1] else: # ^x is rewritten to: len(a)-x let lenExpr = newNodeI(nkCall, n.info) lenExpr.add newIdentNode(getIdent"len", n.info) - lenExpr.add c.p.bracketExpr + lenExpr.add bracketExpr let lenExprB = semExprWithType(c, lenExpr) if lenExprB.typ.isNil or not isOrdinalType(lenExprB.typ): localError(n.info, "'$#' has to be of an ordinal type for '^'" % renderTree(lenExpr)) + result = n.sons[1] else: result = newNodeIT(nkCall, n.info, getSysType(tyInt)) result.add newSymNode(createMagic("-", mSubI), n.info) diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim index fbcd6b6da..b04ba4657 100644 --- a/compiler/semparallel.nim +++ b/compiler/semparallel.nim @@ -128,10 +128,10 @@ template `?`(x): expr = x.renderTree proc checkLe(c: AnalysisCtx; a, b: PNode) = case proveLe(c.guards, a, b) of impUnknown: - localError(a.info, "cannot prove: " & ?a & " <= " & ?b) + localError(a.info, "cannot prove: " & ?a & " <= " & ?b & " (bounds check)") of impYes: discard of impNo: - localError(a.info, "can prove: " & ?a & " > " & ?b) + localError(a.info, "can prove: " & ?a & " > " & ?b & " (bounds check)") proc checkBounds(c: AnalysisCtx; arr, idx: PNode) = checkLe(c, arr.lowBound, idx) @@ -156,19 +156,23 @@ proc addSlice(c: var AnalysisCtx; n: PNode; x, le, ri: PNode) = proc overlap(m: TModel; x,y,c,d: PNode) = # X..Y and C..D overlap iff (X <= D and C <= Y) - case proveLe(m, x, d) + case proveLe(m, c, y) of impUnknown: - localError(x.info, - "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" % - [?x, ?d, ?x, ?y, ?c, ?d]) + case proveLe(m, x, d) + of impNo: discard + of impUnknown, impYes: + localError(x.info, + "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" % + [?c, ?y, ?x, ?y, ?c, ?d]) of impYes: - case proveLe(m, c, y) + case proveLe(m, x, d) of impUnknown: localError(x.info, "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" % - [?c, ?y, ?x, ?y, ?c, ?d]) + [?x, ?d, ?x, ?y, ?c, ?d]) of impYes: - localError(x.info, "($#)..($#) not disjoint from ($#)..($#)" % [?x, ?y, ?c, ?d]) + localError(x.info, "($#)..($#) not disjoint from ($#)..($#)" % + [?c, ?y, ?x, ?y, ?c, ?d]) of impNo: discard of impNo: discard @@ -278,10 +282,12 @@ proc analyseCall(c: var AnalysisCtx; n: PNode; op: PSym) = slot.stride = min(slot.stride, incr) analyseSons(c, n) elif op.name.s == "[]" and op.fromSystem: - c.addSlice(n, n[1], n[2][1], n[2][2]) + let slice = n[2].skipStmtList + c.addSlice(n, n[1], slice[1], slice[2]) analyseSons(c, n) elif op.name.s == "[]=" and op.fromSystem: - c.addSlice(n, n[1], n[2][1], n[2][2]) + let slice = n[2].skipStmtList + c.addSlice(n, n[1], slice[1], slice[2]) analyseSons(c, n) else: analyseSons(c, n) @@ -363,7 +369,7 @@ proc analyse(c: var AnalysisCtx; n: PNode) = else: internalError(it.info, "slot already has a lower bound") if not isSpawned: analyse(c, value) of nkCaseStmt: analyseCase(c, n) - of nkIfStmt, nkIfExpr: analyseIf(c, n) + of nkWhen, nkIfStmt, nkIfExpr: analyseIf(c, n) of nkWhileStmt: analyse(c, n.sons[0]) # 'while true' loop? @@ -395,8 +401,9 @@ proc transformSlices(n: PNode): PNode = result = copyNode(n) result.add opSlice.newSymNode result.add n[1] - result.add n[2][1] - result.add n[2][2] + let slice = n[2].skipStmtList + result.add slice[1] + result.add slice[2] return result if n.safeLen > 0: result = shallowCopy(n) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index adf03be64..ef014963c 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -9,7 +9,7 @@ import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, - wordrecg, strutils, options, guards + wordrecg, strutils, options, guards, writetracking # Second semantic checking pass over the AST. Necessary because the old # way had some inherent problems. Performs: @@ -17,7 +17,7 @@ import # * effect+exception tracking # * "usage before definition" checking # * checks for invalid usages of compiletime magics (not implemented) -# * checks for invalid usages of PNimNode (not implemented) +# * checks for invalid usages of NimNode (not implemented) # * later: will do an escape analysis for closures at least # Predefined effects: @@ -29,21 +29,6 @@ import # --> a TR macro can annotate the proc with user defined annotations # --> the effect system can access these -# Load&Store analysis is performed on *paths*. A path is an access like -# obj.x.y[i].z; splitting paths up causes some problems: -# -# var x = obj.x -# var z = x.y[i].z -# -# Alias analysis is affected by this too! A good solution is *type splitting*: -# T becomes T1 and T2 if it's known that T1 and T2 can't alias. -# -# An aliasing problem and a race condition are effectively the same problem. -# Type based alias analysis is nice but not sufficient; especially splitting -# an array and filling it in parallel should be supported but is not easily -# done: It essentially requires a built-in 'indexSplit' operation and dependent -# typing. - # ------------------------ exception and tag tracking ------------------------- discard """ @@ -207,9 +192,9 @@ proc markGcUnsafe(a: PEffects; reason: PNode) = a.owner.gcUnsafetyReason = newSym(skUnknown, getIdent("<unknown>"), a.owner, reason.info) -proc listGcUnsafety(s: PSym; onlyWarning: bool) = +proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet) = let u = s.gcUnsafetyReason - if u != nil: + if u != nil and not cycleCheck.containsOrIncl(u.id): let msgKind = if onlyWarning: warnGcUnsafe2 else: errGenerated if u.kind in {skLet, skVar}: message(s.info, msgKind, @@ -218,7 +203,7 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool) = elif u.kind in routineKinds: # recursive call *always* produces only a warning so the full error # message is printed: - listGcUnsafety(u, true) + listGcUnsafety(u, true, cycleCheck) message(s.info, msgKind, "'$#' is not GC-safe as it calls '$#'" % [s.name.s, u.name.s]) @@ -227,6 +212,10 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool) = message(u.info, msgKind, "'$#' is not GC-safe as it performs an indirect call here" % s.name.s) +proc listGcUnsafety(s: PSym; onlyWarning: bool) = + var cycleCheck = initIntSet() + listGcUnsafety(s, onlyWarning, cycleCheck) + proc useVar(a: PEffects, n: PNode) = let s = n.sym if isLocalVar(a, s): @@ -237,9 +226,10 @@ proc useVar(a: PEffects, n: PNode) = message(n.info, warnUninit, s.name.s) # prevent superfluous warnings about the same variable: a.init.add s.id - if {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind in {skVar, skLet}: + if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet}: if s.guard != nil: guardGlobal(a, n, s.guard) - if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem): + if {sfGlobal, sfThread} * s.flags == {sfGlobal} and + (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem): #if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) markGcUnsafe(a, s) @@ -433,17 +423,41 @@ proc documentEffect(n, x: PNode, effectType: TSpecialWord, idx: int): PNode = result = newNode(nkExprColonExpr, n.info, @[ newIdentNode(getIdent(specialWords[effectType]), n.info), effects]) +proc documentWriteEffect(n: PNode; flag: TSymFlag; pragmaName: string): PNode = + let s = n.sons[namePos].sym + let params = s.typ.n + + var effects = newNodeI(nkBracket, n.info) + for i in 1 ..< params.len: + if params[i].kind == nkSym and flag in params[i].sym.flags: + effects.add params[i] + + if effects.len > 0: + result = newNode(nkExprColonExpr, n.info, @[ + newIdentNode(getIdent(pragmaName), n.info), effects]) + +proc documentNewEffect(n: PNode): PNode = + let s = n.sons[namePos].sym + if tfReturnsNew in s.typ.flags: + result = newIdentNode(getIdent("new"), n.info) + proc documentRaises*(n: PNode) = if n.sons[namePos].kind != nkSym: return let pragmas = n.sons[pragmasPos] let p1 = documentEffect(n, pragmas, wRaises, exceptionEffects) let p2 = documentEffect(n, pragmas, wTags, tagEffects) + let p3 = documentWriteEffect(n, sfWrittenTo, "writes") + let p4 = documentNewEffect(n) + let p5 = documentWriteEffect(n, sfEscapes, "escapes") - if p1 != nil or p2 != nil: + if p1 != nil or p2 != nil or p3 != nil or p4 != nil or p5 != nil: if pragmas.kind == nkEmpty: n.sons[pragmasPos] = newNodeI(nkPragma, n.info) if p1 != nil: n.sons[pragmasPos].add p1 if p2 != nil: n.sons[pragmasPos].add p2 + if p3 != nil: n.sons[pragmasPos].add p3 + if p4 != nil: n.sons[pragmasPos].add p4 + if p5 != nil: n.sons[pragmasPos].add p5 template notGcSafe(t): expr = {tfGcSafe, tfNoSideEffect} * t.flags == {} @@ -721,7 +735,7 @@ proc track(tracked: PEffects, n: PNode) = # since 'var (a, b): T = ()' is not even allowed, there is always type # inference for (a, b) and thus no nil checking is necessary. of nkCaseStmt: trackCase(tracked, n) - of nkIfStmt, nkIfExpr: trackIf(tracked, n) + of nkWhen, nkIfStmt, nkIfExpr: trackIf(tracked, n) of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n.sons[1]) of nkWhileStmt: track(tracked, n.sons[0]) @@ -748,7 +762,7 @@ proc track(tracked: PEffects, n: PNode) = for i in 1 .. <len(n): let x = n.sons[i] track(tracked, x) - if sfDiscriminant in x.sons[0].sym.flags: + if x.sons[0].kind == nkSym and sfDiscriminant in x.sons[0].sym.flags: addDiscriminantFact(tracked.guards, x) setLen(tracked.guards, oldFacts) of nkPragmaBlock: @@ -841,6 +855,8 @@ proc initEffects(effects: PNode; s: PSym; t: var TEffects) = newSeq(effects.sons, effectListLen) effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info) effects.sons[tagEffects] = newNodeI(nkArgList, s.info) + effects.sons[usesEffects] = ast.emptyNode + effects.sons[writeEffects] = ast.emptyNode t.exc = effects.sons[exceptionEffects] t.tags = effects.sons[tagEffects] @@ -895,6 +911,7 @@ proc trackProc*(s: PSym, body: PNode) = message(s.info, warnLockLevel, "declared lock level is $1, but real lock level is $2" % [$s.typ.lockLevel, $t.maxLockLevel]) + when useWriteTracking: trackWrites(s, body) proc trackTopLevelStmt*(module: PSym; n: PNode) = if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index c355a5bf1..adb1c81c1 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -369,6 +369,15 @@ proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) = else: result.add identDefs +proc addDefer(c: PContext; result: var PNode; s: PSym) = + let deferDestructorCall = createDestructorCall(c, s) + if deferDestructorCall != nil: + if result.kind != nkStmtList: + let oldResult = result + result = newNodeI(nkStmtList, result.info) + result.add oldResult + result.add deferDestructorCall + proc isDiscardUnderscore(v: PSym): bool = if v.name.s == "_": v.flags.incl(sfGenSym) @@ -469,6 +478,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if def.kind == nkPar: v.ast = def[j] v.typ = tup.sons[j] b.sons[j] = newSymNode(v) + addDefer(c, result, v) checkNilable(v) if sfCompileTime in v.flags: hasCompileTime = true if hasCompileTime: vm.setupCompileTimeVar(c.module, result) @@ -744,13 +754,62 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil: checkForMetaFields(s.typ.n) +proc semAllTypeSections(c: PContext; n: PNode): PNode = + proc gatherStmts(c: PContext; n: PNode; result: PNode) {.nimcall.} = + case n.kind + of nkIncludeStmt: + for i in 0..<n.len: + var f = checkModuleName(n.sons[i]) + if f != InvalidFileIDX: + if containsOrIncl(c.includedFiles, f): + localError(n.info, errRecursiveDependencyX, f.toFilename) + else: + let code = gIncludeFile(c.module, f) + gatherStmts c, code, result + excl(c.includedFiles, f) + of nkStmtList: + for i in 0 ..< n.len: + gatherStmts(c, n.sons[i], result) + of nkTypeSection: + incl n.flags, nfSem + typeSectionLeftSidePass(c, n) + result.add n + else: + result.add n + + result = newNodeI(nkStmtList, n.info) + gatherStmts(c, n, result) + + template rec(name) = + for i in 0 ..< result.len: + if result[i].kind == nkTypeSection: + name(c, result[i]) + + rec typeSectionRightSidePass + rec typeSectionFinalPass + when false: + # too beautiful to delete: + template rec(name; setbit=false) = + proc `name rec`(c: PContext; n: PNode) {.nimcall.} = + if n.kind == nkTypeSection: + when setbit: incl n.flags, nfSem + name(c, n) + elif n.kind == nkStmtList: + for i in 0 ..< n.len: + `name rec`(c, n.sons[i]) + `name rec`(c, n) + rec typeSectionLeftSidePass, true + rec typeSectionRightSidePass + rec typeSectionFinalPass + proc semTypeSection(c: PContext, n: PNode): PNode = ## Processes a type section. This must be done in separate passes, in order ## to allow the type definitions in the section to reference each other ## without regard for the order of their definitions. - typeSectionLeftSidePass(c, n) - typeSectionRightSidePass(c, n) - typeSectionFinalPass(c, n) + if sfNoForward notin c.module.flags or nfSem notin n.flags: + typeSectionLeftSidePass(c, n) + typeSectionRightSidePass(c, n) + typeSectionFinalPass(c, n) result = n proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) = @@ -898,27 +957,35 @@ proc semDo(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = var n = n - n = replaceTypesInBody(c, pt, n) - result = n + let original = n.sons[namePos].sym + let s = copySym(original, false) + incl(s.flags, sfFromGeneric) + n = replaceTypesInBody(c, pt, n, original) + result = n + s.ast = result + n.sons[namePos].sym = s n.sons[genericParamsPos] = emptyNode - n.sons[paramsPos] = n.typ.n - + let params = n.typ.n + n.sons[paramsPos] = params + s.typ = n.typ + for i in 1..<params.len: + if params[i].typ.kind in {tyTypeDesc, tyGenericParam, + tyFromExpr, tyFieldAccessor}+tyTypeClasses: + localError(params[i].info, "cannot infer type of parameter: " & + params[i].sym.name.s) openScope(c) - var s = n.sons[namePos].sym pushOwner(s) - addParams(c, n.typ.n, skProc) + addParams(c, params, skProc) pushProcCon(c, s) addResult(c, n.typ.sons[0], n.info, skProc) addResultNode(c, n) let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) - n.sons[bodyPos] = transformBody(c.module, semBody, n.sons[namePos].sym) + n.sons[bodyPos] = transformBody(c.module, semBody, s) popProcCon(c) popOwner() closeScope(c) - s.ast = result - # alternative variant (not quite working): # var prc = arg[0].sym # let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info) @@ -974,6 +1041,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = "signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T") incl(s.flags, sfUsed) of "=": + if s.magic == mAsgn: return incl(s.flags, sfUsed) let t = s.typ if t.len == 3 and t.sons[0] == nil and t.sons[1].kind == tyVar: @@ -1033,12 +1101,13 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, n.sons[namePos] = newSymNode(s) s.ast = n #s.scope = c.currentScope - - if sfNoForward in c.module.flags and - sfSystemModule notin c.module.flags: - addInterfaceOverloadableSymAt(c, c.currentScope, s) - s.flags.incl sfForward - return + when false: + # disable for now + if sfNoForward in c.module.flags and + sfSystemModule notin c.module.flags: + addInterfaceOverloadableSymAt(c, c.currentScope, s) + s.flags.incl sfForward + return else: s = n[namePos].sym s.owner = getCurrOwner() @@ -1071,6 +1140,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # semParamList(c, n.sons[ParamsPos], nil, s) else: s.typ = newProcType(c, n.info) + if tfTriggersCompileTime in s.typ.flags: incl(s.flags, sfCompileTime) if n.sons[patternPos].kind != nkEmpty: n.sons[patternPos] = semPattern(c, n.sons[patternPos]) if s.kind in skIterators: @@ -1160,6 +1230,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if n.sons[patternPos].kind != nkEmpty: c.patterns.add(s) if isAnon: result.typ = s.typ + if isTopLevel(c) and s.kind != skClosureIterator and + s.typ.callConv == ccClosure: + message(s.info, warnDeprecated, "top level '.closure' calling convention") proc determineType(c: PContext, s: PSym) = if s.typ != nil: return @@ -1268,6 +1341,8 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode = of wLocks: result = n result.typ = n.sons[1].typ + of wNoRewrite: + incl(result.flags, nfNoRewrite) else: discard proc semStaticStmt(c: PContext, n: PNode): PNode = @@ -1319,7 +1394,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = for i in countup(0, length - 1): let k = n.sons[i].kind case k - of nkFinally, nkExceptBranch, nkDefer: + of nkFinally, nkExceptBranch: # stand-alone finally and except blocks are # transformed into regular try blocks: # @@ -1360,7 +1435,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = localError(result.info, "type class predicate failed") of tyUnknown: continue else: discard - if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]): + if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]): voidContext = true n.typ = enforceVoidContext if i == last and (length == 1 or efWantValue in flags): @@ -1372,21 +1447,13 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = n.typ = n.sons[i].typ if not isEmptyType(n.typ): n.kind = nkStmtListExpr case n.sons[i].kind - of nkVarSection, nkLetSection: - let (outer, inner) = insertDestructors(c, n.sons[i]) - if outer != nil: - n.sons[i] = outer - var rest = newNode(nkStmtList, n.info, n.sons[i+1 .. length-1]) - inner.addSon(semStmtList(c, rest, flags)) - n.sons.setLen(i+1) - return of LastBlockStmts: for j in countup(i + 1, length - 1): case n.sons[j].kind of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: discard else: localError(n.sons[j].info, errStmtInvalidAfterReturn) else: discard - if result.len == 1: + if result.len == 1 and result.sons[0].kind != nkDefer: result = result.sons[0] when defined(nimfix): if result.kind == nkCommentStmt and not result.comment.isNil and diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 161d22fc1..2dda8276d 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -110,6 +110,13 @@ type toBind, toMixin, toInject: IntSet owner: PSym cursorInBody: bool # only for nimsuggest + bracketExpr: PNode + +template withBracketExpr(ctx, x, body: untyped) = + let old = ctx.bracketExpr + ctx.bracketExpr = x + body + ctx.bracketExpr = old proc getIdentNode(c: var TemplCtx, n: PNode): PNode = case n.kind @@ -184,10 +191,25 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = else: let ident = getIdentNode(c, n) if not isTemplParam(c, ident): - let local = newGenSym(k, ident, c) - addPrelimDecl(c.c, local) - styleCheckDef(n.info, local) - replaceIdentBySym(n, newSymNode(local, n.info)) + # fix #2670, consider: + # + # when b: + # var a = "hi" + # else: + # var a = 5 + # echo a + # + # We need to ensure that both 'a' produce the same gensym'ed symbol. + # So we need only check the *current* scope. + let s = localSearchInScope(c.c, considerQuotedIdent ident) + if s != nil and s.owner == c.owner and sfGenSym in s.flags: + styleCheckUse(n.info, s) + replaceIdentBySym(n, newSymNode(s, n.info)) + else: + let local = newGenSym(k, ident, c) + addPrelimDecl(c.c, local) + styleCheckDef(n.info, local) + replaceIdentBySym(n, newSymNode(local, n.info)) else: replaceIdentBySym(n, ident) @@ -260,6 +282,53 @@ proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind; start=0) = addLocalDecl(c, a.sons[j], symKind) proc semPattern(c: PContext, n: PNode): PNode + +proc semTemplBodySons(c: var TemplCtx, n: PNode): PNode = + result = n + for i in 0.. < n.len: + result.sons[i] = semTemplBody(c, n.sons[i]) + +proc wrapInBind(c: var TemplCtx; n: PNode; opr: string): PNode = + let ident = getIdent(opr) + if ident.id in c.toInject: return n + + let s = searchInScopes(c.c, ident) + if s != nil: + var callee: PNode + if contains(c.toBind, s.id): + callee = symChoice(c.c, n, s, scClosed) + elif contains(c.toMixin, s.name.id): + callee = symChoice(c.c, n, s, scForceOpen) + elif s.owner == c.owner and sfGenSym in s.flags: + # template tmp[T](x: var seq[T]) = + # var yz: T + incl(s.flags, sfUsed) + callee = newSymNode(s, n.info) + styleCheckUse(n.info, s) + else: + callee = semTemplSymbol(c.c, n, s) + + let call = newNodeI(nkCall, n.info) + call.add(callee) + for i in 0 .. n.len-1: call.add(n[i]) + result = newNodeI(nkBind, n.info, 2) + result.sons[0] = n + result.sons[1] = call + else: + result = n + +proc oprIsRoof(n: PNode): bool = + const roof = "^" + case n.kind + of nkIdent: result = n.ident.s == roof + of nkSym: result = n.sym.name.s == roof + of nkAccQuoted: + if n.len == 1: + result = oprIsRoof(n.sons[0]) + of nkOpenSymChoice, nkClosedSymChoice: + result = oprIsRoof(n.sons[0]) + else: discard + proc semTemplBody(c: var TemplCtx, n: PNode): PNode = result = n semIdeForTemplateOrGenericCheck(n, c.cursorInBody) @@ -402,27 +471,65 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = result.sons[1] = semTemplBody(c, n.sons[1]) of nkPragma: result = onlyReplaceParams(c, n) - else: + of nkBracketExpr: + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent("[]"), n.info) + for i in 0 ..< n.len: result.add(n[i]) + let n0 = semTemplBody(c, n.sons[0]) + withBracketExpr c, n0: + result = semTemplBodySons(c, result) + of nkCurlyExpr: + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent("{}"), n.info) + for i in 0 ..< n.len: result.add(n[i]) + result = semTemplBodySons(c, result) + of nkAsgn, nkFastAsgn: + checkSonsLen(n, 2) + let a = n.sons[0] + let b = n.sons[1] + + let k = a.kind + case k + of nkBracketExpr: + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent("[]="), n.info) + for i in 0 ..< a.len: result.add(a[i]) + result.add(b) + let a0 = semTemplBody(c, a.sons[0]) + withBracketExpr c, a0: + result = semTemplBodySons(c, result) + of nkCurlyExpr: + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent("{}="), n.info) + for i in 0 ..< a.len: result.add(a[i]) + result.add(b) + result = semTemplBodySons(c, result) + else: + result = semTemplBodySons(c, n) + of nkCallKinds-{nkPostfix}: + result = semTemplBodySons(c, n) + if c.bracketExpr != nil and n.len == 2 and oprIsRoof(n.sons[0]): + result.add c.bracketExpr + of nkDotExpr, nkAccQuoted: # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam', # so we use the generic code for nkDotExpr too - if n.kind == nkDotExpr or n.kind == nkAccQuoted: - let s = qualifiedLookUp(c.c, n, {}) - if s != nil: - # do not symchoice a quoted template parameter (bug #2390): - if s.owner == c.owner and s.kind == skParam and - n.kind == nkAccQuoted and n.len == 1: - incl(s.flags, sfUsed) - styleCheckUse(n.info, s) - return newSymNode(s, n.info) - elif contains(c.toBind, s.id): - return symChoice(c.c, n, s, scClosed) - elif contains(c.toMixin, s.name.id): - return symChoice(c.c, n, s, scForceOpen) - else: - return symChoice(c.c, n, s, scOpen) - result = n - for i in countup(0, sonsLen(n) - 1): - result.sons[i] = semTemplBody(c, n.sons[i]) + let s = qualifiedLookUp(c.c, n, {}) + if s != nil: + # do not symchoice a quoted template parameter (bug #2390): + if s.owner == c.owner and s.kind == skParam and + n.kind == nkAccQuoted and n.len == 1: + incl(s.flags, sfUsed) + styleCheckUse(n.info, s) + return newSymNode(s, n.info) + elif contains(c.toBind, s.id): + return symChoice(c.c, n, s, scClosed) + elif contains(c.toMixin, s.name.id): + return symChoice(c.c, n, s, scForceOpen) + else: + return symChoice(c.c, n, s, scOpen) + result = semTemplBodySons(c, n) + else: + result = semTemplBodySons(c, n) proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode = result = n @@ -452,30 +559,9 @@ proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode = for i in countup(0, sonsLen(n) - 1): result.sons[i] = semTemplBodyDirty(c, n.sons[i]) -proc transformToExpr(n: PNode): PNode = - var realStmt: int - result = n - case n.kind - of nkStmtList: - realStmt = - 1 - for i in countup(0, sonsLen(n) - 1): - case n.sons[i].kind - of nkCommentStmt, nkEmpty, nkNilLit: - discard - else: - if realStmt == - 1: realStmt = i - else: realStmt = - 2 - if realStmt >= 0: result = transformToExpr(n.sons[realStmt]) - else: n.kind = nkStmtListExpr - of nkBlockStmt: - n.kind = nkBlockExpr - #nkIfStmt: n.kind = nkIfExpr // this is not correct! - else: - discard - proc semTemplateDef(c: PContext, n: PNode): PNode = var s: PSym - if c.p.owner.kind == skModule: + if isTopLevel(c): s = semIdentVis(c, skTemplate, n.sons[0], {sfExported}) incl(s.flags, sfGlobal) else: @@ -530,9 +616,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = n.sons[bodyPos] = semTemplBodyDirty(ctx, n.sons[bodyPos]) else: n.sons[bodyPos] = semTemplBody(ctx, n.sons[bodyPos]) - if s.typ.sons[0].kind notin {tyStmt, tyTypeDesc}: - n.sons[bodyPos] = transformToExpr(n.sons[bodyPos]) - # only parameters are resolved, no type checking is performed + # only parameters are resolved, no type checking is performed semIdeForTemplateOrGeneric(c, n.sons[bodyPos], ctx.cursorInBody) closeScope(c) popOwner() @@ -585,6 +669,11 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode = localError(n.info, errInvalidExpression) result = n + proc stupidStmtListExpr(n: PNode): bool = + for i in 0 .. n.len-2: + if n[i].kind notin {nkEmpty, nkCommentStmt}: return false + result = true + result = n case n.kind of nkIdent: @@ -610,6 +699,12 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode = localError(n.info, errInvalidExpression) else: localError(n.info, errInvalidExpression) + of nkStmtList, nkStmtListExpr: + if stupidStmtListExpr(n): + result = semPatternBody(c, n.lastSon) + else: + for i in countup(0, sonsLen(n) - 1): + result.sons[i] = semPatternBody(c, n.sons[i]) of nkCallKinds: let s = qualifiedLookUp(c.c, n.sons[0], {}) if s != nil: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 8c7bd7243..65cb9421b 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -23,6 +23,9 @@ proc newConstraint(c: PContext, k: TTypeKind): PType = proc semEnum(c: PContext, n: PNode, prev: PType): PType = if n.sonsLen == 0: return newConstraint(c, tyEnum) + elif n.sonsLen == 1: + # don't create an empty tyEnum; fixes #3052 + return errorType(c) var counter, x: BiggestInt e: PSym @@ -130,6 +133,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = let isCall = ord(n.kind in nkCallKinds) let n = if n[0].kind == nkBracket: n[0] else: n checkMinSonsLen(n, 1) + var base = semTypeNode(c, n.lastSon, nil) result = newOrPrevType(kind, prev, c) # check every except the last is an object: for i in isCall .. n.len-2: @@ -137,7 +141,6 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = if region.skipTypes({tyGenericInst}).kind notin {tyError, tyObject}: message n[i].info, errGenerated, "region needs to be an object type" addSonSkipIntLit(result, region) - var base = semTypeNode(c, n.lastSon, nil) addSonSkipIntLit(result, base) proc semVarType(c: PContext, n: PNode, prev: PType): PType = @@ -251,19 +254,21 @@ proc semArrayIndex(c: PContext, n: PNode): PType = proc semArray(c: PContext, n: PNode, prev: PType): PType = var base: PType - result = newOrPrevType(tyArray, prev, c) if sonsLen(n) == 3: # 3 = length(array indx base) - var indx = semArrayIndex(c, n[1]) - addSonSkipIntLit(result, indx) - if indx.kind == tyGenericInst: indx = lastSon(indx) - if indx.kind notin {tyGenericParam, tyStatic, tyFromExpr}: - if not isOrdinalType(indx): + let indx = semArrayIndex(c, n[1]) + var indxB = indx + if indxB.kind == tyGenericInst: indxB = lastSon(indxB) + if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr}: + if not isOrdinalType(indxB): localError(n.sons[1].info, errOrdinalTypeExpected) - elif enumHasHoles(indx): + elif enumHasHoles(indxB): localError(n.sons[1].info, errEnumXHasHoles, - typeToString(indx.skipTypes({tyRange}))) + typeToString(indxB.skipTypes({tyRange}))) base = semTypeNode(c, n.sons[2], nil) + # ensure we only construct a tyArray when there was no error (bug #3048): + result = newOrPrevType(tyArray, prev, c) + addSonSkipIntLit(result, indx) addSonSkipIntLit(result, base) else: localError(n.info, errArrayExpectsTwoTypeParams) @@ -503,8 +508,9 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int, var typ = skipTypes(a.sons[0].typ, abstractVar-{tyTypeDesc}) if not isOrdinalType(typ): localError(n.info, errSelectorMustBeOrdinal) - elif firstOrd(typ) < 0: - localError(n.info, errOrdXMustNotBeNegative, a.sons[0].sym.name.s) + elif firstOrd(typ) != 0: + localError(n.info, errGenerated, "low(" & $a.sons[0].sym.name.s & + ") must be 0 for discriminant") elif lengthOrd(typ) > 0x00007FFF: localError(n.info, errLenXinvalid, a.sons[0].sym.name.s) var chckCovered = true @@ -629,7 +635,7 @@ proc skipGenericInvocation(t: PType): PType {.inline.} = result = t if result.kind == tyGenericInvocation: result = result.sons[0] - while result.kind in {tyGenericInst, tyGenericBody}: + while result.kind in {tyGenericInst, tyGenericBody, tyRef, tyPtr}: result = lastSon(result) proc addInheritedFields(c: PContext, check: var IntSet, pos: var int, @@ -651,7 +657,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = if base.isNil: localError(n.info, errIllegalRecursionInTypeX, "object") else: - var concreteBase = skipGenericInvocation(base).skipTypes(skipPtrs) + var concreteBase = skipGenericInvocation(base) if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags: addInheritedFields(c, check, pos, concreteBase) else: @@ -712,12 +718,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, if paramType == nil: return # (e.g. proc return type) proc addImplicitGenericImpl(typeClass: PType, typId: PIdent): PType = - let finalTypId = if typId != nil: typId - else: getIdent(paramName & ":type") if genericParams == nil: # This happens with anonymous proc types appearing in signatures # XXX: we need to lift these earlier return + let finalTypId = if typId != nil: typId + else: getIdent(paramName & ":type") # is this a bindOnce type class already present in the param list? for i in countup(0, genericParams.len - 1): if genericParams.sons[i].sym.name.id == finalTypId.id: @@ -751,7 +757,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, case paramType.kind: of tyAnything: - result = addImplicitGeneric(newTypeS(tyGenericParam, c)) + result = addImplicitGenericImpl(newTypeS(tyGenericParam, c), nil) of tyStatic: # proc(a: expr{string}, b: expr{nkLambda}) @@ -862,6 +868,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, of tyExpr: if procKind notin {skMacro, skTemplate}: result = addImplicitGeneric(newTypeS(tyAnything, c)) + #result = addImplicitGenericImpl(newTypeS(tyGenericParam, c), nil) of tyGenericParam: markUsed(info, paramType.sym) @@ -964,18 +971,26 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, elif kind == skIterator: # XXX This is special magic we should likely get rid of r = newTypeS(tyExpr, c) + message(n.info, warnDeprecated, "implicit return type for 'iterator'") if r != nil: # turn explicit 'void' return type into 'nil' because the rest of the # compiler only checks for 'nil': if skipTypes(r, {tyGenericInst}).kind != tyEmpty: # 'auto' as a return type does not imply a generic: - if r.kind != tyExpr: + if r.kind == tyAnything: + # 'p(): auto' and 'p(): expr' are equivalent, but the rest of the + # compiler is hardly aware of 'auto': + r = newTypeS(tyExpr, c) + elif r.kind != tyExpr: if r.sym == nil or sfAnon notin r.sym.flags: let lifted = liftParamType(c, kind, genericParams, r, "result", n.sons[0].info) - if lifted != nil: r = lifted - r.flags.incl tfRetType + if lifted != nil: + r = lifted + #if r.kind != tyGenericParam: + #echo "came here for ", typeToString(r) + r.flags.incl tfRetType r = skipIntLit(r) if kind == skIterator: # see tchainediterators @@ -1049,6 +1064,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = return newOrPrevType(tyError, prev, c) else: var m = newCandidate(c, t) + m.isNoCall = true matches(c, n, copyTree(n), m) if m.state != csMatch and not m.typedescMatched: @@ -1085,6 +1101,7 @@ proc semTypeExpr(c: PContext, n: PNode): PType = result = n.typ.base else: localError(n.info, errTypeExpected, n.renderTree) + result = errorType(c) proc freshType(res, prev: PType): PType {.inline.} = if prev.isNil: @@ -1140,7 +1157,17 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = else: result = semAnonTuple(c, n, prev) of nkCallKinds: - if isRange(n): + let x = n[0] + let ident = case x.kind + of nkIdent: x.ident + of nkSym: x.sym.name + of nkClosedSymChoice, nkOpenSymChoice: x[0].sym.name + else: nil + if ident != nil and ident.s == "[]": + let b = newNodeI(nkBracketExpr, n.info) + for i in 1..<n.len: b.add(n[i]) + result = semTypeNode(c, b, prev) + elif ident != nil and ident.id == ord(wDotDot): result = semRangeAux(c, n, prev) elif n[0].kind notin nkIdentKinds: result = semTypeExpr(c, n) @@ -1326,12 +1353,23 @@ proc processMagicType(c: PContext, m: PSym) = rawAddSon(m.typ, newTypeS(tyEmpty, c)) of mIntSetBaseType: setMagicType(m, tyRange, intSize) of mNil: setMagicType(m, tyNil, ptrSize) - of mExpr: setMagicType(m, tyExpr, 0) - of mStmt: setMagicType(m, tyStmt, 0) + of mExpr: + if m.name.s == "auto": + setMagicType(m, tyAnything, 0) + else: + setMagicType(m, tyExpr, 0) + if m.name.s == "expr": m.typ.flags.incl tfOldSchoolExprStmt + of mStmt: + setMagicType(m, tyStmt, 0) + if m.name.s == "stmt": m.typ.flags.incl tfOldSchoolExprStmt of mTypeDesc: setMagicType(m, tyTypeDesc, 0) rawAddSon(m.typ, newTypeS(tyNone, c)) - of mVoidType: setMagicType(m, tyEmpty, 0) + of mVoidType: + setMagicType(m, tyEmpty, 0) + # for historical reasons we conflate 'void' with 'empty' so that '@[]' + # has the type 'seq[void]'. + m.typ.flags.incl tfVoid of mArray: setMagicType(m, tyArray, 0) of mOpenArray: @@ -1348,7 +1386,8 @@ proc processMagicType(c: PContext, m: PSym) = of mOrdinal: setMagicType(m, tyOrdinal, 0) rawAddSon(m.typ, newTypeS(tyNone, c)) - of mPNimrodNode: discard + of mPNimrodNode: + incl m.typ.flags, tfTriggersCompileTime of mShared: setMagicType(m, tyObject, 0) m.typ.n = newNodeI(nkRecList, m.info) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index c5caf8b92..7957ac50a 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -75,8 +75,12 @@ proc searchInstTypes*(key: PType): PType = proc cacheTypeInst*(inst: PType) = # XXX: add to module's generics # update the refcount - let genericTyp = inst.sons[0] - genericTyp.sym.typeInstCache.safeAdd(inst) + let gt = inst.sons[0] + let t = if gt.kind == tyGenericBody: gt.lastSon else: gt + if t.kind in {tyStatic, tyGenericParam, tyIter} + tyTypeClasses: + return + gt.sym.typeInstCache.safeAdd(inst) + type TReplTypeVars* {.final.} = object @@ -90,6 +94,8 @@ type allowMetaTypes*: bool # allow types such as seq[Number] # i.e. the result contains unresolved generics skipTypedesc*: bool # wether we should skip typeDescs + owner*: PSym # where this instantiation comes from + recursionLimit: int proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym @@ -207,6 +213,9 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode = proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym = if s == nil: return nil + # symbol is not our business: + if cl.owner != nil and s.owner != cl.owner: + return s result = PSym(idTableGet(cl.symMap, s)) if result == nil: result = copySym(s, false) @@ -365,6 +374,19 @@ proc propagateFieldFlags(t: PType, n: PNode) = else: discard proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = + template bailout = + if cl.recursionLimit > 100: + # bail out, see bug #2509. But note this caching is in general wrong, + # look at this example where TwoVectors should not share the generic + # instantiations (bug #3112): + + # type + # Vector[N: static[int]] = array[N, float64] + # TwoVectors[Na, Nb: static[int]] = (Vector[Na], Vector[Nb]) + result = PType(idTableGet(cl.localCache, t)) + if result != nil: return result + inc cl.recursionLimit + result = t if t == nil: return @@ -420,8 +442,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result = t of tyGenericInst: - result = PType(idTableGet(cl.localCache, t)) - if result != nil: return result + bailout() result = instCopyType(cl, t) idTablePut(cl.localCache, t, result) for i in 1 .. <result.sonsLen: @@ -431,8 +452,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = else: if containsGenericType(t): #if not cl.allowMetaTypes: - result = PType(idTableGet(cl.localCache, t)) - if result != nil: return result + bailout() result = instCopyType(cl, t) result.size = -1 # needs to be recomputed #if not cl.allowMetaTypes: @@ -440,8 +460,14 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = for i in countup(0, sonsLen(result) - 1): if result.sons[i] != nil: - result.sons[i] = replaceTypeVarsT(cl, result.sons[i]) - propagateToOwner(result, result.sons[i]) + var r = replaceTypeVarsT(cl, result.sons[i]) + if result.kind == tyObject: + # carefully coded to not skip the precious tyGenericInst: + let r2 = r.skipTypes({tyGenericInst}) + if r2.kind in {tyPtr, tyRef}: + r = skipTypes(r2, {tyPtr, tyRef}) + result.sons[i] = r + propagateToOwner(result, r) result.n = replaceTypeVarsN(cl, result.n) @@ -459,22 +485,33 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = else: discard -proc initTypeVars*(p: PContext, pt: TIdTable, info: TLineInfo): TReplTypeVars = +proc initTypeVars*(p: PContext, pt: TIdTable, info: TLineInfo; + owner: PSym): TReplTypeVars = initIdTable(result.symMap) copyIdTable(result.typeMap, pt) initIdTable(result.localCache) result.info = info result.c = p + result.owner = owner + +proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode; + owner: PSym): PNode = + var cl = initTypeVars(p, pt, n.info, owner) + pushInfoContext(n.info) + result = replaceTypeVarsN(cl, n) + popInfoContext() -proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode): PNode = - var cl = initTypeVars(p, pt, n.info) +proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode; + original, new: PSym): PNode = + var cl = initTypeVars(p, pt, n.info, original) + idTablePut(cl.symMap, original, new) pushInfoContext(n.info) result = replaceTypeVarsN(cl, n) popInfoContext() proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo, t: PType): PType = - var cl = initTypeVars(p, pt, info) + var cl = initTypeVars(p, pt, info, nil) pushInfoContext(info) result = replaceTypeVarsT(cl, t) popInfoContext() diff --git a/compiler/service.nim b/compiler/service.nim index 7cb9d7d29..640dd2010 100644 --- a/compiler/service.nim +++ b/compiler/service.nim @@ -18,7 +18,7 @@ when useCaas: # We cache modules and the dependency graph. However, we don't check for # file changes but expect the client to tell us about them, otherwise the -# repeated CRC calculations may turn out to be too slow. +# repeated hash calculations may turn out to be too slow. var curCaasCmd* = "" diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 2a9d15b5a..9fda8c860 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -47,6 +47,7 @@ type coerceDistincts*: bool # this is an explicit coercion that can strip away # a distrinct type typedescMatched*: bool + isNoCall*: bool # misused for generic type instantiations C[T] inheritancePenalty: int # to prefer closest father object type errors*: CandidateErrors # additional clarifications to be displayed to the # user if overload resolution fails @@ -57,6 +58,10 @@ type isSubtype, isSubrange, # subrange of the wanted type; no type conversion # but apart from that counts as ``isSubtype`` + isBothMetaConvertible # generic proc parameter was matched against + # generic type, e.g., map(mySeq, x=>x+1), + # maybe recoverable by rerun if the parameter is + # the proc's return value isInferred, # generic proc was matched against a concrete type isInferredConvertible, # same as above, but requiring proc CC conversion isGeneric, @@ -158,11 +163,15 @@ proc sumGeneric(t: PType): int = t = t.sons[0] inc result inc isvar + of tyTypeDesc: + t = t.lastSon + if t.kind == tyEmpty: break + inc result of tyGenericInvocation, tyTuple: result += ord(t.kind == tyGenericInvocation) for i in 0 .. <t.len: result += t.sons[i].sumGeneric break - of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break + of tyGenericParam, tyExpr, tyStatic, tyStmt: break of tyBool, tyChar, tyEnum, tyObject, tyProc, tyPointer, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128, tyUInt..tyUInt64: @@ -218,12 +227,12 @@ proc cmpCandidates*(a, b: TCandidate): int = result = complexDisambiguation(a.callee, b.callee) proc writeMatches*(c: TCandidate) = - writeln(stdout, "exact matches: " & $c.exactMatches) - writeln(stdout, "generic matches: " & $c.genericMatches) - writeln(stdout, "subtype matches: " & $c.subtypeMatches) - writeln(stdout, "intconv matches: " & $c.intConvMatches) - writeln(stdout, "conv matches: " & $c.convMatches) - writeln(stdout, "inheritance: " & $c.inheritancePenalty) + writeLine(stdout, "exact matches: " & $c.exactMatches) + writeLine(stdout, "generic matches: " & $c.genericMatches) + writeLine(stdout, "subtype matches: " & $c.subtypeMatches) + writeLine(stdout, "intconv matches: " & $c.intConvMatches) + writeLine(stdout, "conv matches: " & $c.convMatches) + writeLine(stdout, "inheritance: " & $c.inheritancePenalty) proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string = if arg.kind in nkSymChoices: @@ -264,6 +273,9 @@ proc concreteType(c: TCandidate, t: PType): PType = addSonSkipIntLit(result, t.sons[1]) # XXX: semantic checking for the type? of tyNil: result = nil # what should it be? + of tyTypeDesc: + if c.isNoCall: result = t + else: result = nil of tySequence, tySet: if t.sons[0].kind == tyEmpty: result = nil else: result = t @@ -381,9 +393,30 @@ proc inconsistentVarTypes(f, a: PType): bool {.inline.} = result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar) proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = - var f = f + ## For example we have: + ## .. code-block:: nim + ## proc myMap[T,S](sIn: seq[T], f: proc(x: T): S): seq[S] = ... + ## proc innerProc[Q,W](q: Q): W = ... + ## And we want to match: myMap(@[1,2,3], innerProc) + ## This proc (procParamTypeRel) will do the following steps in + ## three different calls: + ## - matches f=T to a=Q. Since f is metatype, we resolve it + ## to int (which is already known at this point). So in this case + ## Q=int mapping will be saved to c.bindings. + ## - matches f=S to a=W. Both of these metatypes are unknown, so we + ## return with isBothMetaConvertible to ask for rerun. + ## - matches f=S to a=W. At this point the return type of innerProc + ## is known (we get it from c.bindings). We can use that value + ## to match with f, and save back to c.bindings. + var + f = f + a = a if a.isMetaType: + let aResolved = PType(idTableGet(c.bindings, a)) + if aResolved != nil: + a = aResolved + if a.isMetaType: if f.isMetaType: # We are matching a generic proc (as proc param) # to another generic type appearing in the proc @@ -393,12 +426,15 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = f = generateTypeInstance(c.c, c.bindings, c.call.info, f) if f == nil or f.isMetaType: # no luck resolving the type, so the inference fails - return isNone + return isBothMetaConvertible + # Note that this typeRel call will save a's resolved type into c.bindings let reverseRel = typeRel(c, a, f) if reverseRel >= isGeneric: result = isInferred #inc c.genericMatches else: + # Note that this typeRel call will save f's resolved type into c.bindings + # if f is metatype. result = typeRel(c, f, a) if result <= isSubtype or inconsistentVarTypes(f, a): @@ -442,8 +478,9 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = elif f.callConv != a.callConv: # valid to pass a 'nimcall' thingie to 'closure': if f.callConv == ccClosure and a.callConv == ccDefault: - result = if result != isInferred: isConvertible - else: isInferredConvertible + result = if result == isInferred: isInferredConvertible + elif result == isBothMetaConvertible: isBothMetaConvertible + else: isConvertible else: return isNone when useEffectSystem: @@ -474,6 +511,9 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = proc matchUserTypeClass*(c: PContext, m: var TCandidate, ff, a: PType): TTypeRelation = var body = ff.skipTypes({tyUserTypeClassInst}) + if c.inTypeClass > 20: + localError(body.n[3].info, $body.n[3] & " too nested for type matching") + return isNone openScope(c) inc c.inTypeClass @@ -553,7 +593,7 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode): PNode = # Here, N-1 will be initially nkStaticExpr that can be evaluated only after # N is bound to a concrete value during the matching of the first param. # This proc is used to evaluate such static expressions. - let instantiated = replaceTypesInBody(c.c, c.bindings, n) + let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil) result = c.c.semExpr(c.c, instantiated) proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = @@ -644,6 +684,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyAnything: return if f.kind == tyAnything: isGeneric else: isNone + + of tyUserTypeClass, tyUserTypeClassInst: + # consider this: 'var g: Node' *within* a concept where 'Node' + # is a concept too (tgraph) + let x = typeRel(c, a, f, false) + if x >= isGeneric: + return isGeneric else: discard case f.kind @@ -727,8 +774,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = isNone else: discard of tyOpenArray, tyVarargs: - # varargs[expr] is special - if f.kind == tyVarargs and f.sons[0].kind == tyExpr: return + # varargs[expr] is special too but handled earlier. So we only need to + # handle varargs[stmt] which is the same as varargs[typed]: + if f.kind == tyVarargs: + if tfOldSchoolExprStmt in f.sons[0].flags: + if f.sons[0].kind == tyExpr: return + elif f.sons[0].kind == tyStmt: return case a.kind of tyOpenArray, tyVarargs: result = typeRel(c, base(f), base(a)) @@ -748,6 +799,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = isConvertible elif typeRel(c, base(f), a.sons[0]) >= isGeneric: result = isConvertible + of tyString: + if f.kind == tyOpenArray: + if f.sons[0].kind == tyChar: + result = isConvertible + elif f.sons[0].kind == tyGenericParam and a.len > 0 and + typeRel(c, base(f), base(a)) >= isGeneric: + result = isConvertible else: discard of tySequence: case a.kind @@ -1091,6 +1149,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = isNone of tyStmt: + if aOrig != nil and tfOldSchoolExprStmt notin f.flags: + put(c.bindings, f, aOrig) result = isGeneric of tyProxy: @@ -1204,7 +1264,7 @@ proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) = case r of isConvertible, isIntConv: inc(m.convMatches, convMatch) of isSubtype, isSubrange: inc(m.subtypeMatches) - of isGeneric, isInferred: inc(m.genericMatches) + of isGeneric, isInferred, isBothMetaConvertible: inc(m.genericMatches) of isFromIntLit: inc(m.intConvMatches, 256) of isInferredConvertible: inc(m.convMatches) @@ -1270,6 +1330,29 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, put(m.bindings, f, inlined) return argSemantized + # If r == isBothMetaConvertible then we rerun typeRel. + # bothMetaCounter is for safety to avoid any infinite loop, + # I don't have any example when it is needed. + # lastBindingsLenth is used to check whether m.bindings remains the same, + # because in that case there is no point in continuing. + var bothMetaCounter = 0 + var lastBindingsLength = -1 + while r == isBothMetaConvertible and + lastBindingsLength != m.bindings.counter and + bothMetaCounter < 100: + lastBindingsLength = m.bindings.counter + inc(bothMetaCounter) + if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds: + result = c.semInferredLambda(c, m.bindings, arg) + elif arg.kind != nkSym: + return nil + else: + let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info) + result = newSymNode(inferred, arg.info) + inc(m.convMatches) + arg = result + r = typeRel(m, f, arg.typ) + case r of isConvertible: inc(m.convMatches) @@ -1281,7 +1364,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, result = implicitConv(nkHiddenStdConv, f, arg, m, c) of isSubtype: inc(m.subtypeMatches) - result = implicitConv(nkHiddenSubConv, f, arg, m, c) + if f.kind == tyTypeDesc: + result = arg + else: + result = implicitConv(nkHiddenSubConv, f, arg, m, c) of isSubrange: inc(m.subtypeMatches) if f.kind == tyVar: @@ -1291,6 +1377,8 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, of isInferred, isInferredConvertible: if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds: result = c.semInferredLambda(c, m.bindings, arg) + elif arg.kind != nkSym: + return nil else: let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info) result = newSymNode(inferred, arg.info) @@ -1310,6 +1398,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, result.typ = getInstantiatedType(c, arg, m, f) else: result = arg + of isBothMetaConvertible: + # This is the result for the 101th time. + result = nil of isFromIntLit: # too lazy to introduce another ``*matches`` field, so we conflate # ``isIntConv`` and ``isIntLit`` here: @@ -1454,6 +1545,10 @@ proc incrIndexType(t: PType) = assert t.kind == tyArrayConstr inc t.sons[0].n.sons[1].intVal +template isVarargsUntyped(x): expr = + x.kind == tyVarargs and x.sons[0].kind == tyExpr and + tfOldSchoolExprStmt notin x.sons[0].flags + proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var IntSet) = template checkConstraint(n: expr) {.immediate, dirty.} = @@ -1482,10 +1577,17 @@ proc matchesAux(c: PContext, n, nOrig: PNode, var formalLen = m.callee.n.len addSon(m.call, copyTree(n.sons[0])) var container: PNode = nil # constructed container - var formal: PSym = nil + var formal: PSym = if formalLen > 1: m.callee.n.sons[1].sym else: nil while a < n.len: - if n.sons[a].kind == nkExprEqExpr: + if a >= formalLen-1 and formal != nil and formal.typ.isVarargsUntyped: + if container.isNil: + container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, n.info)) + setSon(m.call, formal.position + 1, container) + else: + incrIndexType(container.typ) + addSon(container, n.sons[a]) + elif n.sons[a].kind == nkExprEqExpr: # named param # check if m.callee has such a param: prepareNamedParam(n.sons[a]) @@ -1507,7 +1609,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, n.sons[a].sons[1] = prepareOperand(c, formal.typ, n.sons[a].sons[1]) n.sons[a].typ = n.sons[a].sons[1].typ var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, - n.sons[a].sons[1], nOrig.sons[a].sons[1]) + n.sons[a].sons[1], n.sons[a].sons[1]) if arg == nil: m.state = csNoMatch return @@ -1536,7 +1638,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, addSon(m.call, copyTree(n.sons[a])) elif formal != nil and formal.typ.kind == tyVarargs: # beware of the side-effects in 'prepareOperand'! So only do it for - # varags matching. See tests/metatype/tstatic_overloading. + # varargs matching. See tests/metatype/tstatic_overloading. m.baseTypeMatch = false n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, @@ -1599,6 +1701,10 @@ proc partialMatch*(c: PContext, n, nOrig: PNode, m: var TCandidate) = matchesAux(c, n, nOrig, m, marker) proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = + if m.calleeSym != nil and m.calleeSym.magic in {mArrGet, mArrPut}: + m.state = csMatch + m.call = n + return var marker = initIntSet() matchesAux(c, n, nOrig, m, marker) if m.state == csNoMatch: return diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 6b168670c..18d723315 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -15,57 +15,79 @@ import algorithm, sequtils const sep = '\t' - sectionSuggest = "sug" - sectionDef = "def" - sectionContext = "con" - sectionUsage = "use" + +type + Suggest* = object + section*: IdeCmd + qualifiedPath*: seq[string] + filePath*: string + line*: int # Starts at 1 + column*: int # Starts at 0 + doc*: string # Not escaped (yet) + symkind*: TSymKind + forth*: string # XXX TODO object on symkind + +var + suggestionResultHook*: proc (result: Suggest) {.closure.} #template sectionSuggest(): expr = "##begin\n" & getStackTrace() & "##end\n" template origModuleName(m: PSym): string = m.name.s -proc symToStr(s: PSym, isLocal: bool, section: string, li: TLineInfo): string = - result = section - result.add(sep) +proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo): Suggest = + result.section = parseIdeCmd(section) if optIdeTerse in gGlobalOptions: - if s.kind in routineKinds: - result.add renderTree(s.ast, {renderNoBody, renderNoComments, - renderDocComments, renderNoPragmas}) - else: - result.add s.name.s - result.add(sep) - result.add(toFullPath(li)) - result.add(sep) - result.add($toLinenumber(li)) - result.add(sep) - result.add($toColumn(li)) + result.symkind = s.kind + result.filePath = toFullPath(li) + result.line = toLinenumber(li) + result.column = toColumn(li) else: - result.add($s.kind) - result.add(sep) + result.symkind = s.kind + result.qualifiedPath = @[] if not isLocal and s.kind != skModule: let ow = s.owner if ow.kind != skModule and ow.owner != nil: let ow2 = ow.owner - result.add(ow2.origModuleName) - result.add('.') - result.add(ow.origModuleName) - result.add('.') - result.add(s.name.s) - result.add(sep) - if s.typ != nil: - result.add(typeToString(s.typ)) - result.add(sep) - result.add(toFullPath(li)) - result.add(sep) - result.add($toLinenumber(li)) - result.add(sep) - result.add($toColumn(li)) - result.add(sep) + result.qualifiedPath.add(ow2.origModuleName) + result.qualifiedPath.add(ow.origModuleName) + result.qualifiedPath.add(s.name.s) + + if s.typ != nil: + result.forth = typeToString(s.typ) + else: + result.forth = "" + result.filePath = toFullPath(li) + result.line = toLinenumber(li) + result.column = toColumn(li) when not defined(noDocgen): - result.add(s.extractDocComment.escape) + result.doc = s.extractDocComment -proc symToStr(s: PSym, isLocal: bool, section: string): string = - result = symToStr(s, isLocal, section, s.info) +proc `$`(suggest: Suggest): string = + result = $suggest.section + result.add(sep) + result.add($suggest.symkind) + result.add(sep) + result.add(suggest.qualifiedPath.join(".")) + result.add(sep) + result.add(suggest.forth) + result.add(sep) + result.add(suggest.filePath) + result.add(sep) + result.add($suggest.line) + result.add(sep) + result.add($suggest.column) + result.add(sep) + when not defined(noDocgen): + result.add(suggest.doc.escape) + +proc symToSuggest(s: PSym, isLocal: bool, section: string): Suggest = + result = symToSuggest(s, isLocal, section, s.info) + +proc suggestResult(s: Suggest) = + if not isNil(suggestionResultHook): + suggestionResultHook(s) + else: + suggestWriteln($(s)) proc filterSym(s: PSym): bool {.inline.} = result = s.kind != skModule @@ -82,9 +104,9 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} = result = true break -proc suggestField(c: PContext, s: PSym, outputs: var int) = +proc suggestField(c: PContext, s: PSym, outputs: var int) = if filterSym(s) and fieldVisible(c, s): - suggestWriteln(symToStr(s, isLocal=true, sectionSuggest)) + suggestResult(symToSuggest(s, isLocal=true, $ideSug)) inc outputs template wholeSymTab(cond, section: expr) {.immediate.} = @@ -97,20 +119,20 @@ template wholeSymTab(cond, section: expr) {.immediate.} = for item in entries: let it {.inject.} = item if cond: - suggestWriteln(symToStr(it, isLocal = isLocal, section)) + suggestResult(symToSuggest(it, isLocal = isLocal, section)) inc outputs -proc suggestSymList(c: PContext, list: PNode, outputs: var int) = - for i in countup(0, sonsLen(list) - 1): +proc suggestSymList(c: PContext, list: PNode, outputs: var int) = + for i in countup(0, sonsLen(list) - 1): if list.sons[i].kind == nkSym: suggestField(c, list.sons[i].sym, outputs) #else: InternalError(list.info, "getSymFromList") -proc suggestObject(c: PContext, n: PNode, outputs: var int) = +proc suggestObject(c: PContext, n: PNode, outputs: var int) = case n.kind - of nkRecList: + of nkRecList: for i in countup(0, sonsLen(n)-1): suggestObject(c, n.sons[i], outputs) - of nkRecCase: + of nkRecCase: var L = sonsLen(n) if L > 0: suggestObject(c, n.sons[0], outputs) @@ -118,7 +140,7 @@ proc suggestObject(c: PContext, n: PNode, outputs: var int) = of nkSym: suggestField(c, n.sym, outputs) else: discard -proc nameFits(c: PContext, s: PSym, n: PNode): bool = +proc nameFits(c: PContext, s: PSym, n: PNode): bool = var op = n.sons[0] if op.kind in {nkOpenSymChoice, nkClosedSymChoice}: op = op.sons[0] var opr: PIdent @@ -128,8 +150,8 @@ proc nameFits(c: PContext, s: PSym, n: PNode): bool = else: return false result = opr.id == s.name.id -proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool = - case candidate.kind +proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool = + case candidate.kind of OverloadableSyms: var m: TCandidate initCandidate(c, m, candidate, nil) @@ -138,11 +160,11 @@ proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool = else: result = false -proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var int) = +proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var int) = wholeSymTab(filterSym(it) and nameFits(c, it, n) and argsFit(c, it, n, nOrig), - sectionContext) + $ideCon) -proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} = +proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} = if s.typ != nil and sonsLen(s.typ) > 1 and s.typ.sons[1] != nil: # special rule: if system and some weird generic match via 'tyExpr' # or 'tyGenericParam' we won't list it either to reduce the noise (nobody @@ -157,7 +179,7 @@ proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} = proc suggestOperations(c: PContext, n: PNode, typ: PType, outputs: var int) = assert typ != nil - wholeSymTab(filterSymNoOpr(it) and typeFits(c, it, typ), sectionSuggest) + wholeSymTab(filterSymNoOpr(it) and typeFits(c, it, typ), $ideSug) proc suggestEverything(c: PContext, n: PNode, outputs: var int) = # do not produce too many symbols: @@ -166,7 +188,7 @@ proc suggestEverything(c: PContext, n: PNode, outputs: var int) = if scope == c.topLevelScope: isLocal = false for it in items(scope.symbols): if filterSym(it): - suggestWriteln(symToStr(it, isLocal = isLocal, sectionSuggest)) + suggestResult(symToSuggest(it, isLocal = isLocal, $ideSug)) inc outputs if scope == c.topLevelScope: break @@ -176,48 +198,48 @@ proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) = var typ = n.typ if typ == nil: # a module symbol has no type for example: - if n.kind == nkSym and n.sym.kind == skModule: - if n.sym == c.module: + if n.kind == nkSym and n.sym.kind == skModule: + if n.sym == c.module: # all symbols accessible, because we are in the current module: for it in items(c.topLevelScope.symbols): - if filterSym(it): - suggestWriteln(symToStr(it, isLocal=false, sectionSuggest)) + if filterSym(it): + suggestResult(symToSuggest(it, isLocal=false, $ideSug)) inc outputs - else: - for it in items(n.sym.tab): - if filterSym(it): - suggestWriteln(symToStr(it, isLocal=false, sectionSuggest)) + else: + for it in items(n.sym.tab): + if filterSym(it): + suggestResult(symToSuggest(it, isLocal=false, $ideSug)) inc outputs else: # fallback: suggestEverything(c, n, outputs) - elif typ.kind == tyEnum and n.kind == nkSym and n.sym.kind == skType: + elif typ.kind == tyEnum and n.kind == nkSym and n.sym.kind == skType: # look up if the identifier belongs to the enum: var t = typ - while t != nil: + while t != nil: suggestSymList(c, t.n, outputs) t = t.sons[0] suggestOperations(c, n, typ, outputs) else: typ = skipTypes(typ, {tyGenericInst, tyVar, tyPtr, tyRef}) - if typ.kind == tyObject: + if typ.kind == tyObject: var t = typ - while true: + while true: suggestObject(c, t.n, outputs) - if t.sons[0] == nil: break + if t.sons[0] == nil: break t = skipTypes(t.sons[0], {tyGenericInst}) suggestOperations(c, n, typ, outputs) - elif typ.kind == tyTuple and typ.n != nil: + elif typ.kind == tyTuple and typ.n != nil: suggestSymList(c, typ.n, outputs) suggestOperations(c, n, typ, outputs) else: suggestOperations(c, n, typ, outputs) type - TCheckPointResult = enum + TCheckPointResult = enum cpNone, cpFuzzy, cpExact -proc inCheckpoint(current: TLineInfo): TCheckPointResult = +proc inCheckpoint(current: TLineInfo): TCheckPointResult = if current.fileIndex == gTrackPos.fileIndex: if current.line == gTrackPos.line and abs(current.col-gTrackPos.col) < 4: @@ -233,8 +255,8 @@ proc findClosestDot(n: PNode): PNode = result = findClosestDot(n.sons[i]) if result != nil: return -proc findClosestCall(n: PNode): PNode = - if n.kind in nkCallKinds and inCheckpoint(n.info) == cpExact: +proc findClosestCall(n: PNode): PNode = + if n.kind in nkCallKinds and inCheckpoint(n.info) == cpExact: result = n else: for i in 0.. <safeLen(n): @@ -248,8 +270,8 @@ proc isTracked(current: TLineInfo, tokenLen: int): bool = if col >= current.col and col <= current.col+tokenLen-1: return true -proc findClosestSym(n: PNode): PNode = - if n.kind == nkSym and inCheckpoint(n.info) == cpExact: +proc findClosestSym(n: PNode): PNode = + if n.kind == nkSym and inCheckpoint(n.info) == cpExact: result = n elif n.kind notin {nkNone..nkNilLit}: for i in 0.. <sonsLen(n): @@ -263,16 +285,16 @@ var proc findUsages(info: TLineInfo; s: PSym) = if usageSym == nil and isTracked(info, s.name.s.len): usageSym = s - suggestWriteln(symToStr(s, isLocal=false, sectionUsage)) + suggestResult(symToSuggest(s, isLocal=false, $ideUse)) elif s == usageSym: if lastLineInfo != info: - suggestWriteln(symToStr(s, isLocal=false, sectionUsage, info)) + suggestResult(symToSuggest(s, isLocal=false, $ideUse, info)) lastLineInfo = info proc findDefinition(info: TLineInfo; s: PSym) = if s.isNil: return if isTracked(info, s.name.s.len): - suggestWriteln(symToStr(s, isLocal=false, sectionDef)) + suggestResult(symToSuggest(s, isLocal=false, $ideDef)) suggestQuit() proc ensureIdx[T](x: var T, y: int) = @@ -287,6 +309,10 @@ proc suggestSym*(info: TLineInfo; s: PSym) {.inline.} = findUsages(info, s) elif gIdeCmd == ideDef: findDefinition(info, s) + elif gIdeCmd == ideDus and s != nil: + if isTracked(info, s.name.s.len): + suggestResult(symToSuggest(s, isLocal=false, $ideDef)) + findUsages(info, s) proc markUsed(info: TLineInfo; s: PSym) = incl(s.flags, sfUsed) @@ -306,15 +332,15 @@ proc safeSemExpr*(c: PContext, n: PNode): PNode = except ERecoverableError: result = ast.emptyNode -proc suggestExpr*(c: PContext, node: PNode) = +proc suggestExpr*(c: PContext, node: PNode) = if nfIsCursor notin node.flags: if gTrackPos.line < 0: return var cp = inCheckpoint(node.info) if cp == cpNone: return var outputs = 0 # This keeps semExpr() from coming here recursively: - if c.inCompilesContext > 0: return - inc(c.inCompilesContext) + if c.compilesContextId > 0: return + inc(c.compilesContextId) if gIdeCmd == ideSug: var n = if nfIsCursor in node.flags: node else: findClosestDot(node) @@ -327,7 +353,7 @@ proc suggestExpr*(c: PContext, node: PNode) = #writeStackTrace() else: suggestEverything(c, n, outputs) - + elif gIdeCmd == ideCon: var n = if nfIsCursor in node.flags: node else: findClosestCall(node) if n == nil: n = node @@ -342,9 +368,9 @@ proc suggestExpr*(c: PContext, node: PNode) = if x.kind == nkEmpty or x.typ == nil: break addSon(a, x) suggestCall(c, a, n, outputs) - - dec(c.inCompilesContext) - if outputs > 0 and gIdeCmd != ideUse: suggestQuit() -proc suggestStmt*(c: PContext, n: PNode) = + dec(c.compilesContextId) + if outputs > 0 and gIdeCmd notin {ideUse, ideDus}: suggestQuit() + +proc suggestStmt*(c: PContext, n: PNode) = suggestExpr(c, n) diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index 7f9e25f82..021910544 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -9,24 +9,24 @@ ## Implements the dispatcher for the different parsers. -import - strutils, llstream, ast, astalgo, idents, lexer, options, msgs, parser, +import + strutils, llstream, ast, astalgo, idents, lexer, options, msgs, parser, pbraces, filters, filter_tmpl, renderer -type - TFilterKind* = enum +type + TFilterKind* = enum filtNone, filtTemplate, filtReplace, filtStrip - TParserKind* = enum + TParserKind* = enum skinStandard, skinStrongSpaces, skinBraces, skinEndX -const +const parserNames*: array[TParserKind, string] = ["standard", "strongspaces", "braces", "endx"] filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace", "strip"] type - TParsers*{.final.} = object + TParsers*{.final.} = object skin*: TParserKind parser*: TParser @@ -42,60 +42,65 @@ proc parseTopLevelStmt*(p: var TParsers): PNode # implementation proc parseFile(fileIdx: int32): PNode = - var + var p: TParsers f: File let filename = fileIdx.toFullPathConsiderDirty if not open(f, filename): rawMessage(errCannotOpenFile, filename) - return + return openParsers(p, fileIdx, llStreamOpen(f)) result = parseAll(p) closeParsers(p) -proc parseAll(p: var TParsers): PNode = +proc parseAll(p: var TParsers): PNode = case p.skin of skinStandard, skinStrongSpaces: result = parser.parseAll(p.parser) - of skinBraces: + of skinBraces: result = pbraces.parseAll(p.parser) - of skinEndX: - internalError("parser to implement") + of skinEndX: + internalError("parser to implement") result = ast.emptyNode - -proc parseTopLevelStmt(p: var TParsers): PNode = + +proc parseTopLevelStmt(p: var TParsers): PNode = case p.skin of skinStandard, skinStrongSpaces: result = parser.parseTopLevelStmt(p.parser) - of skinBraces: + of skinBraces: result = pbraces.parseTopLevelStmt(p.parser) - of skinEndX: - internalError("parser to implement") + of skinEndX: + internalError("parser to implement") result = ast.emptyNode - -proc utf8Bom(s: string): int = - if (s[0] == '\xEF') and (s[1] == '\xBB') and (s[2] == '\xBF'): + +proc utf8Bom(s: string): int = + if (s[0] == '\xEF') and (s[1] == '\xBB') and (s[2] == '\xBF'): result = 3 - else: + else: result = 0 - -proc containsShebang(s: string, i: int): bool = - if (s[i] == '#') and (s[i + 1] == '!'): + +proc containsShebang(s: string, i: int): bool = + if (s[i] == '#') and (s[i + 1] == '!'): var j = i + 2 while s[j] in Whitespace: inc(j) result = s[j] == '/' -proc parsePipe(filename: string, inputStream: PLLStream): PNode = +proc parsePipe(filename: string, inputStream: PLLStream): PNode = result = ast.emptyNode var s = llStreamOpen(filename, fmRead) - if s != nil: + if s != nil: var line = newStringOfCap(80) discard llStreamReadLine(s, line) var i = utf8Bom(line) + var linenumber = 1 if containsShebang(line, i): discard llStreamReadLine(s, line) i = 0 - if line[i] == '#' and line[i+1] == '!': + inc linenumber + if line[i] == '#' and line[i+1] in {'?', '!'}: + if line[i+1] == '!': + message(newLineInfo(filename, linenumber, 1), + warnDeprecated, "use '#?' instead; '#!'") inc(i, 2) while line[i] in Whitespace: inc(i) var q: TParser @@ -104,50 +109,50 @@ proc parsePipe(filename: string, inputStream: PLLStream): PNode = closeParser(q) llStreamClose(s) -proc getFilter(ident: PIdent): TFilterKind = - for i in countup(low(TFilterKind), high(TFilterKind)): - if identEq(ident, filterNames[i]): +proc getFilter(ident: PIdent): TFilterKind = + for i in countup(low(TFilterKind), high(TFilterKind)): + if identEq(ident, filterNames[i]): return i result = filtNone -proc getParser(ident: PIdent): TParserKind = - for i in countup(low(TParserKind), high(TParserKind)): - if identEq(ident, parserNames[i]): +proc getParser(ident: PIdent): TParserKind = + for i in countup(low(TParserKind), high(TParserKind)): + if identEq(ident, parserNames[i]): return i rawMessage(errInvalidDirectiveX, ident.s) -proc getCallee(n: PNode): PIdent = - if n.kind in nkCallKinds and n.sons[0].kind == nkIdent: +proc getCallee(n: PNode): PIdent = + if n.kind in nkCallKinds and n.sons[0].kind == nkIdent: result = n.sons[0].ident - elif n.kind == nkIdent: + elif n.kind == nkIdent: result = n.ident - else: + else: rawMessage(errXNotAllowedHere, renderTree(n)) - -proc applyFilter(p: var TParsers, n: PNode, filename: string, - stdin: PLLStream): PLLStream = + +proc applyFilter(p: var TParsers, n: PNode, filename: string, + stdin: PLLStream): PLLStream = var ident = getCallee(n) var f = getFilter(ident) case f - of filtNone: + of filtNone: p.skin = getParser(ident) result = stdin - of filtTemplate: + of filtTemplate: result = filterTmpl(stdin, filename, n) - of filtStrip: + of filtStrip: result = filterStrip(stdin, filename, n) - of filtReplace: + of filtReplace: result = filterReplace(stdin, filename, n) - if f != filtNone: - if gVerbosity >= 2: + if f != filtNone: + if hintCodeBegin in gNotes: rawMessage(hintCodeBegin, []) msgWriteln(result.s) rawMessage(hintCodeEnd, []) -proc evalPipe(p: var TParsers, n: PNode, filename: string, - start: PLLStream): PLLStream = +proc evalPipe(p: var TParsers, n: PNode, filename: string, + start: PLLStream): PLLStream = result = start - if n.kind == nkEmpty: return + if n.kind == nkEmpty: return if n.kind == nkInfix and n.sons[0].kind == nkIdent and identEq(n.sons[0].ident, "|"): for i in countup(1, 2): @@ -159,8 +164,8 @@ proc evalPipe(p: var TParsers, n: PNode, filename: string, result = evalPipe(p, n.sons[0], filename, result) else: result = applyFilter(p, n, filename, result) - -proc openParsers(p: var TParsers, fileIdx: int32, inputstream: PLLStream) = + +proc openParsers(p: var TParsers, fileIdx: int32, inputstream: PLLStream) = var s: PLLStream p.skin = skinStandard let filename = fileIdx.toFullPathConsiderDirty @@ -172,6 +177,6 @@ proc openParsers(p: var TParsers, fileIdx: int32, inputstream: PLLStream) = parser.openParser(p.parser, fileIdx, s, false) of skinStrongSpaces: parser.openParser(p.parser, fileIdx, s, true) - + proc closeParsers(p: var TParsers) = parser.closeParser(p.parser) diff --git a/compiler/tccgen.nim b/compiler/tccgen.nim index 0082dfcbb..7616641fc 100644 --- a/compiler/tccgen.nim +++ b/compiler/tccgen.nim @@ -12,10 +12,10 @@ import {.compile: "../tinyc/libtcc.c".} -proc tinyCErrorHandler(closure: pointer, msg: cstring) {.cdecl.} = +proc tinyCErrorHandler(closure: pointer, msg: cstring) {.cdecl.} = rawMessage(errGenerated, $msg) - -proc initTinyCState: PccState = + +proc initTinyCState: PccState = result = openCCState() setErrorFunc(result, nil, tinyCErrorHandler) @@ -23,25 +23,25 @@ var gTinyC = initTinyCState() libIncluded = false -proc addFile(filename: string) = +proc addFile(filename: string) = if addFile(gTinyC, filename) != 0'i32: rawMessage(errCannotOpenFile, filename) -proc setupEnvironment = +proc setupEnvironment = when defined(amd64): defineSymbol(gTinyC, "__x86_64__", nil) elif defined(i386): - defineSymbol(gTinyC, "__i386__", nil) + defineSymbol(gTinyC, "__i386__", nil) when defined(linux): defineSymbol(gTinyC, "__linux__", nil) defineSymbol(gTinyC, "__linux", nil) var nimrodDir = getPrefixDir() addIncludePath(gTinyC, libpath) - when defined(windows): + when defined(windows): addSysincludePath(gTinyC, nimrodDir / "tinyc/win32/include") addSysincludePath(gTinyC, nimrodDir / "tinyc/include") - when defined(windows): + when defined(windows): defineSymbol(gTinyC, "_WIN32", nil) # we need Mingw's headers too: var gccbin = getConfigVar("gcc.path") % ["nimrod", nimrodDir] @@ -54,7 +54,7 @@ proc setupEnvironment = #addFile(nimrodDir / r"tinyc\win32\dllcrt1.o") #addFile(nimrodDir / r"tinyc\win32\dllmain.o") addFile(nimrodDir / r"tinyc\win32\libtcc1.o") - + #addFile(nimrodDir / r"tinyc\win32\lib\crt1.c") #addFile(nimrodDir / r"tinyc\lib\libtcc1.c") else: @@ -62,12 +62,12 @@ proc setupEnvironment = when defined(amd64): addSysincludePath(gTinyC, "/usr/include/x86_64-linux-gnu") -proc compileCCode*(ccode: string) = +proc compileCCode*(ccode: string) = if not libIncluded: libIncluded = true setupEnvironment() discard compileString(gTinyC, ccode) - + proc run*(args: string) = var s = @[cstring(gProjectName)] & map(split(args), proc(x: string): cstring = cstring(x)) var err = tinyc.run(gTinyC, cint(len(s)), cast[cstringArray](addr(s[0]))) != 0'i32 diff --git a/compiler/transf.nim b/compiler/transf.nim index 57547b682..3d78a6b92 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -16,6 +16,7 @@ # * converts "continue" to "break"; disambiguates "break" # * introduces method dispatchers # * performs lambda lifting for closure support +# * transforms 'defer' into a 'try finally' statement import intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os, @@ -44,6 +45,7 @@ type inlining: int # > 0 if we are in inlining context (copy vars) nestedProcs: int # > 0 if we are in a nested proc contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' + deferDetected: bool PTransf = ref TTransfContext proc newTransNode(a: PNode): PTransNode {.inline.} = @@ -113,7 +115,7 @@ proc transformSymAux(c: PTransf, n: PNode): PNode = # return liftIterSym(n) var b: PNode var tc = c.transCon - if sfBorrow in n.sym.flags: + if sfBorrow in n.sym.flags and n.sym.kind in routineKinds: # simply exchange the symbol: b = n.sym.getBody if b.kind != nkSym: internalError(n.info, "wrong AST for borrowed symbol") @@ -137,23 +139,26 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode = if it.kind == nkCommentStmt: result[i] = PTransNode(it) elif it.kind == nkIdentDefs: - if it.sons[0].kind != nkSym: internalError(it.info, "transformVarSection") - internalAssert(it.len == 3) - var newVar = copySym(it.sons[0].sym) - incl(newVar.flags, sfFromGeneric) - # fixes a strange bug for rodgen: - #include(it.sons[0].sym.flags, sfFromGeneric); - newVar.owner = getCurrOwner(c) - idNodeTablePut(c.transCon.mapping, it.sons[0].sym, newSymNode(newVar)) - var defs = newTransNode(nkIdentDefs, it.info, 3) - if importantComments(): - # keep documentation information: - PNode(defs).comment = it.comment - defs[0] = newSymNode(newVar).PTransNode - defs[1] = it.sons[1].PTransNode - defs[2] = transform(c, it.sons[2]) - newVar.ast = defs[2].PNode - result[i] = defs + if it.sons[0].kind == nkSym: + internalAssert(it.len == 3) + var newVar = copySym(it.sons[0].sym) + incl(newVar.flags, sfFromGeneric) + # fixes a strange bug for rodgen: + #include(it.sons[0].sym.flags, sfFromGeneric); + newVar.owner = getCurrOwner(c) + idNodeTablePut(c.transCon.mapping, it.sons[0].sym, newSymNode(newVar)) + var defs = newTransNode(nkIdentDefs, it.info, 3) + if importantComments(): + # keep documentation information: + PNode(defs).comment = it.comment + defs[0] = newSymNode(newVar).PTransNode + defs[1] = it.sons[1].PTransNode + defs[2] = transform(c, it.sons[2]) + newVar.ast = defs[2].PNode + result[i] = defs + else: + # has been transformed into 'param.x' for closure iterators, so keep it: + result[i] = PTransNode(it) else: if it.kind != nkVarTuple: internalError(it.info, "transformVarSection: not nkVarTuple") @@ -299,6 +304,7 @@ proc transformYield(c: PTransf, n: PNode): PTransNode = var e = n.sons[0] # c.transCon.forStmt.len == 3 means that there is one for loop variable # and thus no tuple unpacking: + if e.typ.isNil: return result # can happen in nimsuggest for unknown reasons if skipTypes(e.typ, {tyGenericInst}).kind == tyTuple and c.transCon.forStmt.len != 3: e = skipConv(e) @@ -465,10 +471,13 @@ proc transformFor(c: PTransf, n: PNode): PTransNode = var call = n.sons[length - 2] let labl = newLabel(c, n) - c.breakSyms.add(labl) result = newTransNode(nkBlockStmt, n.info, 2) result[0] = newSymNode(labl).PTransNode - + if call.typ.isNil: + # see bug #3051 + result[1] = newNode(nkEmpty).PTransNode + return result + c.breakSyms.add(labl) if call.typ.kind != tyIter and (call.kind notin nkCallKinds or call.sons[0].kind != nkSym or call.sons[0].sym.kind != skIterator): @@ -479,10 +488,10 @@ proc transformFor(c: PTransf, n: PNode): PTransNode = #echo "transforming: ", renderTree(n) var stmtList = newTransNode(nkStmtList, n.info, 0) + result[1] = stmtList var loopBody = transformLoopBody(c, n.sons[length-1]) - result[1] = stmtList discard c.breakSyms.pop var v = newNodeI(nkVarSection, n.info) @@ -677,6 +686,14 @@ proc commonOptimizations*(c: PSym, n: PNode): PNode = result = n proc transform(c: PTransf, n: PNode): PTransNode = + when false: + var oldDeferAnchor: PNode + if n.kind in {nkElifBranch, nkOfBranch, nkExceptBranch, nkElifExpr, + nkElseExpr, nkElse, nkForStmt, nkWhileStmt, nkFinally, + nkBlockStmt, nkBlockExpr}: + oldDeferAnchor = c.deferAnchor + c.deferAnchor = n + case n.kind of nkSym: result = transformSym(c, n) @@ -709,13 +726,36 @@ proc transform(c: PTransf, n: PNode): PTransNode = result = transformFor(c, n) of nkParForStmt: result = transformSons(c, n) - of nkCaseStmt: result = transformCase(c, n) + of nkCaseStmt: + result = transformCase(c, n) + of nkWhileStmt: result = transformWhile(c, n) + of nkBlockStmt, nkBlockExpr: + result = transformBlock(c, n) + of nkDefer: + c.deferDetected = true + result = transformSons(c, n) + when false: + let deferPart = newNodeI(nkFinally, n.info) + deferPart.add n.sons[0] + let tryStmt = newNodeI(nkTryStmt, n.info) + if c.deferAnchor.isNil: + tryStmt.add c.root + c.root = tryStmt + result = PTransNode(tryStmt) + else: + # modify the corresponding *action*, don't rely on nkStmtList: + let L = c.deferAnchor.len-1 + tryStmt.add c.deferAnchor.sons[L] + c.deferAnchor.sons[L] = tryStmt + result = newTransNode(nkCommentStmt, n.info, 0) + tryStmt.addSon(deferPart) + # disable the original 'defer' statement: + n.kind = nkCommentStmt of nkContinueStmt: result = PTransNode(newNodeI(nkBreakStmt, n.info)) var labl = c.contSyms[c.contSyms.high] add(result, PTransNode(newSymNode(labl))) of nkBreakStmt: result = transformBreak(c, n) - of nkWhileStmt: result = transformWhile(c, n) of nkCallKinds: result = transformCall(c, n) of nkAddr, nkHiddenAddr: @@ -751,8 +791,6 @@ proc transform(c: PTransf, n: PNode): PTransNode = result = transformYield(c, n) else: result = transformSons(c, n) - of nkBlockStmt, nkBlockExpr: - result = transformBlock(c, n) of nkIdentDefs, nkConstDef: result = transformSons(c, n) # XXX comment handling really sucks: @@ -761,6 +799,8 @@ proc transform(c: PTransf, n: PNode): PTransNode = of nkClosure: return PTransNode(n) else: result = transformSons(c, n) + when false: + if oldDeferAnchor != nil: c.deferAnchor = oldDeferAnchor var cnst = getConstExpr(c.module, PNode(result)) # we inline constants if they are not complex constants: if cnst != nil and not dontInlineConstant(n, cnst): @@ -782,12 +822,54 @@ proc openTransf(module: PSym, filename: string): PTransf = result.breakSyms = @[] result.module = module +proc flattenStmts(n: PNode) = + var goOn = true + while goOn: + goOn = false + var i = 0 + while i < n.len: + let it = n[i] + if it.kind in {nkStmtList, nkStmtListExpr}: + n.sons[i..i] = it.sons[0..<it.len] + goOn = true + inc i + +proc liftDeferAux(n: PNode) = + if n.kind in {nkStmtList, nkStmtListExpr}: + flattenStmts(n) + var goOn = true + while goOn: + goOn = false + let last = n.len-1 + for i in 0..last: + if n.sons[i].kind == nkDefer: + let deferPart = newNodeI(nkFinally, n.sons[i].info) + deferPart.add n.sons[i].sons[0] + var tryStmt = newNodeI(nkTryStmt, n.sons[i].info) + var body = newNodeI(n.kind, n.sons[i].info) + if i < last: + body.sons = n.sons[(i+1)..last] + tryStmt.addSon(body) + tryStmt.addSon(deferPart) + n.sons[i] = tryStmt + n.sons.setLen(i+1) + n.typ = n.sons[i].typ + goOn = true + break + for i in 0..n.safeLen-1: + liftDeferAux(n.sons[i]) + +template liftDefer(c, root) = + if c.deferDetected: + liftDeferAux(root) + proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode = if nfTransf in n.flags or prc.kind in {skTemplate}: result = n else: var c = openTransf(module, "") result = processTransf(c, n, prc) + liftDefer(c, result) result = liftLambdas(prc, result) #if prc.kind == skClosureIterator: # result = lambdalifting.liftIterator(prc, result) @@ -802,6 +884,7 @@ proc transformStmt*(module: PSym, n: PNode): PNode = else: var c = openTransf(module, "") result = processTransf(c, n, module) + liftDefer(c, result) result = liftLambdasForTopLevel(module, result) incl(result.flags, nfTransf) when useEffectSystem: trackTopLevelStmt(module, result) @@ -812,4 +895,5 @@ proc transformExpr*(module: PSym, n: PNode): PNode = else: var c = openTransf(module, "") result = processTransf(c, n, module) + liftDefer(c, result) incl(result.flags, nfTransf) diff --git a/compiler/trees.nim b/compiler/trees.nim index 2c631af99..659df334b 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -36,15 +36,18 @@ proc cyclicTree*(n: PNode): bool = var s = newNodeI(nkEmpty, n.info) result = cyclicTreeAux(n, s) -proc exprStructuralEquivalent*(a, b: PNode): bool = +proc exprStructuralEquivalent*(a, b: PNode; strictSymEquality=false): bool = result = false if a == b: result = true elif (a != nil) and (b != nil) and (a.kind == b.kind): case a.kind of nkSym: - # don't go nuts here: same symbol as string is enough: - result = a.sym.name.id == b.sym.name.id + if strictSymEquality: + result = a.sym == b.sym + else: + # don't go nuts here: same symbol as string is enough: + result = a.sym.name.id == b.sym.name.id of nkIdent: result = a.ident.id == b.ident.id of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal @@ -53,7 +56,8 @@ proc exprStructuralEquivalent*(a, b: PNode): bool = else: if sonsLen(a) == sonsLen(b): for i in countup(0, sonsLen(a) - 1): - if not exprStructuralEquivalent(a.sons[i], b.sons[i]): return + if not exprStructuralEquivalent(a.sons[i], b.sons[i], + strictSymEquality): return result = true proc sameTree*(a, b: PNode): bool = diff --git a/compiler/treetab.nim b/compiler/treetab.nim index 8d66d56c7..e6eb8c666 100644 --- a/compiler/treetab.nim +++ b/compiler/treetab.nim @@ -9,36 +9,36 @@ # Implements a table from trees to trees. Does structural equivalence checking. -import +import hashes, ast, astalgo, types -proc hashTree(n: PNode): THash = - if n == nil: return +proc hashTree(n: PNode): Hash = + if n == nil: return result = ord(n.kind) case n.kind - of nkEmpty, nkNilLit, nkType: + of nkEmpty, nkNilLit, nkType: discard - of nkIdent: + of nkIdent: result = result !& n.ident.h of nkSym: result = result !& n.sym.name.h - of nkCharLit..nkUInt64Lit: - if (n.intVal >= low(int)) and (n.intVal <= high(int)): + of nkCharLit..nkUInt64Lit: + if (n.intVal >= low(int)) and (n.intVal <= high(int)): result = result !& int(n.intVal) of nkFloatLit..nkFloat64Lit: - if (n.floatVal >= - 1000000.0) and (n.floatVal <= 1000000.0): + if (n.floatVal >= - 1000000.0) and (n.floatVal <= 1000000.0): result = result !& toInt(n.floatVal) of nkStrLit..nkTripleStrLit: if not n.strVal.isNil: result = result !& hash(n.strVal) - else: - for i in countup(0, sonsLen(n) - 1): + else: + for i in countup(0, sonsLen(n) - 1): result = result !& hashTree(n.sons[i]) - -proc treesEquivalent(a, b: PNode): bool = - if a == b: + +proc treesEquivalent(a, b: PNode): bool = + if a == b: result = true - elif (a != nil) and (b != nil) and (a.kind == b.kind): + elif (a != nil) and (b != nil) and (a.kind == b.kind): case a.kind of nkEmpty, nkNilLit, nkType: result = true of nkSym: result = a.sym.id == b.sym.id @@ -46,64 +46,64 @@ proc treesEquivalent(a, b: PNode): bool = of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal - else: - if sonsLen(a) == sonsLen(b): - for i in countup(0, sonsLen(a) - 1): - if not treesEquivalent(a.sons[i], b.sons[i]): return + else: + if sonsLen(a) == sonsLen(b): + for i in countup(0, sonsLen(a) - 1): + if not treesEquivalent(a.sons[i], b.sons[i]): return result = true if result: result = sameTypeOrNil(a.typ, b.typ) - -proc nodeTableRawGet(t: TNodeTable, k: THash, key: PNode): int = - var h: THash = k and high(t.data) - while t.data[h].key != nil: - if (t.data[h].h == k) and treesEquivalent(t.data[h].key, key): + +proc nodeTableRawGet(t: TNodeTable, k: Hash, key: PNode): int = + var h: Hash = k and high(t.data) + while t.data[h].key != nil: + if (t.data[h].h == k) and treesEquivalent(t.data[h].key, key): return h h = nextTry(h, high(t.data)) result = -1 -proc nodeTableGet*(t: TNodeTable, key: PNode): int = +proc nodeTableGet*(t: TNodeTable, key: PNode): int = var index = nodeTableRawGet(t, hashTree(key), key) if index >= 0: result = t.data[index].val else: result = low(int) - -proc nodeTableRawInsert(data: var TNodePairSeq, k: THash, key: PNode, - val: int) = - var h: THash = k and high(data) + +proc nodeTableRawInsert(data: var TNodePairSeq, k: Hash, key: PNode, + val: int) = + var h: Hash = k and high(data) while data[h].key != nil: h = nextTry(h, high(data)) assert(data[h].key == nil) data[h].h = k data[h].key = key data[h].val = val -proc nodeTablePut*(t: var TNodeTable, key: PNode, val: int) = +proc nodeTablePut*(t: var TNodeTable, key: PNode, val: int) = var n: TNodePairSeq - var k: THash = hashTree(key) + var k: Hash = hashTree(key) var index = nodeTableRawGet(t, k, key) - if index >= 0: + if index >= 0: assert(t.data[index].key != nil) t.data[index].val = val - else: - if mustRehash(len(t.data), t.counter): + else: + if mustRehash(len(t.data), t.counter): newSeq(n, len(t.data) * GrowthFactor) - for i in countup(0, high(t.data)): - if t.data[i].key != nil: + for i in countup(0, high(t.data)): + if t.data[i].key != nil: nodeTableRawInsert(n, t.data[i].h, t.data[i].key, t.data[i].val) swap(t.data, n) nodeTableRawInsert(t.data, k, key, val) inc(t.counter) -proc nodeTableTestOrSet*(t: var TNodeTable, key: PNode, val: int): int = +proc nodeTableTestOrSet*(t: var TNodeTable, key: PNode, val: int): int = var n: TNodePairSeq - var k: THash = hashTree(key) + var k: Hash = hashTree(key) var index = nodeTableRawGet(t, k, key) - if index >= 0: + if index >= 0: assert(t.data[index].key != nil) result = t.data[index].val - else: - if mustRehash(len(t.data), t.counter): + else: + if mustRehash(len(t.data), t.counter): newSeq(n, len(t.data) * GrowthFactor) - for i in countup(0, high(t.data)): - if t.data[i].key != nil: + for i in countup(0, high(t.data)): + if t.data[i].key != nil: nodeTableRawInsert(n, t.data[i].h, t.data[i].key, t.data[i].val) swap(t.data, n) nodeTableRawInsert(t.data, k, key, val) diff --git a/compiler/types.nim b/compiler/types.nim index e205f5722..3846be8a0 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -395,7 +395,7 @@ proc rangeToStr(n: PNode): string = const typeToStr: array[TTypeKind, string] = ["None", "bool", "Char", "empty", - "Array Constructor [$1]", "nil", "expr", "stmt", "typeDesc", + "Array Constructor [$1]", "nil", "untyped", "typed", "typeDesc", "GenericInvocation", "GenericBody", "GenericInst", "GenericParam", "distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple", "set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc", @@ -411,6 +411,10 @@ const const preferToResolveSymbols = {preferName, preferModuleInfo, preferGenericArg} +proc addTypeFlags(name: var string, typ: PType) {.inline.} = + if tfShared in typ.flags: name = "shared " & name + if tfNotNil in typ.flags: name.add(" not nil") + proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = var t = typ result = "" @@ -418,11 +422,13 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = if prefer in preferToResolveSymbols and t.sym != nil and sfAnon notin t.sym.flags: if t.kind == tyInt and isIntLit(t): - return t.sym.name.s & " literal(" & $t.n.intVal & ")" - if prefer == preferName or t.sym.owner.isNil: - return t.sym.name.s + result = t.sym.name.s & " literal(" & $t.n.intVal & ")" + elif prefer == preferName or t.sym.owner.isNil: + result = t.sym.name.s else: - return t.sym.owner.name.s & '.' & t.sym.name.s + result = t.sym.owner.name.s & '.' & t.sym.name.s + result.addTypeFlags(t) + return case t.kind of tyInt: if not isIntLit(t) or prefer == preferExported: @@ -481,7 +487,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = result = "not " & typeToString(t.sons[0]) of tyExpr: internalAssert t.len == 0 - result = "expr" + result = "untyped" of tyFromExpr, tyFieldAccessor: result = renderTree(t.n) of tyArray: @@ -563,8 +569,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = result = typeToStr[t.kind] % typeToString(t.sons[0]) else: result = typeToStr[t.kind] - if tfShared in t.flags: result = "shared " & result - if tfNotNil in t.flags: result.add(" not nil") + result.addTypeFlags(t) proc resultType(t: PType): PType = assert(t.kind == tyProc) @@ -986,7 +991,9 @@ proc compareTypes*(x, y: PType, var c = initSameTypeClosure() c.cmp = cmp c.flags = flags - result = sameTypeAux(x, y, c) + if x == y: result = true + elif x.isNil or y.isNil: result = false + else: result = sameTypeAux(x, y, c) proc inheritanceDiff*(a, b: PType): int = # | returns: 0 iff `a` == `b` @@ -1260,13 +1267,16 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = else: result = 8 a = result of tySet: - length = lengthOrd(typ.sons[0]) - if length <= 8: result = 1 - elif length <= 16: result = 2 - elif length <= 32: result = 4 - elif length <= 64: result = 8 - elif align(length, 8) mod 8 == 0: result = align(length, 8) div 8 - else: result = align(length, 8) div 8 + 1 + if typ.sons[0].kind == tyGenericParam: + result = szUnknownSize + else: + length = lengthOrd(typ.sons[0]) + if length <= 8: result = 1 + elif length <= 16: result = 2 + elif length <= 32: result = 4 + elif length <= 64: result = 8 + elif align(length, 8) mod 8 == 0: result = align(length, 8) div 8 + else: result = align(length, 8) div 8 + 1 a = result of tyRange: result = computeSizeAux(typ.sons[0], a) diff --git a/compiler/vm.nim b/compiler/vm.nim index 1c6c9a30b..495b0c747 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -83,7 +83,7 @@ proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, proc bailOut(c: PCtx; tos: PStackFrame) = stackTrace(c, tos, c.exceptionInstr, errUnhandledExceptionX, - c.currentExceptionA.sons[2].strVal) + c.currentExceptionA.sons[3].skipColon.strVal) when not defined(nimComputedGoto): {.pragma: computedGoto.} @@ -120,10 +120,10 @@ template decodeBx(k: expr) {.immediate, dirty.} = template move(a, b: expr) {.immediate, dirty.} = system.shallowCopy(a, b) # XXX fix minor 'shallowCopy' overloading bug in compiler -proc createStrKeepNode(x: var TFullReg) = +proc createStrKeepNode(x: var TFullReg; keepNode=true) = if x.node.isNil: x.node = newNode(nkStrLit) - elif x.node.kind == nkNilLit: + elif x.node.kind == nkNilLit and keepNode: when defined(useNodeIds): let id = x.node.id system.reset(x.node[]) @@ -311,7 +311,7 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = if f.position == x: dest.node.strVal = if f.ast.isNil: f.name.s else: f.ast.strVal return - internalError("opConv for enum") + dest.node.strVal = styp.sym.name.s & " " & $x of tyInt..tyInt64: dest.node.strVal = $src.intVal of tyUInt..tyUInt64: @@ -320,8 +320,19 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = dest.node.strVal = if src.intVal == 0: "false" else: "true" of tyFloat..tyFloat128: dest.node.strVal = $src.floatVal - of tyString, tyCString: + of tyString: dest.node.strVal = src.node.strVal + of tyCString: + if src.node.kind == nkBracket: + # Array of chars + var strVal = "" + for son in src.node.sons: + let c = char(son.intVal) + if c == '\0': break + strVal.add(c) + dest.node.strVal = strVal + else: + dest.node.strVal = src.node.strVal of tyChar: dest.node.strVal = $chr(src.intVal) else: @@ -351,7 +362,7 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = myreset(dest); dest.kind = rkFloat case skipTypes(srctyp, abstractRange).kind of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyBool, tyChar: - dest.floatVal = toFloat(src.intVal.int) + dest.floatVal = toBiggestFloat(src.intVal) else: dest.floatVal = src.floatVal else: @@ -383,8 +394,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let instr = c.code[pc] let ra = instr.regA #if c.traceActive: - # echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra + #echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra, " rb ", instr.regB, " rc ", instr.regC # message(c.debug[pc], warnUser, "Trace") + case instr.opcode of opcEof: return regs[ra] of opcRet: @@ -407,8 +419,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeB(rkInt) regs[ra].intVal = regs[rb].intVal of opcAsgnStr: - decodeB(rkNode) - createStrKeepNode regs[ra] + decodeBC(rkNode) + createStrKeepNode regs[ra], rc != 0 regs[ra].node.strVal = regs[rb].node.strVal of opcAsgnFloat: decodeB(rkFloat) @@ -431,7 +443,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = assert regs[rb].kind == rkNode let nb = regs[rb].node case nb.kind - of nkCharLit..nkInt64Lit: + of nkCharLit..nkUInt64Lit: ensureKind(rkInt) regs[ra].intVal = nb.intVal of nkFloatLit..nkFloat64Lit: @@ -474,14 +486,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeBC(rkNode) let src = regs[rb].node if src.kind notin {nkEmpty..nkNilLit}: - let n = src.sons[rc].skipColon + let n = src.sons[rc + ord(src.kind == nkObjConstr)].skipColon regs[ra].node = n else: stackTrace(c, tos, pc, errNilAccess) of opcWrObj: # a.b = c decodeBC(rkNode) - putIntoNode(regs[ra].node.sons[rb], regs[rc]) + let shiftedRb = rb + ord(regs[ra].node.kind == nkObjConstr) + let dest = regs[ra].node + if dest.sons[shiftedRb].kind == nkExprColonExpr: + putIntoNode(dest.sons[shiftedRb].sons[1], regs[rc]) + else: + putIntoNode(dest.sons[shiftedRb], regs[rc]) of opcWrStrIdx: decodeBC(rkNode) let idx = regs[rb].intVal.int @@ -494,7 +511,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].regAddr = addr(regs[rb]) of opcAddrNode: decodeB(rkNodeAddr) - regs[ra].nodeAddr = addr(regs[rb].node) + if regs[rb].kind == rkNode: + regs[ra].nodeAddr = addr(regs[rb].node) + else: + stackTrace(c, tos, pc, errGenerated, "limited VM support for 'addr'") of opcLdDeref: # a = b[] let ra = instr.regA @@ -509,8 +529,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of rkNode: if regs[rb].node.kind == nkNilLit: stackTrace(c, tos, pc, errNilAccess) - assert regs[rb].node.kind == nkRefTy - regs[ra].node = regs[rb].node.sons[0] + if regs[rb].node.kind == nkRefTy: + regs[ra].node = regs[rb].node.sons[0] + else: + stackTrace(c, tos, pc, errGenerated, "limited VM support for 'ref'") else: stackTrace(c, tos, pc, errNilAccess) of opcWrDeref: @@ -682,11 +704,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcLtu: decodeBC(rkInt) regs[ra].intVal = ord(regs[rb].intVal <% regs[rc].intVal) - of opcEqRef, opcEqNimrodNode: + of opcEqRef: decodeBC(rkInt) regs[ra].intVal = ord((regs[rb].node.kind == nkNilLit and regs[rc].node.kind == nkNilLit) or regs[rb].node == regs[rc].node) + of opcEqNimrodNode: + decodeBC(rkInt) + regs[ra].intVal = + ord(exprStructuralEquivalent(regs[rb].node, regs[rc].node, + strictSymEquality=true)) + of opcSameNodeType: + decodeBC(rkInt) + regs[ra].intVal = ord(regs[rb].node.typ.sameTypeOrNil regs[rc].node.typ) of opcXor: decodeBC(rkInt) regs[ra].intVal = ord(regs[rb].intVal != regs[rc].intVal) @@ -770,16 +800,24 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.add(copyTree(regs[rb].regToNode)) else: stackTrace(c, tos, pc, errNilAccess) + of opcGetImpl: + decodeB(rkNode) + let a = regs[rb].node + if a.kind == nkSym: + regs[ra].node = if a.sym.ast.isNil: newNode(nkNilLit) + else: copyTree(a.sym.ast) + else: + stackTrace(c, tos, pc, errFieldXNotFound, "symbol") of opcEcho: let rb = instr.regB if rb == 1: - msgWriteln(regs[ra].node.strVal) + msgWriteln(regs[ra].node.strVal, {msgStdout}) else: var outp = "" for i in ra..ra+rb-1: #if regs[i].kind != rkNode: debug regs[i] outp.add(regs[i].node.strVal) - msgWriteln(outp) + msgWriteln(outp, {msgStdout}) of opcContainsSet: decodeBC(rkInt) regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].regToNode)) @@ -1047,18 +1085,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = # set to default value: for i in oldLen .. <newLen: regs[ra].node.sons[i] = newNodeI(nkEmpty, c.debug[pc]) - of opcSwap: - let rb = instr.regB - if regs[ra].kind == regs[rb].kind: - case regs[ra].kind - of rkNone: discard - of rkInt: swap regs[ra].intVal, regs[rb].intVal - of rkFloat: swap regs[ra].floatVal, regs[rb].floatVal - of rkNode: swap regs[ra].node, regs[rb].node - of rkRegisterAddr: swap regs[ra].regAddr, regs[rb].regAddr - of rkNodeAddr: swap regs[ra].nodeAddr, regs[rb].nodeAddr - else: - internalError(c.debug[pc], "cannot swap operands") of opcReset: internalError(c.debug[pc], "too implement") of opcNarrowS: @@ -1121,7 +1147,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeB(rkInt) let a = regs[rb].node case a.kind - of nkCharLit..nkInt64Lit: regs[ra].intVal = a.intVal + of nkCharLit..nkUInt64Lit: regs[ra].intVal = a.intVal else: stackTrace(c, tos, pc, errFieldXNotFound, "intVal") of opcNFloatVal: decodeB(rkFloat) @@ -1172,9 +1198,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = c.module) of opcGorge: decodeBC(rkNode) + inc pc + let rd = c.code[pc].regA + createStr regs[ra] regs[ra].node.strVal = opGorge(regs[rb].node.strVal, - regs[rc].node.strVal) + regs[rc].node.strVal, regs[rd].node.strVal) of opcNError: stackTrace(c, tos, pc, errUser, regs[ra].node.strVal) of opcNWarning: @@ -1276,7 +1305,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNSetIntVal: decodeB(rkNode) var dest = regs[ra].node - if dest.kind in {nkCharLit..nkInt64Lit} and + if dest.kind in {nkCharLit..nkUInt64Lit} and regs[rb].kind in {rkInt}: dest.intVal = regs[rb].intVal else: @@ -1385,6 +1414,31 @@ proc execute(c: PCtx, start: int): PNode = newSeq(tos.slots, c.prc.maxSlots) result = rawExecute(c, start, tos).regToNode +proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode = + if sym.kind in routineKinds: + if sym.typ.len-1 != args.len: + localError(sym.info, + "NimScript: expected $# arguments, but got $#" % [ + $(sym.typ.len-1), $args.len]) + else: + let start = genProc(c, sym) + + var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil) + let maxSlots = sym.offset + newSeq(tos.slots, maxSlots) + + # setup parameters: + if not isEmptyType(sym.typ.sons[0]) or sym.kind == skMacro: + putIntoReg(tos.slots[0], getNullValue(sym.typ.sons[0], sym.info)) + # XXX We could perform some type checking here. + for i in 1.. <sym.typ.len: + putIntoReg(tos.slots[i], args[i-1]) + + result = rawExecute(c, start, tos).regToNode + else: + localError(sym.info, + "NimScript: attempt to call non-routine: " & sym.name.s) + proc evalStmt*(c: PCtx, n: PNode) = let n = transformExpr(c.module, n) let start = genStmt(c, n) @@ -1399,13 +1453,17 @@ proc evalExpr*(c: PCtx, n: PNode): PNode = assert c.code[start].opcode != opcEof result = execute(c, start) +proc getGlobalValue*(c: PCtx; s: PSym): PNode = + internalAssert s.kind in {skLet, skVar} and sfGlobal in s.flags + result = c.globals.sons[s.position-1] + include vmops # for now we share the 'globals' environment. XXX Coming soon: An API for # storing&loading the 'globals' environment to get what a component system # requires. var - globalCtx: PCtx + globalCtx*: PCtx proc setupGlobalCtx(module: PSym) = if globalCtx.isNil: @@ -1442,6 +1500,8 @@ proc evalConstExprAux(module, prc: PSym, n: PNode, mode: TEvalMode): PNode = let n = transformExpr(module, n) setupGlobalCtx(module) var c = globalCtx + let oldMode = c.mode + defer: c.mode = oldMode c.mode = mode let start = genExpr(c, n, requiresValue = mode!=emStaticStmt) if c.code[start].opcode == opcEof: return emptyNode @@ -1465,12 +1525,20 @@ proc evalStaticStmt*(module: PSym, e: PNode, prc: PSym) = proc setupCompileTimeVar*(module: PSym, n: PNode) = discard evalConstExprAux(module, nil, n, emStaticStmt) -proc setupMacroParam(x: PNode): PNode = - result = x - if result.kind in {nkHiddenSubConv, nkHiddenStdConv}: result = result.sons[1] - result = canonValue(result) - result.flags.incl nfIsRef - result.typ = x.typ +proc setupMacroParam(x: PNode, typ: PType): TFullReg = + case typ.kind + of tyStatic: + putIntoReg(result, x) + of tyTypeDesc: + putIntoReg(result, x) + else: + result.kind = rkNode + var n = x + if n.kind in {nkHiddenSubConv, nkHiddenStdConv}: n = n.sons[1] + n = n.canonValue + n.flags.incl nfIsRef + n.typ = x.typ + result.node = n var evalMacroCounter: int @@ -1506,10 +1574,17 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode = # return value: tos.slots[0].kind = rkNode tos.slots[0].node = newNodeIT(nkEmpty, n.info, sym.typ.sons[0]) + # setup parameters: - for i in 1 .. < min(tos.slots.len, L): - tos.slots[i].kind = rkNode - tos.slots[i].node = setupMacroParam(n.sons[i]) + for i in 1.. <sym.typ.len: + tos.slots[i] = setupMacroParam(n.sons[i], sym.typ.sons[i]) + + if sfImmediate notin sym.flags: + let gp = sym.ast[genericParamsPos] + for i in 0 .. <gp.len: + let idx = sym.typ.len + i + tos.slots[idx] = setupMacroParam(n.sons[idx], gp[i].sym.typ) + # temporary storage: #for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty) result = rawExecute(c, start, tos).regToNode diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 047009f01..337e4ec8f 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -60,12 +60,13 @@ type opcAddFloat, opcSubFloat, opcMulFloat, opcDivFloat, opcShrInt, opcShlInt, opcBitandInt, opcBitorInt, opcBitxorInt, opcAddu, opcSubu, opcMulu, opcDivu, opcModu, opcEqInt, opcLeInt, opcLtInt, opcEqFloat, - opcLeFloat, opcLtFloat, opcLeu, opcLtu, opcEqRef, opcEqNimrodNode, opcXor, - opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt, + opcLeFloat, opcLtFloat, opcLeu, opcLtu, + opcEqRef, opcEqNimrodNode, opcSameNodeType, + opcXor, opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt, opcEqStr, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet, opcMulSet, opcPlusSet, opcMinusSet, opcSymdiffSet, opcConcatStr, opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq, - opcSwap, opcIsNil, opcOf, opcIs, + opcIsNil, opcOf, opcIs, opcSubStr, opcParseFloat, opcConv, opcCast, opcQuit, opcReset, opcNarrowS, opcNarrowU, @@ -101,6 +102,7 @@ type opcEqIdent, opcStrToIdent, opcIdentToStr, + opcGetImpl, opcEcho, opcIndCall, # dest = call regStart, n; where regStart = fn, arg1, ... diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 21ee4967b..2cc4a107b 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -import ast, types, msgs, osproc, streams, options, idents +import ast, types, msgs, osproc, streams, options, idents, securehash proc readOutput(p: Process): string = result = "" @@ -15,18 +15,39 @@ proc readOutput(p: Process): string = while not output.atEnd: result.add(output.readLine) result.add("\n") - result.setLen(result.len - "\n".len) + if result.len > 0: + result.setLen(result.len - "\n".len) discard p.waitForExit -proc opGorge*(cmd, input: string): string = - try: - var p = startProcess(cmd, options={poEvalCommand}) - if input.len != 0: - p.inputStream.write(input) - p.inputStream.close() - result = p.readOutput - except IOError, OSError: - result = "" +proc opGorge*(cmd, input, cache: string): string = + if cache.len > 0:# and optForceFullMake notin gGlobalOptions: + let h = secureHash(cmd & "\t" & input & "\t" & cache) + let filename = options.toGeneratedFile("gorge_" & $h, "txt") + var f: File + if open(f, filename): + result = f.readAll + f.close + return + var readSuccessful = false + try: + var p = startProcess(cmd, options={poEvalCommand, poStderrToStdout}) + if input.len != 0: + p.inputStream.write(input) + p.inputStream.close() + result = p.readOutput + readSuccessful = true + writeFile(filename, result) + except IOError, OSError: + if not readSuccessful: result = "" + else: + try: + var p = startProcess(cmd, options={poEvalCommand, poStderrToStdout}) + if input.len != 0: + p.inputStream.write(input) + p.inputStream.close() + result = p.readOutput + except IOError, OSError: + result = "" proc opSlurp*(file: string, info: TLineInfo, module: PSym): string = try: diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 0743a4502..97c6a5580 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -37,7 +37,7 @@ when hasFFI: import evalffi type - TGenFlag = enum gfNone, gfAddrOf + TGenFlag = enum gfAddrOf, gfFieldAccess TGenFlags = set[TGenFlag] proc debugInfo(info: TLineInfo): string = @@ -74,8 +74,9 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) = result.addf("\t$#\tr$#, L$#", ($opc).substr(3), x.regA, i+x.regBx-wordExcess) elif opc in {opcLdConst, opcAsgnConst}: - result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, - c.constants[x.regBx-wordExcess].renderTree) + let idx = x.regBx-wordExcess + result.addf("\t$#\tr$#, $# ($#)", ($opc).substr(3), x.regA, + c.constants[idx].renderTree, $idx) elif opc in {opcMarshalLoad, opcMarshalStore}: let y = c.code[i+1] result.addf("\t$#\tr$#, r$#, $#", ($opc).substr(3), x.regA, x.regB, @@ -169,8 +170,12 @@ proc getSlotKind(t: PType): TSlotKind = const HighRegisterPressure = 40 -proc getTemp(c: PCtx; typ: PType): TRegister = - let c = c.prc +proc bestEffort(c: PCtx): TLineInfo = + (if c.prc == nil: c.module.info else: c.prc.sym.info) + +proc getTemp(cc: PCtx; tt: PType): TRegister = + let typ = tt.skipTypesOrNil({tyStatic}) + let c = cc.prc # we prefer the same slot kind here for efficiency. Unfortunately for # discardable return types we may not know the desired type. This can happen # for e.g. mNAdd[Multiple]: @@ -181,13 +186,13 @@ proc getTemp(c: PCtx; typ: PType): TRegister = return TRegister(i) # if register pressure is high, we re-use more aggressively: - if c.maxSlots >= HighRegisterPressure: + if c.maxSlots >= HighRegisterPressure and false: for i in 0 .. c.maxSlots-1: if not c.slots[i].inUse: c.slots[i] = (inUse: true, kind: k) return TRegister(i) if c.maxSlots >= high(TRegister): - internalError("cannot generate code; too many registers required") + globalError(cc.bestEffort, "VM problem: too many registers required") result = TRegister(c.maxSlots) c.slots[c.maxSlots] = (inUse: true, kind: k) inc c.maxSlots @@ -196,9 +201,9 @@ proc freeTemp(c: PCtx; r: TRegister) = let c = c.prc if c.slots[r].kind in {slotSomeTemp..slotTempComplex}: c.slots[r].inUse = false -proc getTempRange(c: PCtx; n: int; kind: TSlotKind): TRegister = +proc getTempRange(cc: PCtx; n: int; kind: TSlotKind): TRegister = # if register pressure is high, we re-use more aggressively: - let c = c.prc + let c = cc.prc if c.maxSlots >= HighRegisterPressure or c.maxSlots+n >= high(TRegister): for i in 0 .. c.maxSlots-n: if not c.slots[i].inUse: @@ -209,7 +214,7 @@ proc getTempRange(c: PCtx; n: int; kind: TSlotKind): TRegister = for k in result .. result+n-1: c.slots[k] = (inUse: true, kind: kind) return if c.maxSlots+n >= high(TRegister): - internalError("cannot generate code; too many registers required") + globalError(cc.bestEffort, "VM problem: too many registers required") result = TRegister(c.maxSlots) inc c.maxSlots, n for k in result .. result+n-1: c.slots[k] = (inUse: true, kind: kind) @@ -305,7 +310,7 @@ proc genBreak(c: PCtx; n: PNode) = if c.prc.blocks[i].label == n.sons[0].sym: c.prc.blocks[i].fixups.add L1 return - internalError(n.info, "cannot find 'break' target") + globalError(n.info, errGenerated, "VM problem: cannot find 'break' target") else: c.prc.blocks[c.prc.blocks.high].fixups.add L1 @@ -393,7 +398,7 @@ proc genLiteral(c: PCtx; n: PNode): int = proc unused(n: PNode; x: TDest) {.inline.} = if x >= 0: #debug(n) - internalError(n.info, "not unused") + globalError(n.info, "not unused") proc genCase(c: PCtx; n: PNode; dest: var TDest) = # if (!expr1) goto L1; @@ -509,10 +514,10 @@ proc needsAsgnPatch(n: PNode): bool = proc genField(n: PNode): TRegister = if n.kind != nkSym or n.sym.kind != skField: - internalError(n.info, "no field symbol") + globalError(n.info, "no field symbol") let s = n.sym if s.position > high(result): - internalError(n.info, + globalError(n.info, "too large offset! cannot generate code for: " & s.name.s) result = s.position @@ -530,7 +535,7 @@ proc genIndex(c: PCtx; n: PNode; arr: PType): TRegister = proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = case le.kind of nkBracketExpr: - let dest = c.genx(le.sons[0], {gfAddrOf}) + let dest = c.genx(le.sons[0], {gfAddrOf, gfFieldAccess}) let idx = c.genIndex(le.sons[1], le.sons[0].typ) c.gABC(le, opcWrArr, dest, idx, value) c.freeTemp(dest) @@ -538,7 +543,7 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = of nkDotExpr, nkCheckedFieldExpr: # XXX field checks here let left = if le.kind == nkDotExpr: le else: le.sons[0] - let dest = c.genx(left.sons[0], {gfAddrOf}) + let dest = c.genx(left.sons[0], {gfAddrOf, gfFieldAccess}) let idx = genField(left.sons[1]) c.gABC(left, opcWrObj, dest, idx, value) c.freeTemp(dest) @@ -596,6 +601,18 @@ proc genBinaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = c.freeTemp(tmp) c.freeTemp(tmp2) +proc genBinaryABCD(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = + let + tmp = c.genx(n.sons[1]) + tmp2 = c.genx(n.sons[2]) + tmp3 = c.genx(n.sons[3]) + if dest < 0: dest = c.getTemp(n.typ) + c.gABC(n, opc, dest, tmp, tmp2) + c.gABC(n, opc, tmp3) + c.freeTemp(tmp) + c.freeTemp(tmp2) + c.freeTemp(tmp3) + proc genNarrow(c: PCtx; n: PNode; dest: TDest) = let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) # uint is uint64 in the VM, we we only need to mask the result for @@ -691,7 +708,7 @@ proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp) c.gABx(n, opc, 0, genType(c, n.typ)) - c.gABx(n, opc, 0, genType(c, arg.typ)) + c.gABx(n, opc, 0, genType(c, arg.typ.skipTypes({tyStatic}))) c.freeTemp(tmp) proc genCard(c: PCtx; n: PNode; dest: var TDest) = @@ -710,9 +727,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = if dest < 0: dest = c.getTemp(n.typ) c.gABI(n, opcSubImmInt, dest, tmp, 1) c.freeTemp(tmp) - of mPred, mSubI, mSubI64: + of mPred, mSubI: c.genAddSubInt(n, dest, opcSubInt) - of mSucc, mAddI, mAddI64: + of mSucc, mAddI: c.genAddSubInt(n, dest, opcAddInt) of mInc, mDec: unused(n, dest) @@ -759,28 +776,28 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.freeTemp(d) c.freeTemp(tmp) of mCard: genCard(c, n, dest) - of mMulI, mMulI64: genBinaryABCnarrow(c, n, dest, opcMulInt) - of mDivI, mDivI64: genBinaryABCnarrow(c, n, dest, opcDivInt) - of mModI, mModI64: genBinaryABCnarrow(c, n, dest, opcModInt) + of mMulI: genBinaryABCnarrow(c, n, dest, opcMulInt) + of mDivI: genBinaryABCnarrow(c, n, dest, opcDivInt) + of mModI: genBinaryABCnarrow(c, n, dest, opcModInt) of mAddF64: genBinaryABC(c, n, dest, opcAddFloat) of mSubF64: genBinaryABC(c, n, dest, opcSubFloat) of mMulF64: genBinaryABC(c, n, dest, opcMulFloat) of mDivF64: genBinaryABC(c, n, dest, opcDivFloat) - of mShrI, mShrI64: genBinaryABCnarrowU(c, n, dest, opcShrInt) - of mShlI, mShlI64: genBinaryABCnarrowU(c, n, dest, opcShlInt) - of mBitandI, mBitandI64: genBinaryABCnarrowU(c, n, dest, opcBitandInt) - of mBitorI, mBitorI64: genBinaryABCnarrowU(c, n, dest, opcBitorInt) - of mBitxorI, mBitxorI64: genBinaryABCnarrowU(c, n, dest, opcBitxorInt) + of mShrI: genBinaryABCnarrowU(c, n, dest, opcShrInt) + of mShlI: genBinaryABCnarrowU(c, n, dest, opcShlInt) + of mBitandI: genBinaryABCnarrowU(c, n, dest, opcBitandInt) + of mBitorI: genBinaryABCnarrowU(c, n, dest, opcBitorInt) + of mBitxorI: genBinaryABCnarrowU(c, n, dest, opcBitxorInt) of mAddU: genBinaryABCnarrowU(c, n, dest, opcAddu) of mSubU: genBinaryABCnarrowU(c, n, dest, opcSubu) of mMulU: genBinaryABCnarrowU(c, n, dest, opcMulu) of mDivU: genBinaryABCnarrowU(c, n, dest, opcDivu) of mModU: genBinaryABCnarrowU(c, n, dest, opcModu) - of mEqI, mEqI64, mEqB, mEqEnum, mEqCh: + of mEqI, mEqB, mEqEnum, mEqCh: genBinaryABC(c, n, dest, opcEqInt) - of mLeI, mLeI64, mLeEnum, mLeCh, mLeB: + of mLeI, mLeEnum, mLeCh, mLeB: genBinaryABC(c, n, dest, opcLeInt) - of mLtI, mLtI64, mLtEnum, mLtCh, mLtB: + of mLtI, mLtEnum, mLtCh, mLtB: genBinaryABC(c, n, dest, opcLtInt) of mEqF64: genBinaryABC(c, n, dest, opcEqFloat) of mLeF64: genBinaryABC(c, n, dest, opcLeFloat) @@ -796,7 +813,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = genNarrow(c, n, dest) of mUnaryMinusF64: genUnaryABC(c, n, dest, opcUnaryMinusFloat) of mUnaryPlusI, mUnaryPlusF64: gen(c, n.sons[1], dest) - of mBitnotI, mBitnotI64: + of mBitnotI: genUnaryABC(c, n, dest, opcBitnotInt) genNarrowU(c, n, dest) of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, @@ -831,12 +848,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.freeTemp(tmp) of mSwap: unused(n, dest) - var - d1 = c.genx(n.sons[1]) - d2 = c.genx(n.sons[2]) - c.gABC(n, opcSwap, d1, d2) - c.genAsgnPatch(n.sons[1], d1) - c.genAsgnPatch(n.sons[2], d2) + c.gen(lowerSwap(n, if c.prc == nil: c.module else: c.prc.sym)) of mIsNil: genUnaryABC(c, n, dest, opcIsNil) of mCopyStr: if dest < 0: dest = c.getTemp(n.typ) @@ -934,8 +946,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.gABC(n, opcTypeTrait, dest, tmp) c.freeTemp(tmp) of mSlurp: genUnaryABC(c, n, dest, opcSlurp) - of mStaticExec: genBinaryABC(c, n, dest, opcGorge) + of mStaticExec: genBinaryABCD(c, n, dest, opcGorge) of mNLen: genUnaryABI(c, n, dest, opcLenSeq) + of mGetImpl: genUnaryABC(c, n, dest, opcGetImpl) of mNChild: genBinaryABC(c, n, dest, opcNChild) of mNSetChild, mNDel: unused(n, dest) @@ -988,11 +1001,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = if dest < 0: dest = c.getTemp(n.typ) c.gABx(n, opcNBindSym, dest, idx) else: - internalError(n.info, "invalid bindSym usage") + localError(n.info, "invalid bindSym usage") of mStrToIdent: genUnaryABC(c, n, dest, opcStrToIdent) of mIdentToStr: genUnaryABC(c, n, dest, opcIdentToStr) of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent) of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode) + of mSameNodeType: genBinaryABC(c, n, dest, opcSameNodeType) of mNLineInfo: genUnaryABC(c, n, dest, opcNLineInfo) of mNHint: unused(n, dest) @@ -1013,7 +1027,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.gABC(n, opcCallSite, dest) of mNGenSym: genBinaryABC(c, n, dest, opcGenSym) of mMinI, mMaxI, mAbsF64, mMinF64, mMaxF64, mAbsI, - mAbsI64, mDotDot: + mDotDot: c.genCall(n, dest) of mExpandToAst: if n.len != 2: @@ -1030,7 +1044,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = globalError(n.info, "expandToAst requires a call expression") else: # mGCref, mGCunref, - internalError(n.info, "cannot generate code for: " & $m) + globalError(n.info, "cannot generate code for: " & $m) proc genMarshalLoad(c: PCtx, n: PNode, dest: var TDest) = ## Signature: proc to*[T](data: string): T @@ -1062,6 +1076,7 @@ const tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64} proc fitsRegister*(t: PType): bool = + assert t != nil t.skipTypes(abstractInst-{tyTypeDesc}).kind in { tyRange, tyEnum, tyBool, tyInt..tyUInt64, tyChar} @@ -1076,10 +1091,32 @@ proc requiresCopy(n: PNode): bool = proc unneededIndirection(n: PNode): bool = n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind == tyRef +proc canElimAddr(n: PNode): PNode = + case n.sons[0].kind + of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: + var m = n.sons[0].sons[0] + if m.kind in {nkDerefExpr, nkHiddenDeref}: + # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) + result = copyNode(n.sons[0]) + result.add m.sons[0] + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + var m = n.sons[0].sons[1] + if m.kind in {nkDerefExpr, nkHiddenDeref}: + # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) + result = copyNode(n.sons[0]) + result.add m.sons[0] + else: + if n.sons[0].kind in {nkDerefExpr, nkHiddenDeref}: + # addr ( deref ( x )) --> x + result = n.sons[0].sons[0] + proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; flags: TGenFlags) = # a nop for certain types let isAddr = opc in {opcAddrNode, opcAddrReg} + if isAddr and (let m = canElimAddr(n); m != nil): + gen(c, m, dest, flags) + return let newflags = if isAddr: flags+{gfAddrOf} else: flags # consider: # proc foo(f: var ref int) = @@ -1092,6 +1129,8 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; # nkAddr we must not use 'unneededIndirection', but for deref we use it. if not isAddr and unneededIndirection(n.sons[0]): gen(c, n.sons[0], dest, newflags) + if gfAddrOf notin flags and fitsRegister(n.typ): + c.gABC(n, opcNodeToReg, dest, dest) elif isAddr and isGlobal(n.sons[0]): gen(c, n.sons[0], dest, flags+{gfAddrOf}) else: @@ -1099,6 +1138,7 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; if dest < 0: dest = c.getTemp(n.typ) if not isAddr: gABC(c, n, opc, dest, tmp) + assert n.typ != nil if gfAddrOf notin flags and fitsRegister(n.typ): c.gABC(n, opcNodeToReg, dest, dest) elif c.prc.slots[tmp].kind >= slotTempUnknown: @@ -1132,7 +1172,7 @@ proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = opc proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) = let tmp = c.genx(ri) assert dest >= 0 - gABC(c, ri, whichAsgnOpc(ri), dest, tmp) + gABC(c, ri, whichAsgnOpc(ri), dest, tmp, 1-ord(requiresCopy)) c.freeTemp(tmp) proc setSlot(c: PCtx; v: PSym) = @@ -1140,7 +1180,7 @@ proc setSlot(c: PCtx; v: PSym) = if v.position == 0: if c.prc.maxSlots == 0: c.prc.maxSlots = 1 if c.prc.maxSlots >= high(TRegister): - internalError(v.info, "cannot generate code; too many registers required") + globalError(v.info, "cannot generate code; too many registers required") v.position = c.prc.maxSlots c.prc.slots[v.position] = (inUse: true, kind: if v.kind == skLet: slotFixedLet else: slotFixedVar) @@ -1166,7 +1206,10 @@ proc checkCanEval(c: PCtx; n: PNode) = let s = n.sym if {sfCompileTime, sfGlobal} <= s.flags: return if s.kind in {skVar, skTemp, skLet, skParam, skResult} and - not s.isOwnedBy(c.prc.sym) and s.owner != c.module: + not s.isOwnedBy(c.prc.sym) and s.owner != c.module and c.mode != emRepl: + cannotEval(n) + elif s.kind in {skProc, skConverter, skMethod, + skIterator, skClosureIterator} and sfForward in s.flags: cannotEval(n) proc isTemp(c: PCtx; dest: TDest): bool = @@ -1183,9 +1226,10 @@ proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode; # opcLdObj et al really means "load address". We sometimes have to create a # copy in order to not introduce false aliasing: # mylocal = a.b # needs a copy of the data! + assert n.typ != nil if needsAdditionalCopy(n): var cc = c.getTemp(n.typ) - c.gABC(n, whichAsgnOpc(n), cc, value) + c.gABC(n, whichAsgnOpc(n), cc, value, 0) c.gABC(n, opc, dest, idx, cc) c.freeTemp(cc) else: @@ -1194,7 +1238,7 @@ proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode; proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = case le.kind of nkBracketExpr: - let dest = c.genx(le.sons[0], {gfAddrOf}) + let dest = c.genx(le.sons[0], {gfAddrOf, gfFieldAccess}) let idx = c.genIndex(le.sons[1], le.sons[0].typ) let tmp = c.genx(ri) if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in { @@ -1206,7 +1250,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = of nkDotExpr, nkCheckedFieldExpr: # XXX field checks here let left = if le.kind == nkDotExpr: le else: le.sons[0] - let dest = c.genx(left.sons[0], {gfAddrOf}) + let dest = c.genx(left.sons[0], {gfAddrOf, gfFieldAccess}) let idx = genField(left.sons[1]) let tmp = c.genx(ri) c.preventFalseAlias(left, opcWrObj, dest, idx, tmp) @@ -1230,10 +1274,11 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = internalAssert s.position > 0 or (s.position == 0 and s.kind in {skParam,skResult}) var dest: TRegister = s.position + ord(s.kind == skParam) + assert le.typ != nil if needsAdditionalCopy(le) and s.kind in {skResult, skVar, skParam}: var cc = c.getTemp(le.typ) gen(c, ri, cc) - c.gABC(le, whichAsgnOpc(le), dest, cc) + c.gABC(le, whichAsgnOpc(le), dest, cc, 1) c.freeTemp(cc) else: gen(c, ri, dest) @@ -1292,12 +1337,13 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = if sfImportc in s.flags: c.importcSym(n.info, s) else: genGlobalInit(c, n, s) if dest < 0: dest = c.getTemp(n.typ) + assert s.typ != nil if gfAddrOf notin flags and fitsRegister(s.typ): var cc = c.getTemp(n.typ) c.gABx(n, opcLdGlobal, cc, s.position) c.gABC(n, opcNodeToReg, dest, cc) c.freeTemp(cc) - elif gfAddrOf in flags: + elif {gfAddrOf, gfFieldAccess} * flags == {gfAddrOf}: c.gABx(n, opcLdGlobalAddr, dest, s.position) else: c.gABx(n, opcLdGlobal, dest, s.position) @@ -1353,9 +1399,11 @@ proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = genObjAccess(c, n.sons[0], dest, flags) proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = - if n.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in { - tyString, tyCString}: + let arrayType = n.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind + if arrayType in {tyString, tyCString}: genArrAccess2(c, n, dest, opcLdStrIdx, {}) + elif arrayType == tyTypeDesc: + c.genTypeLit(n.typ, dest) else: genArrAccess2(c, n, dest, opcLdArr, flags) @@ -1368,8 +1416,11 @@ proc getNullValueAux(obj: PNode, result: PNode) = for i in countup(1, sonsLen(obj) - 1): getNullValueAux(lastSon(obj.sons[i]), result) of nkSym: - addSon(result, getNullValue(obj.sym.typ, result.info)) - else: internalError(result.info, "getNullValueAux") + let field = newNodeI(nkExprColonExpr, result.info) + field.add(obj) + field.add(getNullValue(obj.sym.typ, result.info)) + addSon(result, field) + else: globalError(result.info, "cannot create null element for: " & $obj) proc getNullValue(typ: PType, info: TLineInfo): PNode = var t = skipTypes(typ, abstractRange-{tyTypeDesc}) @@ -1394,7 +1445,8 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = result.add(newNodeIT(nkNilLit, info, t)) result.add(newNodeIT(nkNilLit, info, t)) of tyObject: - result = newNodeIT(nkPar, info, t) + result = newNodeIT(nkObjConstr, info, t) + result.add(newNodeIT(nkEmpty, info, t)) getNullValueAux(t.n, result) # initialize inherited fields: var base = t.sons[0] @@ -1411,9 +1463,11 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = addSon(result, getNullValue(t.sons[i], info)) of tySet: result = newNodeIT(nkCurly, info, t) - else: internalError(info, "getNullValue: " & $t.kind) + else: + globalError(info, "cannot create null element for: " & $t.kind) proc ldNullOpcode(t: PType): TOpcode = + assert t != nil if fitsRegister(t): opcLdNullReg else: opcLdNull proc genVarSection(c: PCtx; n: PNode) = @@ -1441,7 +1495,7 @@ proc genVarSection(c: PCtx; n: PNode) = if a.sons[2].kind != nkEmpty: let tmp = c.genx(a.sons[0], {gfAddrOf}) let val = c.genx(a.sons[2]) - c.preventFalseAlias(a, opcWrDeref, tmp, 0, val) + c.preventFalseAlias(a.sons[2], opcWrDeref, tmp, 0, val) c.freeTemp(val) c.freeTemp(tmp) else: @@ -1449,13 +1503,15 @@ proc genVarSection(c: PCtx; n: PNode) = if a.sons[2].kind == nkEmpty: c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ)) else: + assert s.typ != nil if not fitsRegister(s.typ): c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ)) let le = a.sons[0] + assert le.typ != nil if not fitsRegister(le.typ) and s.kind in {skResult, skVar, skParam}: var cc = c.getTemp(le.typ) gen(c, a.sons[2], cc) - c.gABC(le, whichAsgnOpc(le), s.position.TRegister, cc) + c.gABC(le, whichAsgnOpc(le), s.position.TRegister, cc, 1) c.freeTemp(cc) else: gen(c, a.sons[2], s.position.TRegister) @@ -1522,7 +1578,7 @@ proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) = dest, idx, tmp) c.freeTemp(tmp) else: - internalError(n.info, "invalid object constructor") + globalError(n.info, "invalid object constructor") proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) = if dest < 0: dest = c.getTemp(n.typ) @@ -1596,8 +1652,13 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = c.gABx(n, opcLdConst, dest, lit) of skType: genTypeLit(c, s.typ, dest) + of skGenericParam: + if c.prc.sym.kind == skMacro: + genRdVar(c, n, dest, flags) + else: + internalError(n.info, "cannot generate code for: " & s.name.s) else: - internalError(n.info, "cannot generate code for: " & s.name.s) + globalError(n.info, errGenerated, "cannot generate code for: " & s.name.s) of nkCallKinds: if n.sons[0].kind == nkSym: let s = n.sons[0].sym @@ -1631,7 +1692,10 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = of nkBracketExpr: genArrAccess(c, n, dest, flags) of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcLdDeref, flags) of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddrNode, flags) - of nkWhenStmt, nkIfStmt, nkIfExpr: genIf(c, n, dest) + of nkIfStmt, nkIfExpr: genIf(c, n, dest) + of nkWhenStmt: + # This is "when nimvm" node. Chose the first branch. + gen(c, n.sons[0].sons[1], dest) of nkCaseStmt: genCase(c, n, dest) of nkWhileStmt: unused(n, dest) @@ -1682,7 +1746,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = c.freeTemp(tmp1) c.freeTemp(tmp2) if dest >= 0: - gABC(c, n, whichAsgnOpc(n), dest, tmp0) + gABC(c, n, whichAsgnOpc(n), dest, tmp0, 1) c.freeTemp(tmp0) else: dest = tmp0 @@ -1701,7 +1765,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = else: globalError(n.info, errGenerated, "VM is not allowed to 'cast'") else: - internalError n.info, "cannot generate VM code for " & n.renderTree + globalError(n.info, errGenerated, "cannot generate VM code for " & $n) proc removeLastEof(c: PCtx) = let last = c.code.len-1 @@ -1717,7 +1781,8 @@ proc genStmt*(c: PCtx; n: PNode): int = var d: TDest = -1 c.gen(n, d) c.gABC(n, opcEof) - if d >= 0: internalError(n.info, "some destination set") + if d >= 0: + globalError(n.info, errGenerated, "VM problem: dest register is set") proc genExpr*(c: PCtx; n: PNode, requiresValue = true): int = c.removeLastEof @@ -1725,10 +1790,14 @@ proc genExpr*(c: PCtx; n: PNode, requiresValue = true): int = var d: TDest = -1 c.gen(n, d) if d < 0: - if requiresValue: internalError(n.info, "no destination set") + if requiresValue: + globalError(n.info, errGenerated, "VM problem: dest register is not set") d = 0 c.gABC(n, opcEof, d) + #echo renderTree(n) + #c.echoCode(result) + proc genParams(c: PCtx; params: PNode) = # res.sym.position is already 0 c.prc.slots[0] = (inUse: true, kind: slotFixedVar) @@ -1744,6 +1813,14 @@ proc finalJumpTarget(c: PCtx; pc, diff: int) = c.code[pc] = ((oldInstr.uint32 and 0xffff'u32).uint32 or uint32(diff+wordExcess) shl 16'u32).TInstr +proc genGenericParams(c: PCtx; gp: PNode) = + var base = c.prc.maxSlots + for i in 0.. <gp.len: + var param = gp.sons[i].sym + param.position = base + i # XXX: fix this earlier; make it consistent with templates + c.prc.slots[base + i] = (inUse: true, kind: slotFixedLet) + c.prc.maxSlots = base + gp.len + proc optimizeJumps(c: PCtx; start: int) = const maxIterations = 10 for i in start .. <c.code.len: @@ -1808,6 +1885,13 @@ proc genProc(c: PCtx; s: PSym): int = c.prc = p # iterate over the parameters and allocate space for them: genParams(c, s.typ.n) + + # allocate additional space for any generically bound parameters + if s.kind == skMacro and + sfImmediate notin s.flags and + s.ast[genericParamsPos].kind != nkEmpty: + genGenericParams(c, s.ast[genericParamsPos]) + if tfCapturesEnv in s.typ.flags: #let env = s.ast.sons[paramsPos].lastSon.sym #assert env.position == 2 diff --git a/compiler/vmhooks.nim b/compiler/vmhooks.nim index 6ec5f6044..576b0565f 100644 --- a/compiler/vmhooks.nim +++ b/compiler/vmhooks.nim @@ -17,7 +17,7 @@ template setX(k, field) {.immediate, dirty.} = proc setResult*(a: VmArgs; v: BiggestInt) = setX(rkInt, intVal) proc setResult*(a: VmArgs; v: BiggestFloat) = setX(rkFloat, floatVal) -proc setResult*(a: VmArgs; v: bool) = +proc setResult*(a: VmArgs; v: bool) = let v = v.ord setX(rkInt, intVal) @@ -30,6 +30,24 @@ proc setResult*(a: VmArgs; v: string) = s[a.ra].node = newNode(nkStrLit) s[a.ra].node.strVal = v +proc setResult*(a: VmArgs; n: PNode) = + var s: seq[TFullReg] + move(s, cast[seq[TFullReg]](a.slots)) + if s[a.ra].kind != rkNode: + myreset(s[a.ra]) + s[a.ra].kind = rkNode + s[a.ra].node = n + +proc setResult*(a: VmArgs; v: seq[string]) = + var s: seq[TFullReg] + move(s, cast[seq[TFullReg]](a.slots)) + if s[a.ra].kind != rkNode: + myreset(s[a.ra]) + s[a.ra].kind = rkNode + var n = newNode(nkBracket) + for x in v: n.add newStrNode(nkStrLit, x) + s[a.ra].node = n + template getX(k, field) {.immediate, dirty.} = doAssert i < a.rc-1 let s = cast[seq[TFullReg]](a.slots) diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim index 293d0d949..c08c5d249 100644 --- a/compiler/vmmarshal.nim +++ b/compiler/vmmarshal.nim @@ -36,8 +36,8 @@ proc getField(n: PNode; position: int): PSym = proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet) proc storeObj(s: var string; typ: PType; x: PNode; stored: var IntSet) = - internalAssert x.kind in {nkObjConstr, nkPar} - let start = ord(x.kind == nkObjConstr) + internalAssert x.kind == nkObjConstr + let start = 1 for i in countup(start, sonsLen(x) - 1): if i > start: s.add(", ") var it = x.sons[i] @@ -205,18 +205,23 @@ proc loadAny(p: var JsonParser, t: PType, of tyObject: if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object") next(p) - result = newNode(nkPar) - result.sons = @[] + result = newNode(nkObjConstr) + result.sons = @[newNode(nkEmpty)] while p.kind != jsonObjectEnd and p.kind != jsonEof: if p.kind != jsonString: raiseParseErr(p, "string expected for a field name") - let field = lookupInRecord(t.n, getIdent(p.str)) + let ident = getIdent(p.str) + let field = lookupInRecord(t.n, ident) if field.isNil: raiseParseErr(p, "unknown field for object of type " & typeToString(t)) next(p) - if field.position >= result.sons.len: - setLen(result.sons, field.position+1) - result.sons[field.position] = loadAny(p, field.typ, tab) + let pos = field.position + 1 + if pos >= result.sons.len: + setLen(result.sons, pos + 1) + let fieldNode = newNode(nkExprColonExpr) + fieldNode.addSon(newSymNode(newSym(skField, ident, nil, unknownLineInfo()))) + fieldNode.addSon(loadAny(p, field.typ, tab)) + result.sons[pos] = fieldNode if p.kind == jsonObjectEnd: next(p) else: raiseParseErr(p, "'}' end of object expected") of tySet: @@ -234,7 +239,7 @@ proc loadAny(p: var JsonParser, t: PType, result = newNode(nkNilLit) next(p) of jsonInt: - result = tab[p.getInt] + result = tab.getOrDefault(p.getInt) if result.isNil: raiseParseErr(p, "cannot load object with address " & $p.getInt) next(p) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 1023d4783..e1a0dfef8 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -46,7 +46,7 @@ template wrap2svoid(op) {.immediate, dirty.} = proc getCurrentExceptionMsgWrapper(a: VmArgs) {.nimcall.} = setResult(a, if a.currentException.isNil: "" - else: a.currentException.sons[2].strVal) + else: a.currentException.sons[3].skipColon.strVal) proc registerAdditionalOps*(c: PCtx) = wrap1f(sqrt) diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 63fd995c4..0a0534118 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -42,10 +42,10 @@ type wImmediate, wConstructor, wDestructor, wDelegator, wOverride, wImportCpp, wImportObjC, wImportCompilerProc, - wImportc, wExportc, wIncompleteStruct, wRequiresInit, + wImportc, wExportc, wExportNims, wIncompleteStruct, wRequiresInit, wAlign, wNodecl, wPure, wSideeffect, wHeader, wNosideeffect, wGcSafe, wNoreturn, wMerge, wLib, wDynlib, - wCompilerproc, wProcVar, + wCompilerproc, wProcVar, wBase, wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef, wLinedir, wStacktrace, wLinetrace, wLink, wCompile, wLinksys, wDeprecated, wVarargs, wCallconv, wBreakpoint, wDebugger, @@ -55,7 +55,7 @@ type wFloatchecks, wNanChecks, wInfChecks, wAssertions, wPatterns, wWarnings, wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags, - wDeadCodeElim, wSafecode, wNoForward, + wDeadCodeElim, wSafecode, wNoForward, wNoRewrite, wPragma, wCompileTime, wNoInit, wPassc, wPassl, wBorrow, wDiscardable, @@ -82,6 +82,7 @@ type wStdIn, wStdOut, wStdErr, wInOut, wByCopy, wByRef, wOneWay, + wBitsize, TSpecialWords* = set[TSpecialWord] @@ -125,10 +126,12 @@ const "immediate", "constructor", "destructor", "delegator", "override", "importcpp", "importobjc", - "importcompilerproc", "importc", "exportc", "incompletestruct", + "importcompilerproc", "importc", "exportc", "exportnims", + "incompletestruct", "requiresinit", "align", "nodecl", "pure", "sideeffect", "header", "nosideeffect", "gcsafe", "noreturn", "merge", "lib", "dynlib", - "compilerproc", "procvar", "fatal", "error", "warning", "hint", "line", + "compilerproc", "procvar", "base", + "fatal", "error", "warning", "hint", "line", "push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace", "link", "compile", "linksys", "deprecated", "varargs", "callconv", "breakpoint", "debugger", "nimcall", "stdcall", @@ -139,7 +142,7 @@ const "assertions", "patterns", "warnings", "hints", "optimization", "raises", "writes", "reads", "size", "effects", "tags", - "deadcodeelim", "safecode", "noforward", + "deadcodeelim", "safecode", "noforward", "norewrite", "pragma", "compiletime", "noinit", "passc", "passl", "borrow", "discardable", "fieldchecks", @@ -167,6 +170,7 @@ const "stdin", "stdout", "stderr", "inout", "bycopy", "byref", "oneway", + "bitsize", ] proc findStr*(a: openArray[string], s: string): int = diff --git a/compiler/writetracking.nim b/compiler/writetracking.nim new file mode 100644 index 000000000..141b496c1 --- /dev/null +++ b/compiler/writetracking.nim @@ -0,0 +1,272 @@ +# +# +# The Nim Compiler +# (c) Copyright 2015 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements the write tracking analysis. Read my block post for +## a basic description of the algorithm and ideas. +## The algorithm operates in 2 phases: +## +## * Collecting information about assignments (and pass-by-var calls). +## * Computing an aliasing relation based on the assignments. This relation +## is then used to compute the 'writes' and 'escapes' effects. + +import intsets, idents, ast, astalgo, trees, renderer, msgs, types + +const + debug = false + +type + AssignToResult = enum + asgnNil, # 'nil' is fine + asgnNew, # 'new(result)' + asgnOther # result = fooBar # not a 'new' --> 'result' might not 'new' + NewLocation = enum + newNone, + newLit, + newCall + RootInfo = enum + rootIsResultOrParam, + rootIsHeapAccess, + rootIsSym, + markAsWrittenTo, + markAsEscaping + + Assignment = object # \ + # Note that the transitive closures MUST be computed in + # phase 2 of the algorithm. + dest, src: seq[ptr TSym] # we use 'ptr' here to save RC ops and GC cycles + destNoTc, srcNoTc: int # length of 'dest', 'src' without the + # transitive closure + destInfo: set[RootInfo] + info: TLineInfo + + W = object # WriteTrackContext + owner: PSym + returnsNew: AssignToResult # assignments to 'result' + assignments: seq[Assignment] # list of all assignments in this proc + +proc allRoots(n: PNode; result: var seq[ptr TSym]; info: var set[RootInfo]) = + case n.kind + of nkSym: + if n.sym.kind in {skParam, skVar, skTemp, skLet, skResult, skForVar}: + if n.sym.kind in {skResult, skParam}: incl(info, rootIsResultOrParam) + result.add(cast[ptr TSym](n.sym)) + of nkHiddenDeref, nkDerefExpr: + incl(info, rootIsHeapAccess) + allRoots(n.sons[0], result, info) + of nkDotExpr, nkBracketExpr, nkCheckedFieldExpr, + nkHiddenAddr, nkObjUpConv, nkObjDownConv: + allRoots(n.sons[0], result, info) + of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkConv, + nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkOfBranch, + nkElifBranch, nkElse, nkExceptBranch, nkFinally, nkCast: + allRoots(n.lastSon, result, info) + of nkCallKinds: + if getMagic(n) == mSlice: + allRoots(n.sons[1], result, info) + else: + # we do significantly better here by using the available escape + # information: + if n.sons[0].typ.isNil: return + var typ = n.sons[0].typ + if typ != nil: + typ = skipTypes(typ, abstractInst) + if typ.kind != tyProc: typ = nil + else: assert(sonsLen(typ) == sonsLen(typ.n)) + + for i in 1 ..< n.len: + let it = n.sons[i] + if typ != nil and i < sonsLen(typ): + assert(typ.n.sons[i].kind == nkSym) + let paramType = typ.n.sons[i] + if paramType.typ.isCompileTimeOnly: continue + if sfEscapes in paramType.sym.flags or paramType.typ.kind == tyVar: + allRoots(it, result, info) + else: + allRoots(it, result, info) + else: + for i in 0..<n.safeLen: + allRoots(n.sons[i], result, info) + +proc addAsgn(a: var Assignment; dest, src: PNode; destInfo: set[RootInfo]) = + a.dest = @[] + a.src = @[] + a.destInfo = destInfo + allRoots(dest, a.dest, a.destInfo) + if dest.kind == nkSym: incl(a.destInfo, rootIsSym) + if src != nil: + var dummy: set[RootInfo] + allRoots(src, a.src, dummy) + a.destNoTc = a.dest.len + a.srcNoTc = a.src.len + a.info = dest.info + #echo "ADDING ", dest.info, " ", a.destInfo + +proc srcHasSym(a: Assignment; x: ptr TSym): bool = + for i in 0 ..< a.srcNoTc: + if a.src[i] == x: return true + +proc returnsNewExpr*(n: PNode): NewLocation = + case n.kind + of nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, + nkFloatLit..nkFloat64Lit, nkNilLit: + result = newLit + of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, + nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkOfBranch, + nkElifBranch, nkElse, nkExceptBranch, nkFinally, nkCast: + result = returnsNewExpr(n.lastSon) + of nkCurly, nkBracket, nkPar, nkObjConstr, nkClosure, + nkIfExpr, nkIfStmt, nkWhenStmt, nkCaseStmt, nkTryStmt: + result = newLit + for i in ord(n.kind == nkObjConstr) .. <n.len: + let x = returnsNewExpr(n.sons[i]) + case x + of newNone: return newNone + of newLit: discard + of newCall: result = newCall + of nkCallKinds: + if n.sons[0].typ != nil and tfReturnsNew in n.sons[0].typ.flags: + result = newCall + else: + result = newNone + +proc deps(w: var W; dest, src: PNode; destInfo: set[RootInfo]) = + # let x = (localA, localB) + # compute 'returnsNew' property: + let retNew = if src.isNil: newNone else: returnsNewExpr(src) + if dest.kind == nkSym and dest.sym.kind == skResult: + if retNew != newNone: + if w.returnsNew != asgnOther: w.returnsNew = asgnNew + else: + w.returnsNew = asgnOther + # mark the dependency, but + # rule out obviously innocent assignments like 'somebool = true' + if dest.kind == nkSym and retNew == newLit: discard + else: + let L = w.assignments.len + w.assignments.setLen(L+1) + addAsgn(w.assignments[L], dest, src, destInfo) + +proc depsArgs(w: var W; n: PNode) = + if n.sons[0].typ.isNil: return + var typ = skipTypes(n.sons[0].typ, abstractInst) + if typ.kind != tyProc: return + # echo n.info, " ", n, " ", w.owner.name.s, " ", typeToString(typ) + assert(sonsLen(typ) == sonsLen(typ.n)) + for i in 1 ..< n.len: + let it = n.sons[i] + if i < sonsLen(typ): + assert(typ.n.sons[i].kind == nkSym) + let paramType = typ.n.sons[i] + if paramType.typ.isCompileTimeOnly: continue + var destInfo: set[RootInfo] = {} + if sfWrittenTo in paramType.sym.flags or paramType.typ.kind == tyVar: + # p(f(x, y), X, g(h, z)) + destInfo.incl markAsWrittenTo + if sfEscapes in paramType.sym.flags: + destInfo.incl markAsEscaping + if destInfo != {}: + deps(w, it, nil, destInfo) + +proc deps(w: var W; n: PNode) = + case n.kind + of nkLetSection, nkVarSection: + for child in n: + let last = lastSon(child) + if last.kind == nkEmpty: continue + if child.kind == nkVarTuple and last.kind == nkPar: + internalAssert child.len-2 == last.len + for i in 0 .. child.len-3: + deps(w, child.sons[i], last.sons[i], {}) + else: + for i in 0 .. child.len-3: + deps(w, child.sons[i], last, {}) + of nkAsgn, nkFastAsgn: + deps(w, n.sons[0], n.sons[1], {}) + else: + for i in 0 ..< n.safeLen: + deps(w, n.sons[i]) + if n.kind in nkCallKinds: + if getMagic(n) in {mNew, mNewFinalize, mNewSeq}: + # may not look like an assignment, but it is: + deps(w, n.sons[1], newNodeIT(nkObjConstr, n.info, n.sons[1].typ), {}) + else: + depsArgs(w, n) + +proc possibleAliases(w: var W; result: var seq[ptr TSym]) = + # this is an expensive fixpoint iteration. We could speed up this analysis + # by a smarter data-structure but we wait until profiling shows us it's + # expensive. Usually 'w.assignments' is small enough. + var alreadySeen = initIntSet() + template addNoDup(x) = + if not alreadySeen.containsOrIncl(x.id): result.add x + for x in result: alreadySeen.incl x.id + + var todo = 0 + while todo < result.len: + let x = result[todo] + inc todo + for a in mitems(w.assignments): + #if a.srcHasSym(x): + # # y = f(..., x, ...) + # for i in 0 ..< a.destNoTc: addNoDup a.dest[i] + if a.destNoTc > 0 and a.dest[0] == x and rootIsSym in a.destInfo: + # x = f(..., y, ....) + for i in 0 ..< a.srcNoTc: addNoDup a.src[i] + +proc markWriteOrEscape(w: var W) = + ## Both 'writes' and 'escapes' effects ultimately only care + ## about *parameters*. + ## However, due to aliasing, even locals that might not look as parameters + ## have to count as parameters if they can alias a parameter: + ## + ## .. code-block:: nim + ## proc modifies(n: Node) {.writes: [n].} = + ## let x = n + ## x.data = "abc" + ## + ## We call a symbol *parameter-like* if it is a parameter or can alias a + ## parameter. + ## Let ``p``, ``q`` be *parameter-like* and ``x``, ``y`` be general + ## expressions. + ## + ## A write then looks like ``p[] = x``. + ## An escape looks like ``p[] = q`` or more generally + ## like ``p[] = f(q)`` where ``f`` can forward ``q``. + for a in mitems(w.assignments): + if a.destInfo != {}: + possibleAliases(w, a.dest) + + if {rootIsHeapAccess, markAsWrittenTo} * a.destInfo != {}: + for p in a.dest: + if p.kind == skParam and p.owner == w.owner: + incl(p.flags, sfWrittenTo) + + if {rootIsResultOrParam, rootIsHeapAccess, markAsEscaping}*a.destInfo != {}: + var destIsParam = false + for p in a.dest: + if p.kind in {skResult, skParam} and p.owner == w.owner: + destIsParam = true + break + if destIsParam: + possibleAliases(w, a.src) + for p in a.src: + if p.kind == skParam and p.owner == w.owner: + incl(p.flags, sfEscapes) + +proc trackWrites*(owner: PSym; body: PNode) = + var w: W + w.owner = owner + w.assignments = @[] + # Phase 1: Collect and preprocess any assignments in the proc body: + deps(w, body) + # Phase 2: Compute the 'writes' and 'escapes' effects: + markWriteOrEscape(w) + if w.returnsNew != asgnOther and not isEmptyType(owner.typ.sons[0]) and + containsGarbageCollectedRef(owner.typ.sons[0]): + incl(owner.typ.flags, tfReturnsNew) |