diff --git a/.gitignore b/.gitignore index d804fb8f5..1971a23cb 100644 --- a/.gitignore +++ b/.gitignore @@ -41,4 +41,4 @@ xcuserdata/ /testresults.html /testresults.json testament.db -/csources/ +/csources diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..f68375e93 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,35 @@ +sudo: false +language: c +os: + - linux +addons: + apt: + packages: + - libcurl4-openssl-dev + - libsdl1.2-dev + - libgc-dev +before_script: + - set -e + - wget http://flatassembler.net/fasm-1.71.39.tgz + - tar xvf fasm-1.71.39.tgz + - git clone --depth 1 https://github.com/nim-lang/csources.git + - cd csources + - sh build.sh + - cd .. + - sed -i -e 's,cc = gcc,cc = clang,' config/nim.cfg + - export PATH=$(pwd)/bin:$(pwd)/fasm:$PATH +script: + - nim c koch + - ./koch boot + - ./koch boot -d:release + - nim e install_nimble.nims + - nimble update + - nimble install zip + - nimble install opengl + - nimble install sdl1 + - nimble install jester + - nimble install niminst + - nim c --taintMode:on tests/testament/tester + - tests/testament/tester --pedantic all + - ./koch csource + - ./koch xz diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..2b1eab5d0 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,36 @@ +clone_depth: 5 + +artifacts: + - path: bin\nim.exe + +platform: + - x64 + +before_build: + - git log -1 + - C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S zlib-devel" + - appveyor DownloadFile http://nim-lang.org/download/dlls.zip + - 7z e dlls.zip -odlls + - del dlls\libcurl.dll + - appveyor DownloadFile http://flatassembler.net/fasmw17139.zip + - 7z e fasmw17139.zip -obin fasm.exe + +build_script: + - SET PATH=C:\msys64\mingw64\bin;dlls;bin;%PATH% + - gcc -v + - git clone -q --depth 1 https://github.com/nim-lang/csources + - cd csources + - build64.bat + - cd .. + - nim c koch + - koch boot + - koch boot -d:release + +before_test: + - nim e install_nimble.nims + - nimble update + - nimble install zip + +test_script: + - nim c --taintMode:on tests/testament/tester + - tests\testament\tester --pedantic all diff --git a/bin/empty.txt b/bin/empty.txt index 20f9a91e3..666142073 100644 --- a/bin/empty.txt +++ b/bin/empty.txt @@ -1 +1 @@ -This file keeps several tools from deleting this subdirectory. +This file keeps several tools from deleting this subdirectory. diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 000000000..7f19c2440 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,22 @@ +#!/bin/sh +set -e +set -x + +if [ ! -e csources/.git ]; then + git clone --depth 1 git://github.com/nim-lang/csources.git csources +fi + +cd "csources" +sh build.sh +cd ".." + +./bin/nim c koch +./koch boot -d:release +./koch geninstall + +set +x + +echo +echo 'Install Nim using "./install.sh " or "sudo ./install.sh ".' + +exit 0 diff --git a/build.sh b/build.sh deleted file mode 100644 index 139c28359..000000000 --- a/build.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh -set -e -set -x - -if [ ! -d "csources" ]; then - git clone --depth 1 https://github.com/nim-lang/csources.git -fi - -cd "csources" -sh build.sh -cd ".." - -./bin/nim c koch -./koch boot -d:release - -cp -f install.sh.template install.sh -chmod +x install.sh - -exit 0 diff --git a/build/empty.txt b/build/empty.txt index 20f9a91e3..666142073 100644 --- a/build/empty.txt +++ b/build/empty.txt @@ -1 +1 @@ -This file keeps several tools from deleting this subdirectory. +This file keeps several tools from deleting this subdirectory. diff --git a/compiler.nimble b/compiler.nimble index 52eb4083b..9d39af502 100644 --- a/compiler.nimble +++ b/compiler.nimble @@ -1,6 +1,6 @@ [Package] name = "compiler" -version = "0.10.3" +version = "0.11.3" author = "Andreas Rumpf" description = "Compiler package providing the compiler sources as a library." license = "MIT" @@ -8,4 +8,4 @@ license = "MIT" InstallDirs = "doc, compiler" [Deps] -Requires: "nim >= 0.10.3" +Requires: "nim >= 0.11.3" 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,13 +503,13 @@ 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] = [ - "($#)($# + $#)", "($#)($# - $#)", "($#)($# * $#)", - "($#)($# / $#)", "($#)($# % $#)", "($#)($# + $#)", "($#)($# - $#)", "($#)($# * $#)", "($#)($# / $#)", "($#)($# % $#)", "($#)($# + $#)", "($#)($# - $#)"] @@ -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 .. 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'); - """ % [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 = 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 .. 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 @@ -479,6 +499,30 @@ type ERecoverableError* = object of ValueError 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) @@ -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: - if eStdErr in errorOutputs: writeln(stderr, s) + elif optStdout in gGlobalOptions or msgStdout in flags: + if eStdOut in errorOutputs: + writeLine(stdout, s) + flushFile(stdout) else: - if eStdOut in errorOutputs: writeln(stdout, s) + 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 eStdOut in errorOutputs: + callIgnoringStyle(writeLine, stdout, args) + flushFile(stdout) + else: + 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 & " ") + styledMsgWriteln(fgRed, "No stack traceback available\nTo create a stacktrace, rerun compilation with ./koch temp " & options.command & " ") 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 similarity index 95% rename from compiler/nim.nim.cfg rename to 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..