Merge branch 'devel'

This commit is contained in:
Dominik Picheta 2015-04-30 16:09:27 +01:00
commit 0be654efe1
669 changed files with 28587 additions and 20697 deletions

1
.gitignore vendored
View file

@ -41,3 +41,4 @@ xcuserdata/
/testresults.html /testresults.html
/testresults.json /testresults.json
testament.db testament.db
/csources/

11
compiler.nimble Normal file
View file

@ -0,0 +1,11 @@
[Package]
name = "compiler"
version = "0.10.3"
author = "Andreas Rumpf"
description = "Compiler package providing the compiler sources as a library."
license = "MIT"
InstallDirs = "doc, compiler"
[Deps]
Requires: "nim >= 0.10.3"

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2013 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -196,6 +196,7 @@ type
nkTypeOfExpr, # type(1+2) nkTypeOfExpr, # type(1+2)
nkObjectTy, # object body nkObjectTy, # object body
nkTupleTy, # tuple body nkTupleTy, # tuple body
nkTupleClassTy, # tuple type class
nkTypeClassTy, # user-defined type class nkTypeClassTy, # user-defined type class
nkStaticTy, # ``static[T]`` nkStaticTy, # ``static[T]``
nkRecList, # list of object parts nkRecList, # list of object parts
@ -256,7 +257,7 @@ type
sfThread, # proc will run as a thread sfThread, # proc will run as a thread
# variable is a thread variable # variable is a thread variable
sfCompileTime, # proc can be evaluated at compile time sfCompileTime, # proc can be evaluated at compile time
sfMerge, # proc can be merged with itself sfConstructor, # proc is a C++ constructor
sfDeadCodeElim, # dead code elimination for the module is turned on sfDeadCodeElim, # dead code elimination for the module is turned on
sfBorrow, # proc is borrowed sfBorrow, # proc is borrowed
sfInfixCall, # symbol needs infix call syntax in target language; sfInfixCall, # symbol needs infix call syntax in target language;
@ -295,6 +296,7 @@ const
sfCompileToCpp* = sfInfixCall # compile the module as C++ code sfCompileToCpp* = sfInfixCall # compile the module as C++ code
sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code
sfExperimental* = sfOverriden # module uses the .experimental switch sfExperimental* = sfOverriden # module uses the .experimental switch
sfGoto* = sfOverriden # var is used for 'goto' code generation
const const
# getting ready for the future expr/stmt merge # getting ready for the future expr/stmt merge
@ -314,7 +316,7 @@ type
# XXX put this into an include file to avoid this issue! # XXX put this into an include file to avoid this issue!
tyNone, tyBool, tyChar, tyNone, tyBool, tyChar,
tyEmpty, tyArrayConstr, tyNil, tyExpr, tyStmt, tyTypeDesc, tyEmpty, tyArrayConstr, tyNil, tyExpr, tyStmt, tyTypeDesc,
tyGenericInvokation, # ``T[a, b]`` for types to invoke tyGenericInvocation, # ``T[a, b]`` for types to invoke
tyGenericBody, # ``T[a, b, body]`` last parameter is the body tyGenericBody, # ``T[a, b, body]`` last parameter is the body
tyGenericInst, # ``T[a, b, realInstance]`` instantiated generic type tyGenericInst, # ``T[a, b, realInstance]`` instantiated generic type
# realInstance will be a concrete type like tyObject # realInstance will be a concrete type like tyObject
@ -377,7 +379,7 @@ type
tyFromExpr #\ tyFromExpr #\
# This is a type representing an expression that depends # This is a type representing an expression that depends
# on generic parameters (the exprsesion is stored in t.n) # on generic parameters (the expression is stored in t.n)
# It will be converted to a real type only during generic # It will be converted to a real type only during generic
# instantiation and prior to this it has the potential to # instantiation and prior to this it has the potential to
# be any type. # be any type.
@ -399,6 +401,7 @@ const
tyPureObject* = tyTuple tyPureObject* = tyTuple
GcTypeKinds* = {tyRef, tySequence, tyString} GcTypeKinds* = {tyRef, tySequence, tyString}
tyError* = tyProxy # as an errornous node should match everything tyError* = tyProxy # as an errornous node should match everything
tyUnknown* = tyFromExpr
tyUnknownTypes* = {tyError, tyFromExpr} tyUnknownTypes* = {tyError, tyFromExpr}
@ -427,6 +430,7 @@ type
nfExplicitCall # x.y() was used instead of x.y nfExplicitCall # x.y() was used instead of x.y
nfExprCall # this is an attempt to call a regular expression nfExprCall # this is an attempt to call a regular expression
nfIsRef # this node is a 'ref' node; used for the VM nfIsRef # this node is a 'ref' node; used for the VM
nfIsCursor # this node is attached a cursor; used for idetools
TNodeFlags* = set[TNodeFlag] TNodeFlags* = set[TNodeFlag]
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 28) TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 28)
@ -456,8 +460,8 @@ type
tfNotNil, # type cannot be 'nil' tfNotNil, # type cannot be 'nil'
tfNeedsInit, # type constains a "not nil" constraint somewhere or some tfNeedsInit, # type constains a "not nil" constraint somewhere or some
# other type so that it requires inititalization # other type so that it requires initalization
tfHasShared, # type constains a "shared" constraint modifier somewhere tfVarIsPtr, # 'var' type is translated like 'ptr' even in C++ mode
tfHasMeta, # type contains "wildcard" sub-types such as generic params tfHasMeta, # type contains "wildcard" sub-types such as generic params
# or other type classes # or other type classes
tfHasGCedMem, # type contains GC'ed memory tfHasGCedMem, # type contains GC'ed memory
@ -469,7 +473,7 @@ type
# T and I here can bind to both typedesc and static types # T and I here can bind to both typedesc and static types
# before this is determined, we'll consider them to be a # before this is determined, we'll consider them to be a
# wildcard type. # wildcard type.
tfGuarded # guarded pointer tfHasAsgn # type has overloaded assignment operator
tfBorrowDot # distinct type borrows '.' tfBorrowDot # distinct type borrows '.'
TTypeFlags* = set[TTypeFlag] TTypeFlags* = set[TTypeFlag]
@ -520,23 +524,26 @@ const
skError* = skUnknown skError* = skUnknown
# type flags that are essential for type equality: # type flags that are essential for type equality:
eqTypeFlags* = {tfIterator, tfShared, tfNotNil} eqTypeFlags* = {tfIterator, tfShared, tfNotNil, tfVarIsPtr}
type type
TMagic* = enum # symbols that require compiler magic: TMagic* = enum # symbols that require compiler magic:
mNone, mNone,
mDefined, mDefinedInScope, mCompiles, mDefined, mDefinedInScope, mCompiles,
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof, mPlugin,
mEcho, mShallowCopy, mSlurp, mStaticExec, mEcho, mShallowCopy, mSlurp, mStaticExec,
mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst, mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
mUnaryLt, mSucc, mUnaryLt, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq,
mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref, mIncl, mExcl, mCard, mChr,
mGCunref, mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64, mGCref, mGCunref,
mDivI64, mModI64,
mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64,
mDivI64, mModI64, mSucc, mPred,
mAddF64, mSubF64, mMulF64, mDivF64, mAddF64, mSubF64, mMulF64, mDivF64,
mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI, mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI,
mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinI64, mMaxI64, mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64,
mMinF64, mMaxF64, mAddU, mSubU, mMulU, mMinF64, mMaxF64, mAddU, mSubU, mMulU,
mDivU, mModU, mEqI, mLeI, mDivU, mModU, mEqI, mLeI,
mLtI, mLtI,
@ -545,14 +552,14 @@ type
mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef, mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef,
mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mEqProc, mUnaryMinusI, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mEqProc, mUnaryMinusI,
mUnaryMinusI64, mAbsI, mAbsI64, mNot, mUnaryMinusI64, mAbsI, mAbsI64, mNot,
mUnaryPlusI, mBitnotI, mUnaryPlusI64, mUnaryPlusI, mBitnotI,
mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64, mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64,
mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32,
mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr,
mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr,
mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet,
mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mConArrArr, mConArrT, mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mSlice,
mConTArr, mConTT, mSlice, mDotDot, # this one is only necessary to give nice compile time warnings
mFields, mFieldPairs, mOmpParFor, mFields, mFieldPairs, mOmpParFor,
mAppendStrCh, mAppendStrStr, mAppendSeqElem, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq,
@ -584,11 +591,12 @@ type
const const
ctfeWhitelist* = {mNone, mUnaryLt, mSucc, ctfeWhitelist* = {mNone, mUnaryLt, mSucc,
mPred, mInc, mDec, mOrd, mLengthOpenArray, mPred, mInc, mDec, mOrd, mLengthOpenArray,
mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq,
mIncl, mExcl, mCard, mChr,
mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64, mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64,
mDivI64, mModI64, mAddF64, mSubF64, mMulF64, mDivF64, mDivI64, mModI64, mAddF64, mSubF64, mMulF64, mDivF64,
mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI, mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI,
mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinI64, mMaxI64, mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64,
mMinF64, mMaxF64, mAddU, mSubU, mMulU, mMinF64, mMaxF64, mAddU, mSubU, mMulU,
mDivU, mModU, mEqI, mLeI, mDivU, mModU, mEqI, mLeI,
mLtI, mLtI,
@ -597,14 +605,13 @@ const
mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef, mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef,
mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI, mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI,
mUnaryMinusI64, mAbsI, mAbsI64, mNot, mUnaryMinusI64, mAbsI, mAbsI64, mNot,
mUnaryPlusI, mBitnotI, mUnaryPlusI64, mUnaryPlusI, mBitnotI,
mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64, mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64,
mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32,
mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr,
mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr,
mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet,
mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mConArrArr, mConArrT, mPlusSet, mMinusSet, mSymDiffSet, mConStrStr,
mConTArr, mConTT,
mAppendStrCh, mAppendStrStr, mAppendSeqElem, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
mInRange, mInSet, mRepr, mInRange, mInSet, mRepr,
mCopyStr, mCopyStrLast} mCopyStr, mCopyStrLast}
@ -653,7 +660,6 @@ type
locGlobalVar, # location is a global variable locGlobalVar, # location is a global variable
locParam, # location is a parameter locParam, # location is a parameter
locField, # location is a record field locField, # location is a record field
locArrayElem, # location is an array element
locExpr, # "location" is really an expression locExpr, # "location" is really an expression
locProc, # location is a proc (an address of a procedure) locProc, # location is a proc (an address of a procedure)
locData, # location is a constant locData, # location is a constant
@ -661,26 +667,29 @@ type
locOther # location is something other locOther # location is something other
TLocFlag* = enum TLocFlag* = enum
lfIndirect, # backend introduced a pointer lfIndirect, # backend introduced a pointer
lfParamCopy, # backend introduced a parameter copy (LLVM) lfFullExternalName, # only used when 'gCmd == cmdPretty': Indicates
# that the symbol has been imported via 'importc: "fullname"' and
# no format string.
lfNoDeepCopy, # no need for a deep copy lfNoDeepCopy, # no need for a deep copy
lfNoDecl, # do not declare it in C lfNoDecl, # do not declare it in C
lfDynamicLib, # link symbol to dynamic library lfDynamicLib, # link symbol to dynamic library
lfExportLib, # export symbol for dynamic library generation lfExportLib, # export symbol for dynamic library generation
lfHeader, # include header file for symbol lfHeader, # include header file for symbol
lfImportCompilerProc # ``importc`` of a compilerproc lfImportCompilerProc, # ``importc`` of a compilerproc
lfSingleUse # no location yet and will only be used once
TStorageLoc* = enum TStorageLoc* = enum
OnUnknown, # location is unknown (stack, heap or static) OnUnknown, # location is unknown (stack, heap or static)
OnStack, # location is on hardware stack OnStack, # location is on hardware stack
OnHeap # location is on heap or global OnHeap # location is on heap or global
# (reference counting needed) # (reference counting needed)
TLocFlags* = set[TLocFlag] TLocFlags* = set[TLocFlag]
TLoc*{.final.} = object TLoc* = object
k*: TLocKind # kind of location k*: TLocKind # kind of location
s*: TStorageLoc s*: TStorageLoc
flags*: TLocFlags # location's flags flags*: TLocFlags # location's flags
t*: PType # type of location t*: PType # type of location
r*: PRope # rope value of location (code generators) r*: Rope # rope value of location (code generators)
heapRoot*: PRope # keeps track of the enclosing heap object that heapRoot*: Rope # keeps track of the enclosing heap object that
# owns this location (required by GC algorithms # owns this location (required by GC algorithms
# employing heap snapshots or sliding views) # employing heap snapshots or sliding views)
@ -692,7 +701,7 @@ type
kind*: TLibKind kind*: TLibKind
generated*: bool # needed for the backends: generated*: bool # needed for the backends:
isOverriden*: bool isOverriden*: bool
name*: PRope name*: Rope
path*: PNode # can be a string literal! path*: PNode # can be a string literal!
TInstantiation* = object TInstantiation* = object
@ -722,7 +731,8 @@ type
typScope*: PScope typScope*: PScope
of routineKinds: of routineKinds:
procInstCache*: seq[PInstantiation] procInstCache*: seq[PInstantiation]
scope*: PScope # the scope where the proc was defined gcUnsafetyReason*: PSym # for better error messages wrt gcsafe
#scope*: PScope # the scope where the proc was defined
of skModule: of skModule:
# modules keep track of the generic symbols they use from other modules. # modules keep track of the generic symbols they use from other modules.
# this is because in incremental compilation, when a module is about to # this is because in incremental compilation, when a module is about to
@ -788,8 +798,8 @@ type
# for enum types a list of symbols # for enum types a list of symbols
# for tyInt it can be the int literal # for tyInt it can be the int literal
# for procs and tyGenericBody, it's the # for procs and tyGenericBody, it's the
# the body of the user-defined type class
# formal param list # formal param list
# for concepts, the concept body
# else: unused # else: unused
owner*: PSym # the 'owner' of the type owner*: PSym # the 'owner' of the type
sym*: PSym # types have the sym associated with them sym*: PSym # types have the sym associated with them
@ -798,51 +808,52 @@ type
# mean that there is no destructor. # mean that there is no destructor.
# see instantiateDestructor in semdestruct.nim # see instantiateDestructor in semdestruct.nim
deepCopy*: PSym # overriden 'deepCopy' operation deepCopy*: PSym # overriden 'deepCopy' operation
assignment*: PSym # overriden '=' operator
size*: BiggestInt # the size of the type in bytes size*: BiggestInt # the size of the type in bytes
# -1 means that the size is unkwown # -1 means that the size is unkwown
align*: int16 # the type's alignment requirements align*: int16 # the type's alignment requirements
lockLevel*: TLockLevel # lock level as required for deadlock checking lockLevel*: TLockLevel # lock level as required for deadlock checking
loc*: TLoc loc*: TLoc
TPair*{.final.} = object TPair* = object
key*, val*: RootRef key*, val*: RootRef
TPairSeq* = seq[TPair] TPairSeq* = seq[TPair]
TTable*{.final.} = object # the same as table[PObject] of PObject TTable* = object # the same as table[PObject] of PObject
counter*: int counter*: int
data*: TPairSeq data*: TPairSeq
TIdPair*{.final.} = object TIdPair* = object
key*: PIdObj key*: PIdObj
val*: RootRef val*: RootRef
TIdPairSeq* = seq[TIdPair] TIdPairSeq* = seq[TIdPair]
TIdTable*{.final.} = object # the same as table[PIdent] of PObject TIdTable* = object # the same as table[PIdent] of PObject
counter*: int counter*: int
data*: TIdPairSeq data*: TIdPairSeq
TIdNodePair*{.final.} = object TIdNodePair* = object
key*: PIdObj key*: PIdObj
val*: PNode val*: PNode
TIdNodePairSeq* = seq[TIdNodePair] TIdNodePairSeq* = seq[TIdNodePair]
TIdNodeTable*{.final.} = object # the same as table[PIdObj] of PNode TIdNodeTable* = object # the same as table[PIdObj] of PNode
counter*: int counter*: int
data*: TIdNodePairSeq data*: TIdNodePairSeq
TNodePair*{.final.} = object TNodePair* = object
h*: THash # because it is expensive to compute! h*: THash # because it is expensive to compute!
key*: PNode key*: PNode
val*: int val*: int
TNodePairSeq* = seq[TNodePair] TNodePairSeq* = seq[TNodePair]
TNodeTable*{.final.} = object # the same as table[PNode] of int; TNodeTable* = object # the same as table[PNode] of int;
# nodes are compared by structure! # nodes are compared by structure!
counter*: int counter*: int
data*: TNodePairSeq data*: TNodePairSeq
TObjectSeq* = seq[RootRef] TObjectSeq* = seq[RootRef]
TObjectSet*{.final.} = object TObjectSet* = object
counter*: int counter*: int
data*: TObjectSeq data*: TObjectSeq
@ -857,7 +868,7 @@ const
OverloadableSyms* = {skProc, skMethod, skIterator, skClosureIterator, OverloadableSyms* = {skProc, skMethod, skIterator, skClosureIterator,
skConverter, skModule, skTemplate, skMacro} skConverter, skModule, skTemplate, skMacro}
GenericTypes*: TTypeKinds = {tyGenericInvokation, tyGenericBody, GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody,
tyGenericParam} tyGenericParam}
StructuralEquivTypes*: TTypeKinds = {tyArrayConstr, tyNil, tyTuple, tyArray, StructuralEquivTypes*: TTypeKinds = {tyArrayConstr, tyNil, tyTuple, tyArray,
@ -882,7 +893,7 @@ const
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias} skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
nfDotSetter, nfDotField, nfDotSetter, nfDotField,
nfIsRef} nfIsRef, nfIsCursor}
namePos* = 0 namePos* = 0
patternPos* = 1 # empty except for term rewriting macros patternPos* = 1 # empty except for term rewriting macros
genericParamsPos* = 2 genericParamsPos* = 2
@ -912,53 +923,7 @@ const
skIterators* = {skIterator, skClosureIterator} skIterators* = {skIterator, skClosureIterator}
lfFullExternalName* = lfParamCopy # \ var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things
# only used when 'gCmd == cmdPretty': Indicates that the symbol has been
# imported via 'importc: "fullname"' and no format string.
# creator procs:
proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
info: TLineInfo): PSym
proc newType*(kind: TTypeKind, owner: PSym): PType
proc newNode*(kind: TNodeKind): PNode
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode
proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode
proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode
proc newStrNode*(kind: TNodeKind, strVal: string): PNode
proc newIdentNode*(ident: PIdent, info: TLineInfo): PNode
proc newSymNode*(sym: PSym): PNode
proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode
proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode
proc initStrTable*(x: var TStrTable)
proc initTable*(x: var TTable)
proc initIdTable*(x: var TIdTable)
proc initObjectSet*(x: var TObjectSet)
proc initIdNodeTable*(x: var TIdNodeTable)
proc initNodeTable*(x: var TNodeTable)
# copy procs:
proc copyType*(t: PType, owner: PSym, keepId: bool): PType
proc copySym*(s: PSym, keepId: bool = false): PSym
proc assignType*(dest, src: PType)
proc copyStrTable*(dest: var TStrTable, src: TStrTable)
proc copyTable*(dest: var TTable, src: TTable)
proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet)
proc copyIdTable*(dest: var TIdTable, src: TIdTable)
proc sonsLen*(n: PNode): int {.inline.}
proc sonsLen*(n: PType): int {.inline.}
proc lastSon*(n: PNode): PNode {.inline.}
proc lastSon*(n: PType): PType {.inline.}
proc newSons*(father: PNode, length: int)
proc newSons*(father: PType, length: int)
proc addSon*(father, son: PNode)
proc delSon*(father: PNode, idx: int)
proc hasSonWith*(n: PNode, kind: TNodeKind): bool
proc hasSubnodeWith*(n: PNode, kind: TNodeKind): bool
proc replaceSons*(n: PNode, oldKind, newKind: TNodeKind)
proc copyNode*(src: PNode): PNode
# does not copy its sons!
proc copyTree*(src: PNode): PNode
# does copy its sons!
proc isCallExpr*(n: PNode): bool = proc isCallExpr*(n: PNode): bool =
result = n.kind in nkCallKinds result = n.kind in nkCallKinds
@ -987,6 +952,57 @@ template `{}`*(n: PNode, i: int): expr = n[i -| n]
template `{}=`*(n: PNode, i: int, s: PNode): stmt = template `{}=`*(n: PNode, i: int, s: PNode): stmt =
n.sons[i -| n] = s n.sons[i -| n] = s
when defined(useNodeIds):
const nodeIdToDebug* = -1 # 299750 # 300761 #300863 # 300879
var gNodeId: int
proc newNode*(kind: TNodeKind): PNode =
new(result)
result.kind = kind
#result.info = UnknownLineInfo() inlined:
result.info.fileIndex = int32(- 1)
result.info.col = int16(- 1)
result.info.line = int16(- 1)
when defined(useNodeIds):
result.id = gNodeId
if result.id == nodeIdToDebug:
echo "KIND ", result.kind
writeStackTrace()
inc gNodeId
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
result = newNode(kind)
result.intVal = intVal
proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode =
result = newIntNode(kind, intVal)
result.typ = typ
proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode =
result = newNode(kind)
result.floatVal = floatVal
proc newStrNode*(kind: TNodeKind, strVal: string): PNode =
result = newNode(kind)
result.strVal = strVal
proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
info: TLineInfo): PSym =
# generates a symbol and initializes the hash field too
new(result)
result.name = name
result.kind = symKind
result.flags = {}
result.info = info
result.options = gOptions
result.owner = owner
result.offset = - 1
result.id = getID()
when debugIds:
registerId(result)
#if result.id < 2000:
# MessageOut(name.s & " has id: " & toString(result.id))
var emptyNode* = newNode(nkEmpty) var emptyNode* = newNode(nkEmpty)
# There is a single empty node that is shared! Do not overwrite it! # There is a single empty node that is shared! Do not overwrite it!
@ -1029,80 +1045,43 @@ const # for all kind of hash tables:
GrowthFactor* = 2 # must be power of 2, > 0 GrowthFactor* = 2 # must be power of 2, > 0
StartSize* = 8 # must be power of 2, > 0 StartSize* = 8 # must be power of 2, > 0
proc copyStrTable(dest: var TStrTable, src: TStrTable) = proc copyStrTable*(dest: var TStrTable, src: TStrTable) =
dest.counter = src.counter dest.counter = src.counter
if isNil(src.data): return if isNil(src.data): return
setLen(dest.data, len(src.data)) setLen(dest.data, len(src.data))
for i in countup(0, high(src.data)): dest.data[i] = src.data[i] for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
proc copyIdTable(dest: var TIdTable, src: TIdTable) = proc copyIdTable*(dest: var TIdTable, src: TIdTable) =
dest.counter = src.counter dest.counter = src.counter
if isNil(src.data): return if isNil(src.data): return
newSeq(dest.data, len(src.data)) newSeq(dest.data, len(src.data))
for i in countup(0, high(src.data)): dest.data[i] = src.data[i] for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
proc copyTable(dest: var TTable, src: TTable) = proc copyTable*(dest: var TTable, src: TTable) =
dest.counter = src.counter dest.counter = src.counter
if isNil(src.data): return if isNil(src.data): return
setLen(dest.data, len(src.data)) setLen(dest.data, len(src.data))
for i in countup(0, high(src.data)): dest.data[i] = src.data[i] for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
proc copyObjectSet(dest: var TObjectSet, src: TObjectSet) = proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) =
dest.counter = src.counter dest.counter = src.counter
if isNil(src.data): return if isNil(src.data): return
setLen(dest.data, len(src.data)) setLen(dest.data, len(src.data))
for i in countup(0, high(src.data)): dest.data[i] = src.data[i] for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
proc discardSons(father: PNode) = proc discardSons*(father: PNode) =
father.sons = nil father.sons = nil
when defined(useNodeIds):
const nodeIdToDebug* = -1 # 884953 # 612794
#612840 # 612905 # 614635 # 614637 # 614641
# 423408
#429107 # 430443 # 441048 # 441090 # 441153
var gNodeId: int
proc newNode(kind: TNodeKind): PNode =
new(result)
result.kind = kind
#result.info = UnknownLineInfo() inlined:
result.info.fileIndex = int32(- 1)
result.info.col = int16(- 1)
result.info.line = int16(- 1)
when defined(useNodeIds):
result.id = gNodeId
if result.id == nodeIdToDebug:
echo "KIND ", result.kind
writeStackTrace()
inc gNodeId
proc newIntNode(kind: TNodeKind, intVal: BiggestInt): PNode =
result = newNode(kind)
result.intVal = intVal
proc newIntTypeNode(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode =
result = newIntNode(kind, intVal)
result.typ = typ
proc newFloatNode(kind: TNodeKind, floatVal: BiggestFloat): PNode =
result = newNode(kind)
result.floatVal = floatVal
proc newStrNode(kind: TNodeKind, strVal: string): PNode =
result = newNode(kind)
result.strVal = strVal
proc withInfo*(n: PNode, info: TLineInfo): PNode = proc withInfo*(n: PNode, info: TLineInfo): PNode =
n.info = info n.info = info
return n return n
proc newIdentNode(ident: PIdent, info: TLineInfo): PNode = proc newIdentNode*(ident: PIdent, info: TLineInfo): PNode =
result = newNode(nkIdent) result = newNode(nkIdent)
result.ident = ident result.ident = ident
result.info = info result.info = info
proc newSymNode(sym: PSym): PNode = proc newSymNode*(sym: PSym): PNode =
result = newNode(nkSym) result = newNode(nkSym)
result.sym = sym result.sym = sym
result.typ = sym.typ result.typ = sym.typ
@ -1114,7 +1093,7 @@ proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
result.typ = sym.typ result.typ = sym.typ
result.info = info result.info = info
proc newNodeI(kind: TNodeKind, info: TLineInfo): PNode = proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode =
new(result) new(result)
result.kind = kind result.kind = kind
result.info = info result.info = info
@ -1153,11 +1132,16 @@ proc newNode*(kind: TNodeKind, info: TLineInfo, sons: TNodeSeq = @[],
writeStackTrace() writeStackTrace()
inc gNodeId inc gNodeId
proc newNodeIT(kind: TNodeKind, info: TLineInfo, typ: PType): PNode = proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
result = newNode(kind) result = newNode(kind)
result.info = info result.info = info
result.typ = typ result.typ = typ
proc addSon*(father, son: PNode) =
assert son != nil
if isNil(father.sons): father.sons = @[]
add(father.sons, son)
var emptyParams = newNode(nkFormalParams) var emptyParams = newNode(nkFormalParams)
emptyParams.addSon(emptyNode) emptyParams.addSon(emptyNode)
@ -1179,7 +1163,7 @@ proc `$`*(x: TLockLevel): string =
elif x.ord == UnknownLockLevel.ord: result = "<unknown>" elif x.ord == UnknownLockLevel.ord: result = "<unknown>"
else: result = $int16(x) else: result = $int16(x)
proc newType(kind: TTypeKind, owner: PSym): PType = proc newType*(kind: TTypeKind, owner: PSym): PType =
new(result) new(result)
result.kind = kind result.kind = kind
result.owner = owner result.owner = owner
@ -1189,7 +1173,9 @@ proc newType(kind: TTypeKind, owner: PSym): PType =
result.lockLevel = UnspecifiedLockLevel result.lockLevel = UnspecifiedLockLevel
when debugIds: when debugIds:
registerId(result) registerId(result)
#if result.id < 2000: #if result.id == 92231:
# echo "KNID ", kind
# writeStackTrace()
# messageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id)) # messageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id))
proc mergeLoc(a: var TLoc, b: TLoc) = proc mergeLoc(a: var TLoc, b: TLoc) =
@ -1200,7 +1186,37 @@ proc mergeLoc(a: var TLoc, b: TLoc) =
if a.r == nil: a.r = b.r if a.r == nil: a.r = b.r
#if a.a == 0: a.a = b.a #if a.a == 0: a.a = b.a
proc assignType(dest, src: PType) = proc newSons*(father: PNode, length: int) =
if isNil(father.sons):
newSeq(father.sons, length)
else:
setLen(father.sons, length)
proc newSons*(father: PType, length: int) =
if isNil(father.sons):
newSeq(father.sons, length)
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 assignType*(dest, src: PType) =
dest.kind = src.kind dest.kind = src.kind
dest.flags = src.flags dest.flags = src.flags
dest.callConv = src.callConv dest.callConv = src.callConv
@ -1209,6 +1225,7 @@ proc assignType(dest, src: PType) =
dest.align = src.align dest.align = src.align
dest.destructor = src.destructor dest.destructor = src.destructor
dest.deepCopy = src.deepCopy dest.deepCopy = src.deepCopy
dest.assignment = src.assignment
dest.lockLevel = src.lockLevel dest.lockLevel = src.lockLevel
# this fixes 'type TLock = TSysLock': # this fixes 'type TLock = TSysLock':
if src.sym != nil: if src.sym != nil:
@ -1221,7 +1238,7 @@ proc assignType(dest, src: PType) =
newSons(dest, sonsLen(src)) newSons(dest, sonsLen(src))
for i in countup(0, sonsLen(src) - 1): dest.sons[i] = src.sons[i] for i in countup(0, sonsLen(src) - 1): dest.sons[i] = src.sons[i]
proc copyType(t: PType, owner: PSym, keepId: bool): PType = proc copyType*(t: PType, owner: PSym, keepId: bool): PType =
result = newType(t.kind, owner) result = newType(t.kind, owner)
assignType(result, t) assignType(result, t)
if keepId: if keepId:
@ -1230,7 +1247,7 @@ proc copyType(t: PType, owner: PSym, keepId: bool): PType =
when debugIds: registerId(result) when debugIds: registerId(result)
result.sym = t.sym # backend-info should not be copied result.sym = t.sym # backend-info should not be copied
proc copySym(s: PSym, keepId: bool = false): PSym = proc copySym*(s: PSym, keepId: bool = false): PSym =
result = newSym(s.kind, s.name, s.owner, s.info) result = newSym(s.kind, s.name, s.owner, s.info)
#result.ast = nil # BUGFIX; was: s.ast which made problems #result.ast = nil # BUGFIX; was: s.ast which made problems
result.typ = s.typ result.typ = s.typ
@ -1264,24 +1281,7 @@ proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo): PSym =
# XXX once usedGenerics is used, ensure module aliases keep working! # XXX once usedGenerics is used, ensure module aliases keep working!
assert s.usedGenerics == nil assert s.usedGenerics == nil
proc newSym(symKind: TSymKind, name: PIdent, owner: PSym, proc initStrTable*(x: var TStrTable) =
info: TLineInfo): PSym =
# generates a symbol and initializes the hash field too
new(result)
result.name = name
result.kind = symKind
result.flags = {}
result.info = info
result.options = gOptions
result.owner = owner
result.offset = - 1
result.id = getID()
when debugIds:
registerId(result)
#if result.id < 2000:
# MessageOut(name.s & " has id: " & toString(result.id))
proc initStrTable(x: var TStrTable) =
x.counter = 0 x.counter = 0
newSeq(x.data, StartSize) newSeq(x.data, StartSize)
@ -1292,46 +1292,28 @@ proc initTable(x: var TTable) =
x.counter = 0 x.counter = 0
newSeq(x.data, StartSize) newSeq(x.data, StartSize)
proc initIdTable(x: var TIdTable) = proc initIdTable*(x: var TIdTable) =
x.counter = 0 x.counter = 0
newSeq(x.data, StartSize) newSeq(x.data, StartSize)
proc initObjectSet(x: var TObjectSet) = proc resetIdTable*(x: var TIdTable) =
x.counter = 0
# clear and set to old initial size:
setLen(x.data, 0)
setLen(x.data, StartSize)
proc initObjectSet*(x: var TObjectSet) =
x.counter = 0 x.counter = 0
newSeq(x.data, StartSize) newSeq(x.data, StartSize)
proc initIdNodeTable(x: var TIdNodeTable) = proc initIdNodeTable*(x: var TIdNodeTable) =
x.counter = 0 x.counter = 0
newSeq(x.data, StartSize) newSeq(x.data, StartSize)
proc initNodeTable(x: var TNodeTable) = proc initNodeTable*(x: var TNodeTable) =
x.counter = 0 x.counter = 0
newSeq(x.data, StartSize) newSeq(x.data, StartSize)
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 newSons(father: PType, length: int) =
if isNil(father.sons):
newSeq(father.sons, length)
else:
setLen(father.sons, length)
proc sonsLen(n: PNode): int =
if isNil(n.sons): result = 0
else: result = len(n.sons)
proc newSons(father: PNode, length: int) =
if isNil(father.sons):
newSeq(father.sons, length)
else:
setLen(father.sons, length)
proc skipTypes*(t: PType, kinds: TTypeKinds): PType = proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
## Used throughout the compiler code to test whether a type tree contains or ## Used throughout the compiler code to test whether a type tree contains or
## doesn't contain a specific type/types - it is often the case that only the ## doesn't contain a specific type/types - it is often the case that only the
@ -1340,15 +1322,22 @@ proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
result = t result = t
while result.kind in kinds: result = lastSon(result) while result.kind in kinds: result = lastSon(result)
proc skipTypesOrNil*(t: PType, kinds: TTypeKinds): PType =
## same as skipTypes but handles 'nil'
result = t
while result != nil and result.kind in kinds:
if result.len == 0: return nil
result = lastSon(result)
proc isGCedMem*(t: PType): bool {.inline.} = proc isGCedMem*(t: PType): bool {.inline.} =
result = t.kind in {tyString, tyRef, tySequence} or result = t.kind in {tyString, tyRef, tySequence} or
t.kind == tyProc and t.callConv == ccClosure t.kind == tyProc and t.callConv == ccClosure
proc propagateToOwner*(owner, elem: PType) = proc propagateToOwner*(owner, elem: PType) =
const HaveTheirOwnEmpty = {tySequence, tySet} const HaveTheirOwnEmpty = {tySequence, tySet, tyPtr, tyRef, tyProc}
owner.flags = owner.flags + (elem.flags * {tfHasShared, tfHasMeta}) owner.flags = owner.flags + (elem.flags * {tfHasMeta})
if tfNotNil in elem.flags: if tfNotNil in elem.flags:
if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvokation}: if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvocation}:
owner.flags.incl tfNotNil owner.flags.incl tfNotNil
elif owner.kind notin HaveTheirOwnEmpty: elif owner.kind notin HaveTheirOwnEmpty:
owner.flags.incl tfNeedsInit owner.flags.incl tfNeedsInit
@ -1357,14 +1346,22 @@ proc propagateToOwner*(owner, elem: PType) =
if owner.kind in HaveTheirOwnEmpty: discard if owner.kind in HaveTheirOwnEmpty: discard
else: owner.flags.incl tfNeedsInit else: owner.flags.incl tfNeedsInit
if tfShared in elem.flags:
owner.flags.incl tfHasShared
if elem.isMetaType: if elem.isMetaType:
owner.flags.incl tfHasMeta owner.flags.incl tfHasMeta
if owner.kind != tyProc: if tfHasAsgn in elem.flags:
if elem.isGCedMem or tfHasGCedMem in elem.flags: let o2 = elem.skipTypes({tyGenericInst})
if o2.kind in {tyTuple, tyObject, tyArray, tyArrayConstr,
tySequence, tySet, tyDistinct}:
o2.flags.incl tfHasAsgn
owner.flags.incl tfHasAsgn
if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
tyGenericInvocation}:
let elemB = elem.skipTypes({tyGenericInst})
if elemB.isGCedMem or tfHasGCedMem in elemB.flags:
# for simplicity, we propagate this flag even to generics. We then
# ensure this doesn't bite us in sempass2.
owner.flags.incl tfHasGCedMem owner.flags.incl tfHasGCedMem
proc rawAddSon*(father, son: PType) = proc rawAddSon*(father, son: PType) =
@ -1372,22 +1369,17 @@ proc rawAddSon*(father, son: PType) =
add(father.sons, son) add(father.sons, son)
if not son.isNil: propagateToOwner(father, son) if not son.isNil: propagateToOwner(father, son)
proc addSon(father, son: PNode) =
assert son != nil
if isNil(father.sons): father.sons = @[]
add(father.sons, son)
proc addSonNilAllowed*(father, son: PNode) = proc addSonNilAllowed*(father, son: PNode) =
if isNil(father.sons): father.sons = @[] if isNil(father.sons): father.sons = @[]
add(father.sons, son) add(father.sons, son)
proc delSon(father: PNode, idx: int) = proc delSon*(father: PNode, idx: int) =
if isNil(father.sons): return if isNil(father.sons): return
var length = sonsLen(father) var length = sonsLen(father)
for i in countup(idx, length - 2): father.sons[i] = father.sons[i + 1] for i in countup(idx, length - 2): father.sons[i] = father.sons[i + 1]
setLen(father.sons, length - 1) setLen(father.sons, length - 1)
proc copyNode(src: PNode): PNode = proc copyNode*(src: PNode): PNode =
# does not copy its sons! # does not copy its sons!
if src == nil: if src == nil:
return nil return nil
@ -1424,7 +1416,7 @@ proc shallowCopy*(src: PNode): PNode =
of nkStrLit..nkTripleStrLit: result.strVal = src.strVal of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
else: newSeq(result.sons, sonsLen(src)) else: newSeq(result.sons, sonsLen(src))
proc copyTree(src: PNode): PNode = proc copyTree*(src: PNode): PNode =
# copy a whole syntax tree; performs deep copying # copy a whole syntax tree; performs deep copying
if src == nil: if src == nil:
return nil return nil
@ -1446,13 +1438,7 @@ proc copyTree(src: PNode): PNode =
for i in countup(0, sonsLen(src) - 1): for i in countup(0, sonsLen(src) - 1):
result.sons[i] = copyTree(src.sons[i]) result.sons[i] = copyTree(src.sons[i])
proc lastSon(n: PNode): PNode = proc hasSonWith*(n: PNode, kind: TNodeKind): bool =
result = n.sons[sonsLen(n) - 1]
proc lastSon(n: PType): PType =
result = n.sons[sonsLen(n) - 1]
proc hasSonWith(n: PNode, kind: TNodeKind): bool =
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if n.sons[i].kind == kind: if n.sons[i].kind == kind:
return true return true
@ -1474,7 +1460,7 @@ proc containsNode*(n: PNode, kinds: TNodeKinds): bool =
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if n.kind in kinds or containsNode(n.sons[i], kinds): return true if n.kind in kinds or containsNode(n.sons[i], kinds): return true
proc hasSubnodeWith(n: PNode, kind: TNodeKind): bool = proc hasSubnodeWith*(n: PNode, kind: TNodeKind): bool =
case n.kind case n.kind
of nkEmpty..nkNilLit: result = n.kind == kind of nkEmpty..nkNilLit: result = n.kind == kind
else: else:
@ -1517,7 +1503,7 @@ proc getStr*(a: PNode): string =
proc getStrOrChar*(a: PNode): string = proc getStrOrChar*(a: PNode): string =
case a.kind case a.kind
of nkStrLit..nkTripleStrLit: result = a.strVal of nkStrLit..nkTripleStrLit: result = a.strVal
of nkCharLit: result = $chr(int(a.intVal)) of nkCharLit..nkUInt64Lit: result = $chr(int(a.intVal))
else: else:
internalError(a.info, "getStrOrChar") internalError(a.info, "getStrOrChar")
result = "" result = ""
@ -1566,3 +1552,11 @@ proc makeStmtList*(n: PNode): PNode =
else: else:
result = newNodeI(nkStmtList, n.info) result = newNodeI(nkStmtList, n.info)
result.add n result.add n
proc createMagic*(name: string, m: TMagic): PSym =
result = newSym(skProc, getIdent(name), nil, unknownLineInfo())
result.magic = m
let
opNot* = createMagic("not", mNot)
opContains* = createMagic("contains", mInSet)

View file

@ -15,13 +15,13 @@ import
ast, hashes, intsets, strutils, options, msgs, ropes, idents, rodutils ast, hashes, intsets, strutils, options, msgs, ropes, idents, rodutils
proc hashNode*(p: RootRef): THash proc hashNode*(p: RootRef): THash
proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): PRope proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
# Convert a tree into its YAML representation; this is used by the # Convert a tree into its YAML representation; this is used by the
# YAML code generator and it is invaluable for debugging purposes. # YAML code generator and it is invaluable for debugging purposes.
# If maxRecDepht <> -1 then it won't print the whole graph. # If maxRecDepht <> -1 then it won't print the whole graph.
proc typeToYaml*(n: PType, indent: int = 0, maxRecDepth: int = - 1): PRope proc typeToYaml*(n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope
proc symToYaml*(n: PSym, indent: int = 0, maxRecDepth: int = - 1): PRope proc symToYaml*(n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope
proc lineInfoToStr*(info: TLineInfo): PRope proc lineInfoToStr*(info: TLineInfo): Rope
# ----------------------- node sets: --------------------------------------- # ----------------------- node sets: ---------------------------------------
proc objectSetContains*(t: TObjectSet, obj: RootRef): bool proc objectSetContains*(t: TObjectSet, obj: RootRef): bool
@ -130,8 +130,8 @@ proc skipConvAndClosure*(n: PNode): PNode =
proc sameValue*(a, b: PNode): bool = proc sameValue*(a, b: PNode): bool =
result = false result = false
case a.kind case a.kind
of nkCharLit..nkInt64Lit: of nkCharLit..nkUInt64Lit:
if b.kind in {nkCharLit..nkInt64Lit}: result = a.intVal == b.intVal if b.kind in {nkCharLit..nkUInt64Lit}: result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit: of nkFloatLit..nkFloat64Lit:
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal == b.floatVal if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
@ -145,8 +145,8 @@ proc leValue*(a, b: PNode): bool =
# a <= b? # a <= b?
result = false result = false
case a.kind case a.kind
of nkCharLit..nkInt64Lit: of nkCharLit..nkUInt32Lit:
if b.kind in {nkCharLit..nkInt64Lit}: result = a.intVal <= b.intVal if b.kind in {nkCharLit..nkUInt32Lit}: result = a.intVal <= b.intVal
of nkFloatLit..nkFloat64Lit: of nkFloatLit..nkFloat64Lit:
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
@ -203,9 +203,9 @@ proc mustRehash(length, counter: int): bool =
assert(length > counter) assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4) result = (length * 2 < counter * 3) or (length - counter < 4)
proc spaces(x: int): PRope = proc rspaces(x: int): Rope =
# returns x spaces # returns x spaces
result = toRope(repeatChar(x)) result = rope(spaces(x))
proc toYamlChar(c: char): string = proc toYamlChar(c: char): string =
case c case c
@ -213,7 +213,7 @@ proc toYamlChar(c: char): string =
of '\'', '\"', '\\': result = '\\' & c of '\'', '\"', '\\': result = '\\' & c
else: result = $c else: result = $c
proc makeYamlString*(s: string): PRope = proc makeYamlString*(s: string): Rope =
# We have to split long strings into many ropes. Otherwise # We have to split long strings into many ropes. Otherwise
# this could trigger InternalError(111). See the ropes module for # this could trigger InternalError(111). See the ropes module for
# further information. # further information.
@ -224,242 +224,244 @@ proc makeYamlString*(s: string): PRope =
if (i + 1) mod MaxLineLength == 0: if (i + 1) mod MaxLineLength == 0:
add(res, '\"') add(res, '\"')
add(res, "\n") add(res, "\n")
app(result, toRope(res)) add(result, rope(res))
res = "\"" # reset res = "\"" # reset
add(res, toYamlChar(s[i])) add(res, toYamlChar(s[i]))
add(res, '\"') add(res, '\"')
app(result, toRope(res)) add(result, rope(res))
proc flagsToStr[T](flags: set[T]): PRope = proc flagsToStr[T](flags: set[T]): Rope =
if flags == {}: if flags == {}:
result = toRope("[]") result = rope("[]")
else: else:
result = nil result = nil
for x in items(flags): for x in items(flags):
if result != nil: app(result, ", ") if result != nil: add(result, ", ")
app(result, makeYamlString($x)) add(result, makeYamlString($x))
result = con("[", con(result, "]")) result = "[" & result & "]"
proc lineInfoToStr(info: TLineInfo): PRope = proc lineInfoToStr(info: TLineInfo): Rope =
result = ropef("[$1, $2, $3]", [makeYamlString(toFilename(info)), result = "[$1, $2, $3]" % [makeYamlString(toFilename(info)),
toRope(toLinenumber(info)), rope(toLinenumber(info)),
toRope(toColumn(info))]) rope(toColumn(info))]
proc treeToYamlAux(n: PNode, marker: var IntSet, proc treeToYamlAux(n: PNode, marker: var IntSet,
indent, maxRecDepth: int): PRope indent, maxRecDepth: int): Rope
proc symToYamlAux(n: PSym, marker: var IntSet, proc symToYamlAux(n: PSym, marker: var IntSet,
indent, maxRecDepth: int): PRope indent, maxRecDepth: int): Rope
proc typeToYamlAux(n: PType, marker: var IntSet, proc typeToYamlAux(n: PType, marker: var IntSet,
indent, maxRecDepth: int): PRope indent, maxRecDepth: int): Rope
proc strTableToYaml(n: TStrTable, marker: var IntSet, indent: int, proc strTableToYaml(n: TStrTable, marker: var IntSet, indent: int,
maxRecDepth: int): PRope = maxRecDepth: int): Rope =
var istr = spaces(indent + 2) var istr = rspaces(indent + 2)
result = toRope("[") result = rope("[")
var mycount = 0 var mycount = 0
for i in countup(0, high(n.data)): for i in countup(0, high(n.data)):
if n.data[i] != nil: if n.data[i] != nil:
if mycount > 0: app(result, ",") if mycount > 0: add(result, ",")
appf(result, "$N$1$2", addf(result, "$N$1$2",
[istr, symToYamlAux(n.data[i], marker, indent + 2, maxRecDepth - 1)]) [istr, symToYamlAux(n.data[i], marker, indent + 2, maxRecDepth - 1)])
inc(mycount) inc(mycount)
if mycount > 0: appf(result, "$N$1", [spaces(indent)]) if mycount > 0: addf(result, "$N$1", [rspaces(indent)])
app(result, "]") add(result, "]")
assert(mycount == n.counter) assert(mycount == n.counter)
proc ropeConstr(indent: int, c: openArray[PRope]): PRope = proc ropeConstr(indent: int, c: openArray[Rope]): Rope =
# array of (name, value) pairs # array of (name, value) pairs
var istr = spaces(indent + 2) var istr = rspaces(indent + 2)
result = toRope("{") result = rope("{")
var i = 0 var i = 0
while i <= high(c): while i <= high(c):
if i > 0: app(result, ",") if i > 0: add(result, ",")
appf(result, "$N$1\"$2\": $3", [istr, c[i], c[i + 1]]) addf(result, "$N$1\"$2\": $3", [istr, c[i], c[i + 1]])
inc(i, 2) inc(i, 2)
appf(result, "$N$1}", [spaces(indent)]) addf(result, "$N$1}", [rspaces(indent)])
proc symToYamlAux(n: PSym, marker: var IntSet, indent: int, proc symToYamlAux(n: PSym, marker: var IntSet, indent: int,
maxRecDepth: int): PRope = maxRecDepth: int): Rope =
if n == nil: if n == nil:
result = toRope("null") result = rope("null")
elif containsOrIncl(marker, n.id): elif containsOrIncl(marker, n.id):
result = ropef("\"$1 @$2\"", [toRope(n.name.s), toRope( result = "\"$1 @$2\"" % [rope(n.name.s), rope(
strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]) strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]
else: else:
var ast = treeToYamlAux(n.ast, marker, indent + 2, maxRecDepth - 1) var ast = treeToYamlAux(n.ast, marker, indent + 2, maxRecDepth - 1)
result = ropeConstr(indent, [toRope("kind"), result = ropeConstr(indent, [rope("kind"),
makeYamlString($n.kind), makeYamlString($n.kind),
toRope("name"), makeYamlString(n.name.s), rope("name"), makeYamlString(n.name.s),
toRope("typ"), typeToYamlAux(n.typ, marker, rope("typ"), typeToYamlAux(n.typ, marker,
indent + 2, maxRecDepth - 1), indent + 2, maxRecDepth - 1),
toRope("info"), lineInfoToStr(n.info), rope("info"), lineInfoToStr(n.info),
toRope("flags"), flagsToStr(n.flags), rope("flags"), flagsToStr(n.flags),
toRope("magic"), makeYamlString($n.magic), rope("magic"), makeYamlString($n.magic),
toRope("ast"), ast, toRope("options"), rope("ast"), ast, rope("options"),
flagsToStr(n.options), toRope("position"), flagsToStr(n.options), rope("position"),
toRope(n.position)]) rope(n.position)])
proc typeToYamlAux(n: PType, marker: var IntSet, indent: int, proc typeToYamlAux(n: PType, marker: var IntSet, indent: int,
maxRecDepth: int): PRope = maxRecDepth: int): Rope =
if n == nil: if n == nil:
result = toRope("null") result = rope("null")
elif containsOrIncl(marker, n.id): elif containsOrIncl(marker, n.id):
result = ropef("\"$1 @$2\"", [toRope($n.kind), toRope( result = "\"$1 @$2\"" % [rope($n.kind), rope(
strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]) strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]
else: else:
if sonsLen(n) > 0: if sonsLen(n) > 0:
result = toRope("[") result = rope("[")
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ",") if i > 0: add(result, ",")
appf(result, "$N$1$2", [spaces(indent + 4), typeToYamlAux(n.sons[i], addf(result, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(n.sons[i],
marker, indent + 4, maxRecDepth - 1)]) marker, indent + 4, maxRecDepth - 1)])
appf(result, "$N$1]", [spaces(indent + 2)]) addf(result, "$N$1]", [rspaces(indent + 2)])
else: else:
result = toRope("null") result = rope("null")
result = ropeConstr(indent, [toRope("kind"), result = ropeConstr(indent, [rope("kind"),
makeYamlString($n.kind), makeYamlString($n.kind),
toRope("sym"), symToYamlAux(n.sym, marker, rope("sym"), symToYamlAux(n.sym, marker,
indent + 2, maxRecDepth - 1), toRope("n"), treeToYamlAux(n.n, marker, indent + 2, maxRecDepth - 1), rope("n"), treeToYamlAux(n.n, marker,
indent + 2, maxRecDepth - 1), toRope("flags"), flagsToStr(n.flags), indent + 2, maxRecDepth - 1), rope("flags"), flagsToStr(n.flags),
toRope("callconv"), rope("callconv"),
makeYamlString(CallingConvToStr[n.callConv]), makeYamlString(CallingConvToStr[n.callConv]),
toRope("size"), toRope(n.size), rope("size"), rope(n.size),
toRope("align"), toRope(n.align), rope("align"), rope(n.align),
toRope("sons"), result]) rope("sons"), result])
proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int, proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int,
maxRecDepth: int): PRope = maxRecDepth: int): Rope =
if n == nil: if n == nil:
result = toRope("null") result = rope("null")
else: else:
var istr = spaces(indent + 2) var istr = rspaces(indent + 2)
result = ropef("{$N$1\"kind\": $2", [istr, makeYamlString($n.kind)]) result = "{$N$1\"kind\": $2" % [istr, makeYamlString($n.kind)]
if maxRecDepth != 0: if maxRecDepth != 0:
appf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)]) addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
case n.kind case n.kind
of nkCharLit..nkInt64Lit: of nkCharLit..nkInt64Lit:
appf(result, ",$N$1\"intVal\": $2", [istr, toRope(n.intVal)]) addf(result, ",$N$1\"intVal\": $2", [istr, rope(n.intVal)])
of nkFloatLit, nkFloat32Lit, nkFloat64Lit: of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
appf(result, ",$N$1\"floatVal\": $2", addf(result, ",$N$1\"floatVal\": $2",
[istr, toRope(n.floatVal.toStrMaxPrecision)]) [istr, rope(n.floatVal.toStrMaxPrecision)])
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
if n.strVal.isNil: if n.strVal.isNil:
appf(result, ",$N$1\"strVal\": null", [istr]) addf(result, ",$N$1\"strVal\": null", [istr])
else: else:
appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
of nkSym: of nkSym:
appf(result, ",$N$1\"sym\": $2", addf(result, ",$N$1\"sym\": $2",
[istr, symToYamlAux(n.sym, marker, indent + 2, maxRecDepth)]) [istr, symToYamlAux(n.sym, marker, indent + 2, maxRecDepth)])
of nkIdent: of nkIdent:
if n.ident != nil: if n.ident != nil:
appf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)]) addf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
else: else:
appf(result, ",$N$1\"ident\": null", [istr]) addf(result, ",$N$1\"ident\": null", [istr])
else: else:
if sonsLen(n) > 0: if sonsLen(n) > 0:
appf(result, ",$N$1\"sons\": [", [istr]) addf(result, ",$N$1\"sons\": [", [istr])
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ",") if i > 0: add(result, ",")
appf(result, "$N$1$2", [spaces(indent + 4), treeToYamlAux(n.sons[i], addf(result, "$N$1$2", [rspaces(indent + 4), treeToYamlAux(n.sons[i],
marker, indent + 4, maxRecDepth - 1)]) marker, indent + 4, maxRecDepth - 1)])
appf(result, "$N$1]", [istr]) addf(result, "$N$1]", [istr])
appf(result, ",$N$1\"typ\": $2", addf(result, ",$N$1\"typ\": $2",
[istr, typeToYamlAux(n.typ, marker, indent + 2, maxRecDepth)]) [istr, typeToYamlAux(n.typ, marker, indent + 2, maxRecDepth)])
appf(result, "$N$1}", [spaces(indent)]) addf(result, "$N$1}", [rspaces(indent)])
proc treeToYaml(n: PNode, indent: int = 0, maxRecDepth: int = - 1): PRope = proc treeToYaml(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope =
var marker = initIntSet() var marker = initIntSet()
result = treeToYamlAux(n, marker, indent, maxRecDepth) result = treeToYamlAux(n, marker, indent, maxRecDepth)
proc typeToYaml(n: PType, indent: int = 0, maxRecDepth: int = - 1): PRope = proc typeToYaml(n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope =
var marker = initIntSet() var marker = initIntSet()
result = typeToYamlAux(n, marker, indent, maxRecDepth) result = typeToYamlAux(n, marker, indent, maxRecDepth)
proc symToYaml(n: PSym, indent: int = 0, maxRecDepth: int = - 1): PRope = proc symToYaml(n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope =
var marker = initIntSet() var marker = initIntSet()
result = symToYamlAux(n, marker, indent, maxRecDepth) result = symToYamlAux(n, marker, indent, maxRecDepth)
proc debugTree(n: PNode, indent: int, maxRecDepth: int; renderType=false): PRope proc debugTree*(n: PNode, indent: int, maxRecDepth: int; renderType=false): Rope
proc debugType(n: PType, maxRecDepth=100): PRope = proc debugType(n: PType, maxRecDepth=100): Rope =
if n == nil: if n == nil:
result = toRope("null") result = rope("null")
else: else:
result = toRope($n.kind) result = rope($n.kind)
if n.sym != nil: if n.sym != nil:
app(result, " ") add(result, " ")
app(result, n.sym.name.s) add(result, n.sym.name.s)
if n.kind in IntegralTypes and n.n != nil:
add(result, ", node: ")
add(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true))
if (n.kind != tyString) and (sonsLen(n) > 0) and maxRecDepth != 0: if (n.kind != tyString) and (sonsLen(n) > 0) and maxRecDepth != 0:
app(result, "(") add(result, "(")
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ", ") if i > 0: add(result, ", ")
if n.sons[i] == nil: if n.sons[i] == nil:
app(result, "null") add(result, "null")
else: else:
app(result, debugType(n.sons[i], maxRecDepth-1)) add(result, debugType(n.sons[i], maxRecDepth-1))
if n.kind == tyObject and n.n != nil: if n.kind == tyObject and n.n != nil:
app(result, ", node: ") add(result, ", node: ")
app(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true)) add(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true))
app(result, ")") add(result, ")")
proc debugTree(n: PNode, indent: int, maxRecDepth: int; proc debugTree(n: PNode, indent: int, maxRecDepth: int;
renderType=false): PRope = renderType=false): Rope =
if n == nil: if n == nil:
result = toRope("null") result = rope("null")
else: else:
var istr = spaces(indent + 2) var istr = rspaces(indent + 2)
result = ropef("{$N$1\"kind\": $2", result = "{$N$1\"kind\": $2" %
[istr, makeYamlString($n.kind)]) [istr, makeYamlString($n.kind)]
if maxRecDepth != 0: if maxRecDepth != 0:
case n.kind case n.kind
of nkCharLit..nkUInt64Lit: of nkCharLit..nkUInt64Lit:
appf(result, ",$N$1\"intVal\": $2", [istr, toRope(n.intVal)]) addf(result, ",$N$1\"intVal\": $2", [istr, rope(n.intVal)])
of nkFloatLit, nkFloat32Lit, nkFloat64Lit: of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
appf(result, ",$N$1\"floatVal\": $2", addf(result, ",$N$1\"floatVal\": $2",
[istr, toRope(n.floatVal.toStrMaxPrecision)]) [istr, rope(n.floatVal.toStrMaxPrecision)])
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
if n.strVal.isNil: if n.strVal.isNil:
appf(result, ",$N$1\"strVal\": null", [istr]) addf(result, ",$N$1\"strVal\": null", [istr])
else: else:
appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
of nkSym: of nkSym:
appf(result, ",$N$1\"sym\": $2_$3", addf(result, ",$N$1\"sym\": $2_$3",
[istr, toRope(n.sym.name.s), toRope(n.sym.id)]) [istr, rope(n.sym.name.s), rope(n.sym.id)])
# [istr, symToYaml(n.sym, indent, maxRecDepth), # [istr, symToYaml(n.sym, indent, maxRecDepth),
# toRope(n.sym.id)]) # rope(n.sym.id)])
if renderType and n.sym.typ != nil: if renderType and n.sym.typ != nil:
appf(result, ",$N$1\"typ\": $2", [istr, debugType(n.sym.typ, 2)]) addf(result, ",$N$1\"typ\": $2", [istr, debugType(n.sym.typ, 2)])
of nkIdent: of nkIdent:
if n.ident != nil: if n.ident != nil:
appf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)]) addf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
else: else:
appf(result, ",$N$1\"ident\": null", [istr]) addf(result, ",$N$1\"ident\": null", [istr])
else: else:
if sonsLen(n) > 0: if sonsLen(n) > 0:
appf(result, ",$N$1\"sons\": [", [istr]) addf(result, ",$N$1\"sons\": [", [istr])
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ",") if i > 0: add(result, ",")
appf(result, "$N$1$2", [spaces(indent + 4), debugTree(n.sons[i], addf(result, "$N$1$2", [rspaces(indent + 4), debugTree(n.sons[i],
indent + 4, maxRecDepth - 1, renderType)]) indent + 4, maxRecDepth - 1, renderType)])
appf(result, "$N$1]", [istr]) addf(result, "$N$1]", [istr])
appf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)]) addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
appf(result, "$N$1}", [spaces(indent)]) addf(result, "$N$1}", [rspaces(indent)])
proc debug(n: PSym) = proc debug(n: PSym) =
if n == nil: if n == nil:
writeln(stdout, "null") msgWriteln("null")
elif n.kind == skUnknown: elif n.kind == skUnknown:
writeln(stdout, "skUnknown") msgWriteln("skUnknown")
else: else:
#writeln(stdout, ropeToStr(symToYaml(n, 0, 1))) #writeln(stdout, $symToYaml(n, 0, 1))
writeln(stdout, "$1_$2: $3, $4, $5, $6" % [ msgWriteln("$1_$2: $3, $4, $5, $6" % [
n.name.s, $n.id, flagsToStr(n.flags).ropeToStr, n.name.s, $n.id, $flagsToStr(n.flags), $flagsToStr(n.loc.flags),
flagsToStr(n.loc.flags).ropeToStr, lineInfoToStr(n.info).ropeToStr, $lineInfoToStr(n.info), $n.kind])
$n.kind])
proc debug(n: PType) = proc debug(n: PType) =
writeln(stdout, ropeToStr(debugType(n))) msgWriteln($debugType(n))
proc debug(n: PNode) = proc debug(n: PNode) =
writeln(stdout, ropeToStr(debugTree(n, 0, 100))) msgWriteln($debugTree(n, 0, 100))
const const
EmptySeq = @[] EmptySeq = @[]
@ -678,9 +680,8 @@ proc initIdentIter(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym =
else: result = nextIdentIter(ti, tab) else: result = nextIdentIter(ti, tab)
proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym = proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym =
var h, start: THash var h = ti.h and high(tab.data)
h = ti.h and high(tab.data) var start = h
start = h
result = tab.data[h] result = tab.data[h]
while result != nil: while result != nil:
if result.name.id == ti.name.id: break if result.name.id == ti.name.id: break

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -119,8 +119,8 @@ proc hashType(c: var MD5Context, t: PType) =
c.hashSym(t.sym) c.hashSym(t.sym)
case t.kind case t.kind
of tyGenericBody, tyGenericInst, tyGenericInvokation: of tyGenericBody, tyGenericInst, tyGenericInvocation:
for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvokation)): for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvocation)):
c.hashType t.sons[i] c.hashType t.sons[i]
of tyUserTypeClass: of tyUserTypeClass:
internalAssert t.sym != nil and t.sym.owner != nil internalAssert t.sym != nil and t.sym.owner != nil
@ -259,7 +259,7 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
pushType(w, loc.t) pushType(w, loc.t)
if loc.r != nil: if loc.r != nil:
add(result, '!') add(result, '!')
encodeStr(ropeToStr(loc.r), result) encodeStr($loc.r, result)
if loc.a != 0: if loc.a != 0:
add(result, '?') add(result, '?')
encodeVInt(loc.a, result) encodeVInt(loc.a, result)
@ -317,7 +317,7 @@ proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
add(result, '|') add(result, '|')
encodeVInt(ord(lib.kind), result) encodeVInt(ord(lib.kind), result)
add(result, '|') add(result, '|')
encodeStr(ropeToStr(lib.name), result) encodeStr($lib.name, result)
add(result, '|') add(result, '|')
encodeNode(w, info, lib.path, result) encodeNode(w, info, lib.path, result)

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2013 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -19,13 +19,13 @@ proc hasNoInit(call: PNode): bool {.inline.} =
result = call.sons[0].kind == nkSym and sfNoInit in call.sons[0].sym.flags result = call.sons[0].kind == nkSym and sfNoInit in call.sons[0].sym.flags
proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
callee, params: PRope) = callee, params: Rope) =
var pl = con(callee, ~"(", params) var pl = callee & ~"(" & params
# getUniqueType() is too expensive here: # getUniqueType() is too expensive here:
var typ = skipTypes(ri.sons[0].typ, abstractInst) var typ = skipTypes(ri.sons[0].typ, abstractInst)
if typ.sons[0] != nil: if typ.sons[0] != nil:
if isInvalidReturnType(typ.sons[0]): if isInvalidReturnType(typ.sons[0]):
if params != nil: pl.app(~", ") if params != nil: pl.add(~", ")
# beware of 'result = p(result)'. We may need to allocate a temporary: # beware of 'result = p(result)'. We may need to allocate a temporary:
if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri): if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
# Great, we can use 'd': # Great, we can use 'd':
@ -33,26 +33,34 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
elif d.k notin {locExpr, locTemp} and not hasNoInit(ri): elif d.k notin {locExpr, locTemp} and not hasNoInit(ri):
# reset before pass as 'result' var: # reset before pass as 'result' var:
resetLoc(p, d) resetLoc(p, d)
app(pl, addrLoc(d)) add(pl, addrLoc(d))
app(pl, ~");$n") add(pl, ~");$n")
line(p, cpsStmts, pl) line(p, cpsStmts, pl)
else: else:
var tmp: TLoc var tmp: TLoc
getTemp(p, typ.sons[0], tmp, needsInit=true) getTemp(p, typ.sons[0], tmp, needsInit=true)
app(pl, addrLoc(tmp)) add(pl, addrLoc(tmp))
app(pl, ~");$n") add(pl, ~");$n")
line(p, cpsStmts, pl) line(p, cpsStmts, pl)
genAssignment(p, d, tmp, {}) # no need for deep copying genAssignment(p, d, tmp, {}) # no need for deep copying
else: else:
app(pl, ~")") add(pl, ~")")
if d.k == locNone: getTemp(p, typ.sons[0], d) if p.module.compileToCpp and lfSingleUse in d.flags:
assert(d.t != nil) # generate an assignment to d: # do not generate spurious temporaries for C++! For C we're better off
var list: TLoc # with them to prevent undefined behaviour and because the codegen
initLoc(list, locCall, d.t, OnUnknown) # is free to emit expressions multiple times!
list.r = pl d.k = locCall
genAssignment(p, d, list, {}) # no need for deep copying d.r = pl
excl d.flags, lfSingleUse
else:
if d.k == locNone: getTemp(p, typ.sons[0], d)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc
initLoc(list, locCall, d.t, OnUnknown)
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
else: else:
app(pl, ~");$n") add(pl, ~");$n")
line(p, cpsStmts, pl) line(p, cpsStmts, pl)
proc isInCurrentFrame(p: BProc, n: PNode): bool = proc isInCurrentFrame(p: BProc, n: PNode): bool =
@ -75,7 +83,7 @@ proc isInCurrentFrame(p: BProc, n: PNode): bool =
result = isInCurrentFrame(p, n.sons[0]) result = isInCurrentFrame(p, n.sons[0])
else: discard else: discard
proc openArrayLoc(p: BProc, n: PNode): PRope = proc openArrayLoc(p: BProc, n: PNode): Rope =
var a: TLoc var a: TLoc
let q = skipConv(n) let q = skipConv(n)
@ -90,33 +98,34 @@ proc openArrayLoc(p: BProc, n: PNode): PRope =
of tyOpenArray, tyVarargs, tyArray, tyArrayConstr: of tyOpenArray, tyVarargs, tyArray, tyArrayConstr:
"($1)+($2), ($3)-($2)+1" "($1)+($2), ($3)-($2)+1"
of tyString, tySequence: of tyString, tySequence:
if skipTypes(n.typ, abstractInst).kind == tyVar: if skipTypes(n.typ, abstractInst).kind == tyVar and
not compileToCpp(p.module):
"(*$1)->data+($2), ($3)-($2)+1" "(*$1)->data+($2), ($3)-($2)+1"
else: else:
"$1->data+($2), ($3)-($2)+1" "$1->data+($2), ($3)-($2)+1"
else: (internalError("openArrayLoc: " & typeToString(a.t)); "") else: (internalError("openArrayLoc: " & typeToString(a.t)); "")
result = ropef(fmt, [rdLoc(a), rdLoc(b), rdLoc(c)]) result = fmt % [rdLoc(a), rdLoc(b), rdLoc(c)]
else: else:
initLocExpr(p, n, a) initLocExpr(p, n, a)
case skipTypes(a.t, abstractVar).kind case skipTypes(a.t, abstractVar).kind
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
result = ropef("$1, $1Len0", [rdLoc(a)]) result = "$1, $1Len0" % [rdLoc(a)]
of tyString, tySequence: of tyString, tySequence:
if skipTypes(n.typ, abstractInst).kind == tyVar: if skipTypes(n.typ, abstractInst).kind == tyVar and
result = ropef("(*$1)->data, (*$1)->$2", [a.rdLoc, lenField(p)]) not compileToCpp(p.module):
result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)]
else: else:
result = ropef("$1->data, $1->$2", [a.rdLoc, lenField(p)]) result = "$1->data, $1->$2" % [a.rdLoc, lenField(p)]
of tyArray, tyArrayConstr: of tyArray, tyArrayConstr:
result = ropef("$1, $2", [rdLoc(a), toRope(lengthOrd(a.t))]) result = "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))]
else: internalError("openArrayLoc: " & typeToString(a.t)) else: internalError("openArrayLoc: " & typeToString(a.t))
proc genArgStringToCString(p: BProc, proc genArgStringToCString(p: BProc, n: PNode): Rope {.inline.} =
n: PNode): PRope {.inline.} =
var a: TLoc var a: TLoc
initLocExpr(p, n.sons[0], a) initLocExpr(p, n.sons[0], a)
result = ropef("$1->data", [a.rdLoc]) result = "$1->data" % [a.rdLoc]
proc genArg(p: BProc, n: PNode, param: PSym): PRope = proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope =
var a: TLoc var a: TLoc
if n.kind == nkStringToCString: if n.kind == nkStringToCString:
result = genArgStringToCString(p, n) result = genArgStringToCString(p, n)
@ -126,23 +135,35 @@ proc genArg(p: BProc, n: PNode, param: PSym): PRope =
elif ccgIntroducedPtr(param): elif ccgIntroducedPtr(param):
initLocExpr(p, n, a) initLocExpr(p, n, a)
result = addrLoc(a) result = addrLoc(a)
elif p.module.compileToCpp and param.typ.kind == tyVar and
n.kind == nkHiddenAddr:
initLocExprSingleUse(p, n.sons[0], a)
# if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still
# means '*T'. See posix.nim for lots of examples that do that in the wild.
let callee = call.sons[0]
if callee.kind == nkSym and
{sfImportC, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportC} and
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
result = addrLoc(a)
else:
result = rdLoc(a)
else: else:
initLocExpr(p, n, a) initLocExprSingleUse(p, n, a)
result = rdLoc(a) result = rdLoc(a)
proc genArgNoParam(p: BProc, n: PNode): PRope = proc genArgNoParam(p: BProc, n: PNode): Rope =
var a: TLoc var a: TLoc
if n.kind == nkStringToCString: if n.kind == nkStringToCString:
result = genArgStringToCString(p, n) result = genArgStringToCString(p, n)
else: else:
initLocExpr(p, n, a) initLocExprSingleUse(p, n, a)
result = rdLoc(a) result = rdLoc(a)
proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) = proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var op: TLoc var op: TLoc
# this is a hotspot in the compiler # this is a hotspot in the compiler
initLocExpr(p, ri.sons[0], op) initLocExpr(p, ri.sons[0], op)
var params: PRope var params: Rope
# getUniqueType() is too expensive here: # getUniqueType() is too expensive here:
var typ = skipTypes(ri.sons[0].typ, abstractInst) var typ = skipTypes(ri.sons[0].typ, abstractInst)
assert(typ.kind == tyProc) assert(typ.kind == tyProc)
@ -150,48 +171,49 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var length = sonsLen(ri) var length = sonsLen(ri)
for i in countup(1, length - 1): for i in countup(1, length - 1):
if ri.sons[i].typ.isCompileTimeOnly: continue if ri.sons[i].typ.isCompileTimeOnly: continue
if params != nil: app(params, ~", ") if params != nil: add(params, ~", ")
if i < sonsLen(typ): if i < sonsLen(typ):
assert(typ.n.sons[i].kind == nkSym) assert(typ.n.sons[i].kind == nkSym)
app(params, genArg(p, ri.sons[i], typ.n.sons[i].sym)) add(params, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri))
else: else:
app(params, genArgNoParam(p, ri.sons[i])) add(params, genArgNoParam(p, ri.sons[i]))
fixupCall(p, le, ri, d, op.r, params) fixupCall(p, le, ri, d, op.r, params)
proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
proc getRawProcType(p: BProc, t: PType): PRope = proc getRawProcType(p: BProc, t: PType): Rope =
result = getClosureType(p.module, t, clHalf) result = getClosureType(p.module, t, clHalf)
proc addComma(r: PRope): PRope = proc addComma(r: Rope): Rope =
result = if r == nil: r else: con(r, ~", ") result = if r == nil: r else: r & ~", "
const PatProc = "$1.ClEnv? $1.ClPrc($3$1.ClEnv):(($4)($1.ClPrc))($2)" const PatProc = "$1.ClEnv? $1.ClPrc($3$1.ClEnv):(($4)($1.ClPrc))($2)"
const PatIter = "$1.ClPrc($3$1.ClEnv)" # we know the env exists const PatIter = "$1.ClPrc($3$1.ClEnv)" # we know the env exists
var op: TLoc var op: TLoc
initLocExpr(p, ri.sons[0], op) initLocExpr(p, ri.sons[0], op)
var pl: PRope var pl: Rope
var typ = skipTypes(ri.sons[0].typ, abstractInst) var typ = skipTypes(ri.sons[0].typ, abstractInst)
assert(typ.kind == tyProc) assert(typ.kind == tyProc)
var length = sonsLen(ri) var length = sonsLen(ri)
for i in countup(1, length - 1): for i in countup(1, length - 1):
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
if ri.sons[i].typ.isCompileTimeOnly: continue
if i < sonsLen(typ): if i < sonsLen(typ):
assert(typ.n.sons[i].kind == nkSym) assert(typ.n.sons[i].kind == nkSym)
app(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym)) add(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri))
else: else:
app(pl, genArgNoParam(p, ri.sons[i])) add(pl, genArgNoParam(p, ri.sons[i]))
if i < length - 1: app(pl, ~", ") if i < length - 1: add(pl, ~", ")
template genCallPattern {.dirty.} = template genCallPattern {.dirty.} =
lineF(p, cpsStmts, callPattern & ";$n", op.r, pl, pl.addComma, rawProc) lineF(p, cpsStmts, callPattern & ";$n", [op.r, pl, pl.addComma, rawProc])
let rawProc = getRawProcType(p, typ) let rawProc = getRawProcType(p, typ)
let callPattern = if tfIterator in typ.flags: PatIter else: PatProc let callPattern = if tfIterator in typ.flags: PatIter else: PatProc
if typ.sons[0] != nil: if typ.sons[0] != nil:
if isInvalidReturnType(typ.sons[0]): if isInvalidReturnType(typ.sons[0]):
if sonsLen(ri) > 1: app(pl, ~", ") if sonsLen(ri) > 1: add(pl, ~", ")
# beware of 'result = p(result)'. We may need to allocate a temporary: # beware of 'result = p(result)'. We may need to allocate a temporary:
if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri): if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
# Great, we can use 'd': # Great, we can use 'd':
@ -200,12 +222,12 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
elif d.k notin {locExpr, locTemp} and not hasNoInit(ri): elif d.k notin {locExpr, locTemp} and not hasNoInit(ri):
# reset before pass as 'result' var: # reset before pass as 'result' var:
resetLoc(p, d) resetLoc(p, d)
app(pl, addrLoc(d)) add(pl, addrLoc(d))
genCallPattern() genCallPattern()
else: else:
var tmp: TLoc var tmp: TLoc
getTemp(p, typ.sons[0], tmp, needsInit=true) getTemp(p, typ.sons[0], tmp, needsInit=true)
app(pl, addrLoc(tmp)) add(pl, addrLoc(tmp))
genCallPattern() genCallPattern()
genAssignment(p, d, tmp, {}) # no need for deep copying genAssignment(p, d, tmp, {}) # no need for deep copying
else: else:
@ -213,37 +235,214 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
assert(d.t != nil) # generate an assignment to d: assert(d.t != nil) # generate an assignment to d:
var list: TLoc var list: TLoc
initLoc(list, locCall, d.t, OnUnknown) initLoc(list, locCall, d.t, OnUnknown)
list.r = ropef(callPattern, op.r, pl, pl.addComma, rawProc) list.r = callPattern % [op.r, pl, pl.addComma, rawProc]
genAssignment(p, d, list, {}) # no need for deep copying genAssignment(p, d, list, {}) # no need for deep copying
else: else:
genCallPattern() genCallPattern()
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
if ri.sons[i].typ.isCompileTimeOnly:
result = nil
elif 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:
result = genArgNoParam(p, ri.sons[i][0])
else:
result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym)
else:
result = genArgNoParam(p, ri.sons[i])
discard """
Dot call syntax in C++
======================
so c2nim translates 'this' sometimes to 'T' and sometimes to 'var T'
both of which are wrong, but often more convenient to use.
For manual wrappers it can also be 'ptr T'
Fortunately we know which parameter is the 'this' parameter and so can fix this
mess in the codegen.
now ... if the *argument* is a 'ptr' the codegen shall emit -> and otherwise .
but this only depends on the argument and not on how the 'this' was declared
however how the 'this' was declared affects whether we end up with
wrong 'addr' and '[]' ops...
Since I'm tired I'll enumerate all the cases here:
var
x: ptr T
y: T
proc t(x: T)
x[].t() --> (*x).t() is correct.
y.t() --> y.t() is correct
proc u(x: ptr T)
x.u() --> needs to become x->u()
(addr y).u() --> needs to become y.u()
proc v(x: var T)
--> first skip the implicit 'nkAddr' node
x[].v() --> (*x).v() is correct, but might have been eliminated due
to the nkAddr node! So for this case we need to generate '->'
y.v() --> y.v() is correct
"""
proc skipAddrDeref(node: PNode): PNode =
var n = node
var isAddr = false
case n.kind
of nkAddr, nkHiddenAddr:
n = n.sons[0]
isAddr = true
of nkDerefExpr, nkHiddenDeref:
n = n.sons[0]
else: return n
if n.kind == nkObjDownConv: n = n.sons[0]
if isAddr and n.kind in {nkDerefExpr, nkHiddenDeref}:
result = n.sons[0]
elif n.kind in {nkAddr, nkHiddenAddr}:
result = n.sons[0]
else:
result = node
proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
# for better or worse c2nim translates the 'this' argument to a 'var T'.
# However manual wrappers may also use 'ptr T'. In any case we support both
# for convenience.
internalAssert i < sonsLen(typ)
assert(typ.n.sons[i].kind == nkSym)
# if the parameter is lying (tyVar) and thus we required an additional deref,
# skip the deref:
var ri = ri[i]
while ri.kind == nkObjDownConv: ri = ri[0]
let t = typ.sons[i].skipTypes({tyGenericInst})
if t.kind == tyVar:
let x = if ri.kind == nkHiddenAddr: ri[0] else: ri
if x.typ.kind == tyPtr:
result = genArgNoParam(p, x)
result.add("->")
elif x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].typ.kind == tyPtr:
result = genArgNoParam(p, x[0])
result.add("->")
else:
result = genArgNoParam(p, x)
result.add(".")
elif t.kind == tyPtr:
if ri.kind in {nkAddr, nkHiddenAddr}:
result = genArgNoParam(p, ri[0])
result.add(".")
else:
result = genArgNoParam(p, ri)
result.add("->")
else:
ri = skipAddrDeref(ri)
if ri.kind in {nkAddr, nkHiddenAddr}: ri = ri[0]
result = genArgNoParam(p, ri) #, typ.n.sons[i].sym)
result.add(".")
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope =
var i = 0
var j = 1
while i < pat.len:
case pat[i]
of '@':
if j < ri.len:
result.add genOtherArg(p, ri, j, typ)
for k in j+1 .. < ri.len:
result.add(~", ")
result.add genOtherArg(p, ri, k, typ)
inc i
of '#':
if pat[i+1] in {'+', '@'}:
let ri = ri[j]
if ri.kind in nkCallKinds:
let typ = skipTypes(ri.sons[0].typ, abstractInst)
if pat[i+1] == '+': result.add genArgNoParam(p, ri.sons[0])
result.add(~"(")
if 1 < ri.len:
result.add genOtherArg(p, ri, 1, typ)
for k in j+1 .. < ri.len:
result.add(~", ")
result.add genOtherArg(p, ri, k, typ)
result.add(~")")
else:
localError(ri.info, "call expression expected for C++ pattern")
inc i
elif pat[i+1] == '.':
result.add genThisArg(p, ri, j, typ)
inc i
elif pat[i+1] == '[':
var arg = ri.sons[j].skipAddrDeref
while arg.kind in {nkAddr, nkHiddenAddr, nkObjDownConv}: arg = arg[0]
result.add genArgNoParam(p, arg)
#result.add debugTree(arg, 0, 10)
else:
result.add genOtherArg(p, ri, j, typ)
inc j
inc i
of '\'':
var idx, stars: int
if scanCppGenericSlot(pat, i, idx, stars):
var t = resolveStarsInCppType(typ, idx, stars)
if t == nil: result.add(~"void")
else: result.add(getTypeDesc(p.module, t))
else:
let start = i
while i < pat.len:
if pat[i] notin {'@', '#', '\''}: inc(i)
else: break
if i - 1 >= start:
add(result, substr(pat, start, i - 1))
proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var op, a: TLoc var op, a: TLoc
initLocExpr(p, ri.sons[0], op) initLocExpr(p, ri.sons[0], op)
var pl: PRope = nil
# getUniqueType() is too expensive here: # getUniqueType() is too expensive here:
var typ = skipTypes(ri.sons[0].typ, abstractInst) var typ = skipTypes(ri.sons[0].typ, abstractInst)
assert(typ.kind == tyProc) assert(typ.kind == tyProc)
var length = sonsLen(ri) var length = sonsLen(ri)
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
# don't call '$' here for efficiency:
var param = typ.n.sons[1].sym let pat = ri.sons[0].sym.loc.r.data
app(pl, genArg(p, ri.sons[1], param)) internalAssert pat != nil
if pat.contains({'#', '(', '@', '\''}):
if skipTypes(param.typ, {tyGenericInst}).kind == tyPtr: app(pl, ~"->") var pl = genPatternCall(p, ri, pat, typ)
else: app(pl, ~".") # simpler version of 'fixupCall' that works with the pl+params combination:
app(pl, op.r) var typ = skipTypes(ri.sons[0].typ, abstractInst)
var params: PRope if typ.sons[0] != nil:
for i in countup(2, length - 1): if p.module.compileToCpp and lfSingleUse in d.flags:
if params != nil: params.app(~", ") # do not generate spurious temporaries for C++! For C we're better off
assert(sonsLen(typ) == sonsLen(typ.n)) # with them to prevent undefined behaviour and because the codegen
if i < sonsLen(typ): # is free to emit expressions multiple times!
assert(typ.n.sons[i].kind == nkSym) d.k = locCall
app(params, genArg(p, ri.sons[i], typ.n.sons[i].sym)) d.r = pl
excl d.flags, lfSingleUse
else:
if d.k == locNone: getTemp(p, typ.sons[0], d)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc
initLoc(list, locCall, d.t, OnUnknown)
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
else: else:
app(params, genArgNoParam(p, ri.sons[i])) add(pl, ~";$n")
fixupCall(p, le, ri, d, pl, params) line(p, cpsStmts, pl)
else:
var pl: Rope = nil
#var param = typ.n.sons[1].sym
if 1 < ri.len:
add(pl, genThisArg(p, ri, 1, typ))
add(pl, op.r)
var params: Rope
for i in countup(2, length - 1):
if params != nil: params.add(~", ")
assert(sonsLen(typ) == sonsLen(typ.n))
add(params, genOtherArg(p, ri, i, typ))
fixupCall(p, le, ri, d, pl, params)
proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
# generates a crappy ObjC call # generates a crappy ObjC call
@ -256,43 +455,55 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
var length = sonsLen(ri) var length = sonsLen(ri)
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
if length > 1: # don't call '$' here for efficiency:
app(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym)) let pat = ri.sons[0].sym.loc.r.data
app(pl, ~" ") internalAssert pat != nil
app(pl, op.r) var start = 3
if length > 2: if ' ' in pat:
app(pl, ~": ") start = 1
app(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym)) add(pl, op.r)
for i in countup(3, length-1): if length > 1:
add(pl, ~": ")
add(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri))
start = 2
else:
if length > 1:
add(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri))
add(pl, ~" ")
add(pl, op.r)
if length > 2:
add(pl, ~": ")
add(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym, ri))
for i in countup(start, length-1):
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
if i >= sonsLen(typ): if i >= sonsLen(typ):
internalError(ri.info, "varargs for objective C method?") internalError(ri.info, "varargs for objective C method?")
assert(typ.n.sons[i].kind == nkSym) assert(typ.n.sons[i].kind == nkSym)
var param = typ.n.sons[i].sym var param = typ.n.sons[i].sym
app(pl, ~" ") add(pl, ~" ")
app(pl, param.name.s) add(pl, param.name.s)
app(pl, ~": ") add(pl, ~": ")
app(pl, genArg(p, ri.sons[i], param)) add(pl, genArg(p, ri.sons[i], param, ri))
if typ.sons[0] != nil: if typ.sons[0] != nil:
if isInvalidReturnType(typ.sons[0]): if isInvalidReturnType(typ.sons[0]):
if sonsLen(ri) > 1: app(pl, ~" ") if sonsLen(ri) > 1: add(pl, ~" ")
# beware of 'result = p(result)'. We always allocate a temporary: # beware of 'result = p(result)'. We always allocate a temporary:
if d.k in {locTemp, locNone}: if d.k in {locTemp, locNone}:
# We already got a temp. Great, special case it: # We already got a temp. Great, special case it:
if d.k == locNone: getTemp(p, typ.sons[0], d, needsInit=true) if d.k == locNone: getTemp(p, typ.sons[0], d, needsInit=true)
app(pl, ~"Result: ") add(pl, ~"Result: ")
app(pl, addrLoc(d)) add(pl, addrLoc(d))
app(pl, ~"];$n") add(pl, ~"];$n")
line(p, cpsStmts, pl) line(p, cpsStmts, pl)
else: else:
var tmp: TLoc var tmp: TLoc
getTemp(p, typ.sons[0], tmp, needsInit=true) getTemp(p, typ.sons[0], tmp, needsInit=true)
app(pl, addrLoc(tmp)) add(pl, addrLoc(tmp))
app(pl, ~"];$n") add(pl, ~"];$n")
line(p, cpsStmts, pl) line(p, cpsStmts, pl)
genAssignment(p, d, tmp, {}) # no need for deep copying genAssignment(p, d, tmp, {}) # no need for deep copying
else: else:
app(pl, ~"]") add(pl, ~"]")
if d.k == locNone: getTemp(p, typ.sons[0], d) if d.k == locNone: getTemp(p, typ.sons[0], d)
assert(d.t != nil) # generate an assignment to d: assert(d.t != nil) # generate an assignment to d:
var list: TLoc var list: TLoc
@ -300,14 +511,13 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
list.r = pl list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying genAssignment(p, d, list, {}) # no need for deep copying
else: else:
app(pl, ~"];$n") add(pl, ~"];$n")
line(p, cpsStmts, pl) line(p, cpsStmts, pl)
proc genCall(p: BProc, e: PNode, d: var TLoc) = proc genCall(p: BProc, e: PNode, d: var TLoc) =
if e.sons[0].typ.callConv == ccClosure: if e.sons[0].typ.callConv == ccClosure:
genClosureCall(p, nil, e, d) genClosureCall(p, nil, e, d)
elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags and elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags:
e.len >= 2:
genInfixCall(p, nil, e, d) genInfixCall(p, nil, e, d)
elif e.sons[0].kind == nkSym and sfNamedParamCall in e.sons[0].sym.flags: elif e.sons[0].kind == nkSym and sfNamedParamCall in e.sons[0].sym.flags:
genNamedParamCall(p, e, d) genNamedParamCall(p, e, d)
@ -320,8 +530,7 @@ proc genCall(p: BProc, e: PNode, d: var TLoc) =
proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) = proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
if ri.sons[0].typ.callConv == ccClosure: if ri.sons[0].typ.callConv == ccClosure:
genClosureCall(p, le, ri, d) genClosureCall(p, le, ri, d)
elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags and elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags:
ri.len >= 2:
genInfixCall(p, le, ri, d) genInfixCall(p, le, ri, d)
elif ri.sons[0].kind == nkSym and sfNamedParamCall in ri.sons[0].sym.flags: elif ri.sons[0].kind == nkSym and sfNamedParamCall in ri.sons[0].sym.flags:
genNamedParamCall(p, ri, d) genNamedParamCall(p, ri, d)

File diff suppressed because it is too large Load diff

View file

@ -45,29 +45,29 @@ const
] ]
NimMergeEndMark = "/*\tNIM_merge_END:*/" NimMergeEndMark = "/*\tNIM_merge_END:*/"
proc genSectionStart*(fs: TCFileSection): PRope = proc genSectionStart*(fs: TCFileSection): Rope =
if compilationCachePresent: if compilationCachePresent:
result = toRope(tnl) result = rope(tnl)
app(result, "/*\t") add(result, "/*\t")
app(result, CFileSectionNames[fs]) add(result, CFileSectionNames[fs])
app(result, ":*/") add(result, ":*/")
app(result, tnl) add(result, tnl)
proc genSectionEnd*(fs: TCFileSection): PRope = proc genSectionEnd*(fs: TCFileSection): Rope =
if compilationCachePresent: if compilationCachePresent:
result = toRope(NimMergeEndMark & tnl) result = rope(NimMergeEndMark & tnl)
proc genSectionStart*(ps: TCProcSection): PRope = proc genSectionStart*(ps: TCProcSection): Rope =
if compilationCachePresent: if compilationCachePresent:
result = toRope(tnl) result = rope(tnl)
app(result, "/*\t") add(result, "/*\t")
app(result, CProcSectionNames[ps]) add(result, CProcSectionNames[ps])
app(result, ":*/") add(result, ":*/")
app(result, tnl) add(result, tnl)
proc genSectionEnd*(ps: TCProcSection): PRope = proc genSectionEnd*(ps: TCProcSection): Rope =
if compilationCachePresent: if compilationCachePresent:
result = toRope(NimMergeEndMark & tnl) result = rope(NimMergeEndMark & tnl)
proc writeTypeCache(a: TIdTable, s: var string) = proc writeTypeCache(a: TIdTable, s: var string) =
var i = 0 var i = 0
@ -79,7 +79,7 @@ proc writeTypeCache(a: TIdTable, s: var string) =
s.add(' ') s.add(' ')
encodeVInt(id, s) encodeVInt(id, s)
s.add(':') s.add(':')
encodeStr(PRope(value).ropeToStr, s) encodeStr($Rope(value), s)
inc i inc i
s.add('}') s.add('}')
@ -95,7 +95,7 @@ proc writeIntSet(a: IntSet, s: var string) =
inc i inc i
s.add('}') s.add('}')
proc genMergeInfo*(m: BModule): PRope = proc genMergeInfo*(m: BModule): Rope =
if optSymbolFiles notin gGlobalOptions: return nil if optSymbolFiles notin gGlobalOptions: return nil
var s = "/*\tNIM_merge_INFO:" var s = "/*\tNIM_merge_INFO:"
s.add(tnl) s.add(tnl)
@ -111,9 +111,9 @@ proc genMergeInfo*(m: BModule): PRope =
encodeVInt(ord(m.frameDeclared), s) encodeVInt(ord(m.frameDeclared), s)
s.add(tnl) s.add(tnl)
s.add("*/") s.add("*/")
result = s.toRope result = s.rope
template `^`(pos: expr): expr = L.buf[pos] template `^`(pos: int): expr = L.buf[pos]
proc skipWhite(L: var TBaseLexer) = proc skipWhite(L: var TBaseLexer) =
var pos = L.bufpos var pos = L.bufpos
@ -145,7 +145,7 @@ proc atEndMark(buf: cstring, pos: int): bool =
while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
result = s == NimMergeEndMark.len result = s == NimMergeEndMark.len
proc readVerbatimSection(L: var TBaseLexer): PRope = proc readVerbatimSection(L: var TBaseLexer): Rope =
var pos = L.bufpos var pos = L.bufpos
var buf = L.buf var buf = L.buf
var r = newStringOfCap(30_000) var r = newStringOfCap(30_000)
@ -169,7 +169,7 @@ proc readVerbatimSection(L: var TBaseLexer): PRope =
r.add(buf[pos]) r.add(buf[pos])
inc pos inc pos
L.bufpos = pos L.bufpos = pos
result = r.toRope result = r.rope
proc readKey(L: var TBaseLexer, result: var string) = proc readKey(L: var TBaseLexer, result: var string) =
var pos = L.bufpos var pos = L.bufpos
@ -197,7 +197,7 @@ proc readTypeCache(L: var TBaseLexer, result: var TIdTable) =
# XXX little hack: we create a "fake" type object with the correct Id # XXX little hack: we create a "fake" type object with the correct Id
# better would be to adapt the data structure to not even store the # better would be to adapt the data structure to not even store the
# object as key, but only the Id # object as key, but only the Id
idTablePut(result, newFakeType(key), value.toRope) idTablePut(result, newFakeType(key), value.rope)
inc L.bufpos inc L.bufpos
proc readIntSet(L: var TBaseLexer, result: var IntSet) = proc readIntSet(L: var TBaseLexer, result: var IntSet) =
@ -223,7 +223,7 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) =
of "typeInfo": readIntSet(L, m.typeInfoMarker) of "typeInfo": readIntSet(L, m.typeInfoMarker)
of "labels": m.labels = decodeVInt(L.buf, L.bufpos) of "labels": m.labels = decodeVInt(L.buf, L.bufpos)
of "hasframe": m.frameDeclared = decodeVInt(L.buf, L.bufpos) != 0 of "hasframe": m.frameDeclared = decodeVInt(L.buf, L.bufpos) != 0
else: internalError("ccgmerge: unkown key: " & k) else: internalError("ccgmerge: unknown key: " & k)
when not defined(nimhygiene): when not defined(nimhygiene):
{.pragma: inject.} {.pragma: inject.}
@ -280,11 +280,11 @@ proc readMergeSections(cfilename: string, m: var TMergeSections) =
proc mergeRequired*(m: BModule): bool = proc mergeRequired*(m: BModule): bool =
for i in cfsHeaders..cfsProcs: for i in cfsHeaders..cfsProcs:
if m.s[i] != nil: if m.s[i] != nil:
#echo "not empty: ", i, " ", ropeToStr(m.s[i]) #echo "not empty: ", i, " ", m.s[i]
return true return true
for i in low(TCProcSection)..high(TCProcSection): for i in low(TCProcSection)..high(TCProcSection):
if m.initProc.s(i) != nil: if m.initProc.s(i) != nil:
#echo "not empty: ", i, " ", ropeToStr(m.initProc.s[i]) #echo "not empty: ", i, " ", m.initProc.s[i]
return true return true
proc mergeFiles*(cfilename: string, m: BModule) = proc mergeFiles*(cfilename: string, m: BModule) =
@ -293,6 +293,6 @@ proc mergeFiles*(cfilename: string, m: BModule) =
readMergeSections(cfilename, old) readMergeSections(cfilename, old)
# do the merge; old section before new section: # do the merge; old section before new section:
for i in low(TCFileSection)..high(TCFileSection): for i in low(TCFileSection)..high(TCFileSection):
m.s[i] = con(old.f[i], m.s[i]) m.s[i] = old.f[i] & m.s[i]
for i in low(TCProcSection)..high(TCProcSection): for i in low(TCProcSection)..high(TCProcSection):
m.initProc.s(i) = con(old.p[i], m.initProc.s(i)) m.initProc.s(i) = old.p[i] & m.initProc.s(i)

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -48,7 +48,7 @@ proc genVarTuple(p: BProc, n: PNode) =
return return
genLineDir(p, n) genLineDir(p, n)
initLocExpr(p, n.sons[L-1], tup) initLocExpr(p, n.sons[L-1], tup)
var t = tup.t var t = tup.t.getUniqueType
for i in countup(0, L-3): for i in countup(0, L-3):
var v = n.sons[i].sym var v = n.sons[i].sym
if sfCompileTime in v.flags: continue if sfCompileTime in v.flags: continue
@ -61,11 +61,10 @@ proc genVarTuple(p: BProc, n: PNode) =
initLocalVar(p, v, immediateAsgn=isAssignedImmediately(n[L-1])) initLocalVar(p, v, immediateAsgn=isAssignedImmediately(n[L-1]))
initLoc(field, locExpr, t.sons[i], tup.s) initLoc(field, locExpr, t.sons[i], tup.s)
if t.kind == tyTuple: if t.kind == tyTuple:
field.r = ropef("$1.Field$2", [rdLoc(tup), toRope(i)]) field.r = "$1.Field$2" % [rdLoc(tup), rope(i)]
else: else:
if t.n.sons[i].kind != nkSym: internalError(n.info, "genVarTuple") if t.n.sons[i].kind != nkSym: internalError(n.info, "genVarTuple")
field.r = ropef("$1.$2", field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(t.n.sons[i].sym, t)]
[rdLoc(tup), mangleRecFieldName(t.n.sons[i].sym, t)])
putLocIntoDest(p, v.loc, field) putLocIntoDest(p, v.loc, field)
proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false)
@ -86,8 +85,8 @@ proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} =
else: else:
expr(p, ri, a) expr(p, ri, a)
proc startBlock(p: BProc, start: TFormatStr = "{$n", proc startBlock(p: BProc, start: FormatStr = "{$n",
args: varargs[PRope]): int {.discardable.} = args: varargs[Rope]): int {.discardable.} =
lineCg(p, cpsStmts, start, args) lineCg(p, cpsStmts, start, args)
inc(p.labels) inc(p.labels)
result = len(p.blocks) result = len(p.blocks)
@ -96,21 +95,21 @@ proc startBlock(p: BProc, start: TFormatStr = "{$n",
p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16 p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16
p.blocks[result].nestedExceptStmts = p.inExceptBlock.int16 p.blocks[result].nestedExceptStmts = p.inExceptBlock.int16
proc assignLabel(b: var TBlock): PRope {.inline.} = proc assignLabel(b: var TBlock): Rope {.inline.} =
b.label = con("LA", b.id.toRope) b.label = "LA" & b.id.rope
result = b.label result = b.label
proc blockBody(b: var TBlock): PRope = proc blockBody(b: var TBlock): Rope =
result = b.sections[cpsLocals] result = b.sections[cpsLocals]
if b.frameLen > 0: if b.frameLen > 0:
result.appf("F.len+=$1;$n", b.frameLen.toRope) result.addf("F.len+=$1;$n", [b.frameLen.rope])
result.app(b.sections[cpsInit]) result.add(b.sections[cpsInit])
result.app(b.sections[cpsStmts]) result.add(b.sections[cpsStmts])
proc endBlock(p: BProc, blockEnd: PRope) = proc endBlock(p: BProc, blockEnd: Rope) =
let topBlock = p.blocks.len-1 let topBlock = p.blocks.len-1
# the block is merged into the parent block # the block is merged into the parent block
app(p.blocks[topBlock-1].sections[cpsStmts], p.blocks[topBlock].blockBody) add(p.blocks[topBlock-1].sections[cpsStmts], p.blocks[topBlock].blockBody)
setLen(p.blocks, topBlock) setLen(p.blocks, topBlock)
# this is done after the block is popped so $n is # this is done after the block is popped so $n is
# properly indented when pretty printing is enabled # properly indented when pretty printing is enabled
@ -124,7 +123,7 @@ proc endBlock(p: BProc) =
~"}$n" ~"}$n"
let frameLen = p.blocks[topBlock].frameLen let frameLen = p.blocks[topBlock].frameLen
if frameLen > 0: if frameLen > 0:
blockEnd.appf("F.len-=$1;$n", frameLen.toRope) blockEnd.addf("F.len-=$1;$n", [frameLen.rope])
endBlock(p, blockEnd) endBlock(p, blockEnd)
proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} = proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} =
@ -145,7 +144,7 @@ template preserveBreakIdx(body: stmt): stmt {.immediate.} =
proc genState(p: BProc, n: PNode) = proc genState(p: BProc, n: PNode) =
internalAssert n.len == 1 and n.sons[0].kind == nkIntLit internalAssert n.len == 1 and n.sons[0].kind == nkIntLit
let idx = n.sons[0].intVal let idx = n.sons[0].intVal
linefmt(p, cpsStmts, "STATE$1: ;$n", idx.toRope) linefmt(p, cpsStmts, "STATE$1: ;$n", idx.rope)
proc genGotoState(p: BProc, n: PNode) = proc genGotoState(p: BProc, n: PNode) =
# we resist the temptation to translate it into duff's device as it later # we resist the temptation to translate it into duff's device as it later
@ -159,7 +158,7 @@ proc genGotoState(p: BProc, n: PNode) =
p.beforeRetNeeded = true p.beforeRetNeeded = true
lineF(p, cpsStmts, "case -1: goto BeforeRet;$n", []) lineF(p, cpsStmts, "case -1: goto BeforeRet;$n", [])
for i in 0 .. lastOrd(n.sons[0].typ): for i in 0 .. lastOrd(n.sons[0].typ):
lineF(p, cpsStmts, "case $1: goto STATE$1;$n", [toRope(i)]) lineF(p, cpsStmts, "case $1: goto STATE$1;$n", [rope(i)])
lineF(p, cpsStmts, "}$n", []) lineF(p, cpsStmts, "}$n", [])
proc genBreakState(p: BProc, n: PNode) = proc genBreakState(p: BProc, n: PNode) =
@ -176,9 +175,18 @@ proc genBreakState(p: BProc, n: PNode) =
proc genVarPrototypeAux(m: BModule, sym: PSym) proc genVarPrototypeAux(m: BModule, sym: PSym)
proc genGotoVar(p: BProc; value: PNode) =
if value.kind notin {nkCharLit..nkUInt64Lit}:
localError(value.info, "'goto' target must be a literal value")
else:
lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])
proc genSingleVar(p: BProc, a: PNode) = proc genSingleVar(p: BProc, a: PNode) =
var v = a.sons[0].sym var v = a.sons[0].sym
if sfCompileTime in v.flags: return if {sfCompileTime, sfGoto} * v.flags != {}:
# translate 'var state {.goto.} = X' into 'goto LX':
if sfGoto in v.flags: genGotoVar(p, a.sons[2])
return
var targetProc = p var targetProc = p
if sfGlobal in v.flags: if sfGlobal in v.flags:
if v.flags * {sfImportc, sfExportc} == {sfImportc} and if v.flags * {sfImportc, sfExportc} == {sfImportc} and
@ -202,8 +210,32 @@ proc genSingleVar(p: BProc, a: PNode) =
genVarPrototypeAux(generatedHeader, v) genVarPrototypeAux(generatedHeader, v)
registerGcRoot(p, v) registerGcRoot(p, v)
else: else:
let value = a.sons[2]
let imm = isAssignedImmediately(value)
if imm and p.module.compileToCpp and p.splitDecls == 0 and
not containsHiddenPointer(v.typ):
# C++ really doesn't like things like 'Foo f; f = x' as that invokes a
# parameterless constructor followed by an assignment operator. So we
# generate better code here:
genLineDir(p, a)
let decl = localVarDecl(p, v)
var tmp: TLoc
if value.kind in nkCallKinds and value[0].kind == nkSym and
sfConstructor in value[0].sym.flags:
var params: Rope
let typ = skipTypes(value.sons[0].typ, abstractInst)
assert(typ.kind == tyProc)
for i in 1.. <value.len:
if params != nil: params.add(~", ")
assert(sonsLen(typ) == sonsLen(typ.n))
add(params, genOtherArg(p, value, i, typ))
lineF(p, cpsStmts, "$#($#);$n", [decl, params])
else:
initLocExprSingleUse(p, value, tmp)
lineF(p, cpsStmts, "$# = $#;$n", [decl, tmp.rdLoc])
return
assignLocalVar(p, v) assignLocalVar(p, v)
initLocalVar(p, v, isAssignedImmediately(a.sons[2])) initLocalVar(p, v, imm)
if a.sons[2].kind != nkEmpty: if a.sons[2].kind != nkEmpty:
genLineDir(targetProc, a) genLineDir(targetProc, a)
@ -242,15 +274,6 @@ proc genConstStmt(p: BProc, t: PNode) =
elif c.typ.kind in ConstantDataTypes and lfNoDecl notin c.loc.flags and elif c.typ.kind in ConstantDataTypes and lfNoDecl notin c.loc.flags and
c.ast.len != 0: c.ast.len != 0:
if not emitLazily(c): requestConstImpl(p, c) if not emitLazily(c): requestConstImpl(p, c)
when false:
# generate the data:
fillLoc(c.loc, locData, c.typ, mangleName(c), OnUnknown)
if sfImportc in c.flags:
appf(p.module.s[cfsData], "extern NIM_CONST $1 $2;$n",
[getTypeDesc(p.module, c.typ), c.loc.r])
else:
appf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
[getTypeDesc(p.module, c.typ), c.loc.r, genConstExpr(p, c.ast)])
proc genIf(p: BProc, n: PNode, d: var TLoc) = proc genIf(p: BProc, n: PNode, d: var TLoc) =
# #
@ -275,17 +298,22 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
let it = n.sons[i] let it = n.sons[i]
if it.len == 2: if it.len == 2:
when newScopeForIf: startBlock(p) when newScopeForIf: startBlock(p)
initLocExpr(p, it.sons[0], a) initLocExprSingleUse(p, it.sons[0], a)
lelse = getLabel(p) lelse = getLabel(p)
inc(p.labels) inc(p.labels)
lineFF(p, cpsStmts, "if (!$1) goto $2;$n", lineF(p, cpsStmts, "if (!$1) goto $2;$n",
"br i1 $1, label %LOC$3, label %$2$nLOC$3: $n", [rdLoc(a), lelse])
[rdLoc(a), lelse, toRope(p.labels)])
when not newScopeForIf: startBlock(p) when not newScopeForIf: startBlock(p)
expr(p, it.sons[1], d) if p.module.compileToCpp:
# avoid "jump to label crosses initialization" error:
add(p.s(cpsStmts), "{")
expr(p, it.sons[1], d)
add(p.s(cpsStmts), "}")
else:
expr(p, it.sons[1], d)
endBlock(p) endBlock(p)
if sonsLen(n) > 1: if sonsLen(n) > 1:
lineFF(p, cpsStmts, "goto $1;$n", "br label %$1$n", [lend]) lineF(p, cpsStmts, "goto $1;$n", [lend])
fixLabel(p, lelse) fixLabel(p, lelse)
elif it.len == 1: elif it.len == 1:
startBlock(p) startBlock(p)
@ -344,7 +372,20 @@ proc genReturnStmt(p: BProc, t: PNode) =
# consume it before we return. # consume it before we return.
var safePoint = p.finallySafePoints[p.finallySafePoints.len-1] var safePoint = p.finallySafePoints[p.finallySafePoints.len-1]
linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", safePoint) linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", safePoint)
lineFF(p, cpsStmts, "goto BeforeRet;$n", "br label %BeforeRet$n", []) lineF(p, cpsStmts, "goto BeforeRet;$n", [])
proc genGotoForCase(p: BProc; caseStmt: PNode) =
for i in 1 .. <caseStmt.len:
startBlock(p)
let it = caseStmt.sons[i]
for j in 0 .. it.len-2:
if it.sons[j].kind == nkRange:
localError(it.info, "range notation not available for computed goto")
return
let val = getOrdValue(it.sons[j])
lineF(p, cpsStmts, "NIMSTATE_$#:$n", [val.rope])
genStmts(p, it.lastSon)
endBlock(p)
proc genComputedGoto(p: BProc; n: PNode) = proc genComputedGoto(p: BProc; n: PNode) =
# first pass: Generate array of computed labels: # first pass: Generate array of computed labels:
@ -369,11 +410,11 @@ proc genComputedGoto(p: BProc; n: PNode) =
localError(n.info, "no case statement found for computed goto"); return localError(n.info, "no case statement found for computed goto"); return
var id = p.labels+1 var id = p.labels+1
inc p.labels, arraySize+1 inc p.labels, arraySize+1
let tmp = ropef("TMP$1", id.toRope) let tmp = "TMP$1" % [id.rope]
var gotoArray = ropef("static void* $#[$#] = {", tmp, arraySize.toRope) var gotoArray = "static void* $#[$#] = {" % [tmp, arraySize.rope]
for i in 1..arraySize-1: for i in 1..arraySize-1:
gotoArray.appf("&&TMP$#, ", (id+i).toRope) gotoArray.addf("&&TMP$#, ", [(id+i).rope])
gotoArray.appf("&&TMP$#};$n", (id+arraySize).toRope) gotoArray.addf("&&TMP$#};$n", [(id+arraySize).rope])
line(p, cpsLocals, gotoArray) line(p, cpsLocals, gotoArray)
let topBlock = p.blocks.len-1 let topBlock = p.blocks.len-1
@ -387,13 +428,13 @@ proc genComputedGoto(p: BProc; n: PNode) =
for j in 0 .. casePos-1: genStmts(p, n.sons[j]) for j in 0 .. casePos-1: genStmts(p, n.sons[j])
let tailA = p.blocks[topBlock].sections[cpsStmts] let tailA = p.blocks[topBlock].sections[cpsStmts]
p.blocks[topBlock].sections[cpsStmts] = oldBody.con(tailA) p.blocks[topBlock].sections[cpsStmts] = oldBody & tailA
let caseStmt = n.sons[casePos] let caseStmt = n.sons[casePos]
var a: TLoc var a: TLoc
initLocExpr(p, caseStmt.sons[0], a) initLocExpr(p, caseStmt.sons[0], a)
# first goto: # first goto:
lineF(p, cpsStmts, "goto *$#[$#];$n", tmp, a.rdLoc) lineF(p, cpsStmts, "goto *$#[$#];$n", [tmp, a.rdLoc])
for i in 1 .. <caseStmt.len: for i in 1 .. <caseStmt.len:
startBlock(p) startBlock(p)
@ -403,16 +444,16 @@ proc genComputedGoto(p: BProc; n: PNode) =
localError(it.info, "range notation not available for computed goto") localError(it.info, "range notation not available for computed goto")
return return
let val = getOrdValue(it.sons[j]) let val = getOrdValue(it.sons[j])
lineF(p, cpsStmts, "TMP$#:$n", intLiteral(val+id+1)) lineF(p, cpsStmts, "TMP$#:$n", [intLiteral(val+id+1)])
genStmts(p, it.lastSon) genStmts(p, it.lastSon)
#for j in casePos+1 .. <n.len: genStmts(p, n.sons[j]) # tailB #for j in casePos+1 .. <n.len: genStmts(p, n.sons[j]) # tailB
#for j in 0 .. casePos-1: genStmts(p, n.sons[j]) # tailA #for j in 0 .. casePos-1: genStmts(p, n.sons[j]) # tailA
app(p.s(cpsStmts), tailB) add(p.s(cpsStmts), tailB)
app(p.s(cpsStmts), tailA) add(p.s(cpsStmts), tailA)
var a: TLoc var a: TLoc
initLocExpr(p, caseStmt.sons[0], a) initLocExpr(p, caseStmt.sons[0], a)
lineF(p, cpsStmts, "goto *$#[$#];$n", tmp, a.rdLoc) lineF(p, cpsStmts, "goto *$#[$#];$n", [tmp, a.rdLoc])
endBlock(p) endBlock(p)
proc genWhileStmt(p: BProc, t: PNode) = proc genWhileStmt(p: BProc, t: PNode) =
@ -478,9 +519,9 @@ proc genParForStmt(p: BProc, t: PNode) =
lineF(p, cpsStmts, "#pragma omp parallel for $4$n" & lineF(p, cpsStmts, "#pragma omp parallel for $4$n" &
"for ($1 = $2; $1 <= $3; ++$1)", "for ($1 = $2; $1 <= $3; ++$1)",
forLoopVar.loc.rdLoc, [forLoopVar.loc.rdLoc,
rangeA.rdLoc, rangeB.rdLoc, rangeA.rdLoc, rangeB.rdLoc,
call.sons[3].getStr.toRope) call.sons[3].getStr.rope])
p.breakIdx = startBlock(p) p.breakIdx = startBlock(p)
p.blocks[p.breakIdx].isLoop = true p.blocks[p.breakIdx].isLoop = true
@ -510,12 +551,7 @@ proc genBreakStmt(p: BProc, t: PNode) =
lineF(p, cpsStmts, "goto $1;$n", [label]) lineF(p, cpsStmts, "goto $1;$n", [label])
proc getRaiseFrmt(p: BProc): string = proc getRaiseFrmt(p: BProc): string =
if p.module.compileToCpp: result = "#raiseException((#Exception*)$1, $2);$n"
result = "throw NimException($1, $2);$n"
elif getCompilerProc("Exception") != nil:
result = "#raiseException((#Exception*)$1, $2);$n"
else:
result = "#raiseException((#E_Base*)$1, $2);$n"
proc genRaiseStmt(p: BProc, t: PNode) = proc genRaiseStmt(p: BProc, t: PNode) =
if p.inExceptBlock > 0: if p.inExceptBlock > 0:
@ -540,7 +576,7 @@ proc genRaiseStmt(p: BProc, t: PNode) =
linefmt(p, cpsStmts, "#reraiseException();$n") linefmt(p, cpsStmts, "#reraiseException();$n")
proc genCaseGenericBranch(p: BProc, b: PNode, e: TLoc, proc genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
rangeFormat, eqFormat: TFormatStr, labl: TLabel) = rangeFormat, eqFormat: FormatStr, labl: TLabel) =
var var
x, y: TLoc x, y: TLoc
var length = sonsLen(b) var length = sonsLen(b)
@ -558,7 +594,7 @@ proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc,
labId, until: int): TLabel = labId, until: int): TLabel =
var lend = getLabel(p) var lend = getLabel(p)
for i in 1..until: for i in 1..until:
lineF(p, cpsStmts, "LA$1: ;$n", [toRope(labId + i)]) lineF(p, cpsStmts, "LA$1: ;$n", [rope(labId + i)])
if t.sons[i].kind == nkOfBranch: if t.sons[i].kind == nkOfBranch:
var length = sonsLen(t.sons[i]) var length = sonsLen(t.sons[i])
exprBlock(p, t.sons[i].sons[length - 1], d) exprBlock(p, t.sons[i].sons[length - 1], d)
@ -568,7 +604,7 @@ proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc,
result = lend result = lend
proc genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc, proc genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc,
rangeFormat, eqFormat: TFormatStr, rangeFormat, eqFormat: FormatStr,
until: int, a: TLoc): TLabel = until: int, a: TLoc): TLabel =
# generate a C-if statement for a Nim case statement # generate a C-if statement for a Nim case statement
var labId = p.labels var labId = p.labels
@ -576,27 +612,27 @@ proc genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc,
inc(p.labels) inc(p.labels)
if t.sons[i].kind == nkOfBranch: # else statement if t.sons[i].kind == nkOfBranch: # else statement
genCaseGenericBranch(p, t.sons[i], a, rangeFormat, eqFormat, genCaseGenericBranch(p, t.sons[i], a, rangeFormat, eqFormat,
con("LA", toRope(p.labels))) "LA" & rope(p.labels))
else: else:
lineF(p, cpsStmts, "goto LA$1;$n", [toRope(p.labels)]) lineF(p, cpsStmts, "goto LA$1;$n", [rope(p.labels)])
if until < t.len-1: if until < t.len-1:
inc(p.labels) inc(p.labels)
var gotoTarget = p.labels var gotoTarget = p.labels
lineF(p, cpsStmts, "goto LA$1;$n", [toRope(gotoTarget)]) lineF(p, cpsStmts, "goto LA$1;$n", [rope(gotoTarget)])
result = genCaseSecondPass(p, t, d, labId, until) result = genCaseSecondPass(p, t, d, labId, until)
lineF(p, cpsStmts, "LA$1: ;$n", [toRope(gotoTarget)]) lineF(p, cpsStmts, "LA$1: ;$n", [rope(gotoTarget)])
else: else:
result = genCaseSecondPass(p, t, d, labId, until) result = genCaseSecondPass(p, t, d, labId, until)
proc genCaseGeneric(p: BProc, t: PNode, d: var TLoc, proc genCaseGeneric(p: BProc, t: PNode, d: var TLoc,
rangeFormat, eqFormat: TFormatStr) = rangeFormat, eqFormat: FormatStr) =
var a: TLoc var a: TLoc
initLocExpr(p, t.sons[0], a) initLocExpr(p, t.sons[0], a)
var lend = genIfForCaseUntil(p, t, d, rangeFormat, eqFormat, sonsLen(t)-1, a) var lend = genIfForCaseUntil(p, t, d, rangeFormat, eqFormat, sonsLen(t)-1, a)
fixLabel(p, lend) fixLabel(p, lend)
proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel, proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
branches: var openArray[PRope]) = branches: var openArray[Rope]) =
var x: TLoc var x: TLoc
var length = sonsLen(b) var length = sonsLen(b)
for i in countup(0, length - 2): for i in countup(0, length - 2):
@ -614,7 +650,7 @@ proc genStringCase(p: BProc, t: PNode, d: var TLoc) =
if t.sons[i].kind == nkOfBranch: inc(strings, sonsLen(t.sons[i]) - 1) if t.sons[i].kind == nkOfBranch: inc(strings, sonsLen(t.sons[i]) - 1)
if strings > stringCaseThreshold: if strings > stringCaseThreshold:
var bitMask = math.nextPowerOfTwo(strings) - 1 var bitMask = math.nextPowerOfTwo(strings) - 1
var branches: seq[PRope] var branches: seq[Rope]
newSeq(branches, bitMask + 1) newSeq(branches, bitMask + 1)
var a: TLoc var a: TLoc
initLocExpr(p, t.sons[0], a) # fist pass: gnerate ifs+goto: initLocExpr(p, t.sons[0], a) # fist pass: gnerate ifs+goto:
@ -622,21 +658,21 @@ proc genStringCase(p: BProc, t: PNode, d: var TLoc) =
for i in countup(1, sonsLen(t) - 1): for i in countup(1, sonsLen(t) - 1):
inc(p.labels) inc(p.labels)
if t.sons[i].kind == nkOfBranch: if t.sons[i].kind == nkOfBranch:
genCaseStringBranch(p, t.sons[i], a, con("LA", toRope(p.labels)), genCaseStringBranch(p, t.sons[i], a, "LA" & rope(p.labels),
branches) branches)
else: else:
# else statement: nothing to do yet # else statement: nothing to do yet
# but we reserved a label, which we use later # but we reserved a label, which we use later
discard discard
linefmt(p, cpsStmts, "switch (#hashString($1) & $2) {$n", linefmt(p, cpsStmts, "switch (#hashString($1) & $2) {$n",
rdLoc(a), toRope(bitMask)) rdLoc(a), rope(bitMask))
for j in countup(0, high(branches)): for j in countup(0, high(branches)):
if branches[j] != nil: if branches[j] != nil:
lineF(p, cpsStmts, "case $1: $n$2break;$n", lineF(p, cpsStmts, "case $1: $n$2break;$n",
[intLiteral(j), branches[j]]) [intLiteral(j), branches[j]])
lineF(p, cpsStmts, "}$n") # else statement: lineF(p, cpsStmts, "}$n", []) # else statement:
if t.sons[sonsLen(t)-1].kind != nkOfBranch: if t.sons[sonsLen(t)-1].kind != nkOfBranch:
lineF(p, cpsStmts, "goto LA$1;$n", [toRope(p.labels)]) lineF(p, cpsStmts, "goto LA$1;$n", [rope(p.labels)])
# third pass: generate statements # third pass: generate statements
var lend = genCaseSecondPass(p, t, d, labId, sonsLen(t)-1) var lend = genCaseSecondPass(p, t, d, labId, sonsLen(t)-1)
fixLabel(p, lend) fixLabel(p, lend)
@ -698,13 +734,13 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
genCaseRange(p, branch) genCaseRange(p, branch)
else: else:
# else part of case statement: # else part of case statement:
lineF(p, cpsStmts, "default:$n") lineF(p, cpsStmts, "default:$n", [])
hasDefault = true hasDefault = true
exprBlock(p, branch.lastSon, d) exprBlock(p, branch.lastSon, d)
lineF(p, cpsStmts, "break;$n") lineF(p, cpsStmts, "break;$n", [])
if (hasAssume in CC[cCompiler].props) and not hasDefault: if (hasAssume in CC[cCompiler].props) and not hasDefault:
lineF(p, cpsStmts, "default: __assume(0);$n") lineF(p, cpsStmts, "default: __assume(0);$n", [])
lineF(p, cpsStmts, "}$n") lineF(p, cpsStmts, "}$n", [])
if lend != nil: fixLabel(p, lend) if lend != nil: fixLabel(p, lend)
proc genCase(p: BProc, t: PNode, d: var TLoc) = proc genCase(p: BProc, t: PNode, d: var TLoc) =
@ -718,7 +754,10 @@ proc genCase(p: BProc, t: PNode, d: var TLoc) =
genCaseGeneric(p, t, d, "if ($1 >= $2 && $1 <= $3) goto $4;$n", genCaseGeneric(p, t, d, "if ($1 >= $2 && $1 <= $3) goto $4;$n",
"if ($1 == $2) goto $3;$n") "if ($1 == $2) goto $3;$n")
else: else:
genOrdinalCase(p, t, d) if t.sons[0].kind == nkSym and sfGoto in t.sons[0].sym.flags:
genGotoForCase(p, t)
else:
genOrdinalCase(p, t, d)
proc hasGeneralExceptSection(t: PNode): bool = proc hasGeneralExceptSection(t: PNode): bool =
var length = sonsLen(t) var length = sonsLen(t)
@ -753,11 +792,8 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
# finallyPart(); # finallyPart();
if not isEmptyType(t.typ) and d.k == locNone: if not isEmptyType(t.typ) and d.k == locNone:
getTemp(p, t.typ, d) getTemp(p, t.typ, d)
var
exc: PRope
i, length, blen: int
genLineDir(p, t) genLineDir(p, t)
exc = getTempName() let exc = getTempName()
if getCompilerProc("Exception") != nil: if getCompilerProc("Exception") != nil:
discard cgsym(p.module, "Exception") discard cgsym(p.module, "Exception")
else: else:
@ -765,36 +801,42 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
add(p.nestedTryStmts, t) add(p.nestedTryStmts, t)
startBlock(p, "try {$n") startBlock(p, "try {$n")
expr(p, t.sons[0], d) expr(p, t.sons[0], d)
length = sonsLen(t) let length = sonsLen(t)
endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc])) endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc]))
if optStackTrace in p.options: if optStackTrace in p.options:
linefmt(p, cpsStmts, "#setFrame((TFrame*)&F);$n") linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR);$n")
inc p.inExceptBlock inc p.inExceptBlock
i = 1 var i = 1
var catchAllPresent = false var catchAllPresent = false
while (i < length) and (t.sons[i].kind == nkExceptBranch): while (i < length) and (t.sons[i].kind == nkExceptBranch):
blen = sonsLen(t.sons[i]) let blen = sonsLen(t.sons[i])
if i > 1: appf(p.s(cpsStmts), "else ") if i > 1: addf(p.s(cpsStmts), "else ", [])
if blen == 1: if blen == 1:
# general except section: # general except section:
catchAllPresent = true catchAllPresent = true
exprBlock(p, t.sons[i].sons[0], d) startBlock(p)
expr(p, t.sons[i].sons[0], d)
linefmt(p, cpsStmts, "#popCurrentException();$n")
endBlock(p)
else: else:
var orExpr: PRope = nil var orExpr: Rope = nil
for j in countup(0, blen - 2): for j in countup(0, blen - 2):
assert(t.sons[i].sons[j].kind == nkType) assert(t.sons[i].sons[j].kind == nkType)
if orExpr != nil: app(orExpr, "||") if orExpr != nil: add(orExpr, "||")
appcg(p.module, orExpr, appcg(p.module, orExpr,
"#isObj($1.exp->m_type, $2)", "#isObj($1.exp->m_type, $2)",
[exc, genTypeInfo(p.module, t.sons[i].sons[j].typ)]) [exc, genTypeInfo(p.module, t.sons[i].sons[j].typ)])
lineF(p, cpsStmts, "if ($1) ", [orExpr]) lineF(p, cpsStmts, "if ($1) ", [orExpr])
exprBlock(p, t.sons[i].sons[blen-1], d) startBlock(p)
expr(p, t.sons[i].sons[blen-1], d)
linefmt(p, cpsStmts, "#popCurrentException();$n")
endBlock(p)
inc(i) inc(i)
# reraise the exception if there was no catch all # reraise the exception if there was no catch all
# and none of the handlers matched # and none of the handlers matched
if not catchAllPresent: if not catchAllPresent:
if i > 1: lineF(p, cpsStmts, "else ") if i > 1: lineF(p, cpsStmts, "else ", [])
startBlock(p) startBlock(p)
var finallyBlock = t.lastSon var finallyBlock = t.lastSon
if finallyBlock.kind == nkFinally: if finallyBlock.kind == nkFinally:
@ -804,7 +846,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
line(p, cpsStmts, ~"throw;$n") line(p, cpsStmts, ~"throw;$n")
endBlock(p) endBlock(p)
lineF(p, cpsStmts, "}$n") # end of catch block lineF(p, cpsStmts, "}$n", []) # end of catch block
dec p.inExceptBlock dec p.inExceptBlock
discard pop(p.nestedTryStmts) discard pop(p.nestedTryStmts)
@ -868,24 +910,24 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
startBlock(p, "else {$n") startBlock(p, "else {$n")
linefmt(p, cpsStmts, "#popSafePoint();$n") linefmt(p, cpsStmts, "#popSafePoint();$n")
if optStackTrace in p.options: if optStackTrace in p.options:
linefmt(p, cpsStmts, "#setFrame((TFrame*)&F);$n") linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR);$n")
inc p.inExceptBlock inc p.inExceptBlock
var i = 1 var i = 1
while (i < length) and (t.sons[i].kind == nkExceptBranch): while (i < length) and (t.sons[i].kind == nkExceptBranch):
var blen = sonsLen(t.sons[i]) var blen = sonsLen(t.sons[i])
if blen == 1: if blen == 1:
# general except section: # general except section:
if i > 1: lineF(p, cpsStmts, "else") if i > 1: lineF(p, cpsStmts, "else", [])
startBlock(p) startBlock(p)
linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint) linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint)
expr(p, t.sons[i].sons[0], d) expr(p, t.sons[i].sons[0], d)
linefmt(p, cpsStmts, "#popCurrentException();$n") linefmt(p, cpsStmts, "#popCurrentException();$n")
endBlock(p) endBlock(p)
else: else:
var orExpr: PRope = nil var orExpr: Rope = nil
for j in countup(0, blen - 2): for j in countup(0, blen - 2):
assert(t.sons[i].sons[j].kind == nkType) assert(t.sons[i].sons[j].kind == nkType)
if orExpr != nil: app(orExpr, "||") if orExpr != nil: add(orExpr, "||")
appcg(p.module, orExpr, appcg(p.module, orExpr,
"#isObj(#getCurrentException()->Sup.m_type, $1)", "#isObj(#getCurrentException()->Sup.m_type, $1)",
[genTypeInfo(p.module, t.sons[i].sons[j].typ)]) [genTypeInfo(p.module, t.sons[i].sons[j].typ)])
@ -905,7 +947,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
discard pop(p.finallySafePoints) discard pop(p.finallySafePoints)
linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint) linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint)
proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope = proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope =
var res = "" var res = ""
for i in countup(0, sonsLen(t) - 1): for i in countup(0, sonsLen(t) - 1):
case t.sons[i].kind case t.sons[i].kind
@ -916,7 +958,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope =
if sym.kind in {skProc, skIterator, skClosureIterator, skMethod}: if sym.kind in {skProc, skIterator, skClosureIterator, skMethod}:
var a: TLoc var a: TLoc
initLocExpr(p, t.sons[i], a) initLocExpr(p, t.sons[i], a)
res.add(rdLoc(a).ropeToStr) res.add($rdLoc(a))
else: else:
var r = sym.loc.r var r = sym.loc.r
if r == nil: if r == nil:
@ -924,47 +966,59 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope =
# it doesn't matter much: # it doesn't matter much:
r = mangleName(sym) r = mangleName(sym)
sym.loc.r = r # but be consequent! sym.loc.r = r # but be consequent!
res.add(r.ropeToStr) res.add($r)
else: internalError(t.sons[i].info, "genAsmOrEmitStmt()") else: internalError(t.sons[i].info, "genAsmOrEmitStmt()")
if isAsmStmt and hasGnuAsm in CC[cCompiler].props: if isAsmStmt and hasGnuAsm in CC[cCompiler].props:
for x in splitLines(res): for x in splitLines(res):
var j = 0 var j = 0
while x[j] in {' ', '\t'}: inc(j) while x[j] in {' ', '\t'}: inc(j)
if x[j] == ':' and x[j+1] == '"' or x[j] == '"': if x[j] in {'"', ':'}:
# don't modify the line if already in quotes or
# some clobber register list: # some clobber register list:
app(result, x); app(result, tnl) add(result, x); add(result, tnl)
elif x[j] != '\0': elif x[j] != '\0':
# ignore empty lines # ignore empty lines
app(result, "\"") add(result, "\"")
app(result, x) add(result, x)
app(result, "\\n\"\n") add(result, "\\n\"\n")
else: else:
res.add(tnl) res.add(tnl)
result = res.toRope result = res.rope
proc genAsmStmt(p: BProc, t: PNode) = proc genAsmStmt(p: BProc, t: PNode) =
assert(t.kind == nkAsmStmt) assert(t.kind == nkAsmStmt)
genLineDir(p, t) genLineDir(p, t)
var s = genAsmOrEmitStmt(p, t, isAsmStmt=true) var s = genAsmOrEmitStmt(p, t, isAsmStmt=true)
# see bug #2362, "top level asm statements" seem to be a mis-feature
# but even if we don't do this, the example in #2362 cannot possibly
# work:
if p.prc == nil: if p.prc == nil:
# top level asm statement? # top level asm statement?
appf(p.module.s[cfsProcHeaders], CC[cCompiler].asmStmtFrmt, [s]) addf(p.module.s[cfsProcHeaders], CC[cCompiler].asmStmtFrmt, [s])
else: else:
lineF(p, cpsStmts, CC[cCompiler].asmStmtFrmt, [s]) lineF(p, cpsStmts, CC[cCompiler].asmStmtFrmt, [s])
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
proc genEmit(p: BProc, t: PNode) = proc genEmit(p: BProc, t: PNode) =
genLineDir(p, t)
var s = genAsmOrEmitStmt(p, t.sons[1]) var s = genAsmOrEmitStmt(p, t.sons[1])
if p.prc == nil: if p.prc == nil:
# top level emit pragma? # top level emit pragma?
app(p.module.s[cfsProcHeaders], s) let section = determineSection(t[1])
genCLineDir(p.module.s[section], t.info)
add(p.module.s[section], s)
else: else:
genLineDir(p, t)
line(p, cpsStmts, s) line(p, cpsStmts, s)
var var
breakPointId: int = 0 breakPointId: int = 0
gBreakpoints: PRope # later the breakpoints are inserted into the main proc gBreakpoints: Rope # later the breakpoints are inserted into the main proc
proc genBreakPoint(p: BProc, t: PNode) = proc genBreakPoint(p: BProc, t: PNode) =
var name: string var name: string
@ -978,7 +1032,7 @@ proc genBreakPoint(p: BProc, t: PNode) =
genLineDir(p, t) # BUGFIX genLineDir(p, t) # BUGFIX
appcg(p.module, gBreakpoints, appcg(p.module, gBreakpoints,
"#dbgRegisterBreakpoint($1, (NCSTRING)$2, (NCSTRING)$3);$n", [ "#dbgRegisterBreakpoint($1, (NCSTRING)$2, (NCSTRING)$3);$n", [
toRope(toLinenumber(t.info)), makeCString(toFilename(t.info)), rope(toLinenumber(t.info)), makeCString(toFilename(t.info)),
makeCString(name)]) makeCString(name)])
proc genWatchpoint(p: BProc, n: PNode) = proc genWatchpoint(p: BProc, n: PNode) =
@ -1041,7 +1095,9 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) =
proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
genLineDir(p, e) genLineDir(p, e)
if not fieldDiscriminantCheckNeeded(p, e): if e.sons[0].kind == nkSym and sfGoto in e.sons[0].sym.flags:
genGotoVar(p, e.sons[1])
elif not fieldDiscriminantCheckNeeded(p, e):
var a: TLoc var a: TLoc
initLocExpr(p, e.sons[0], a) initLocExpr(p, e.sons[0], a)
if fastAsgn: incl(a.flags, lfNoDeepCopy) if fastAsgn: incl(a.flags, lfNoDeepCopy)

View file

@ -19,12 +19,12 @@ proc accessThreadLocalVar(p: BProc, s: PSym) =
if emulatedThreadVars() and not p.threadVarAccessed: if emulatedThreadVars() and not p.threadVarAccessed:
p.threadVarAccessed = true p.threadVarAccessed = true
p.module.usesThreadVars = true p.module.usesThreadVars = true
appf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV;$n") addf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV;$n", [])
app(p.procSec(cpsInit), add(p.procSec(cpsInit),
ropecg(p.module, "\tNimTV = (NimThreadVars*) #GetThreadLocalVars();$n")) ropecg(p.module, "\tNimTV = (NimThreadVars*) #GetThreadLocalVars();$n"))
var var
nimtv: PRope # nimrod thread vars; the struct body nimtv: Rope # nimrod thread vars; the struct body
nimtvDeps: seq[PType] = @[] # type deps: every module needs whole struct nimtvDeps: seq[PType] = @[] # type deps: every module needs whole struct
nimtvDeclared = initIntSet() # so that every var/field exists only once nimtvDeclared = initIntSet() # so that every var/field exists only once
# in the struct # in the struct
@ -43,23 +43,23 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
# allocator for it :-( # allocator for it :-(
if not containsOrIncl(nimtvDeclared, s.id): if not containsOrIncl(nimtvDeclared, s.id):
nimtvDeps.add(s.loc.t) nimtvDeps.add(s.loc.t)
appf(nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r]) addf(nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
else: else:
if isExtern: app(m.s[cfsVars], "extern ") if isExtern: add(m.s[cfsVars], "extern ")
if optThreads in gGlobalOptions: app(m.s[cfsVars], "NIM_THREADVAR ") if optThreads in gGlobalOptions: add(m.s[cfsVars], "NIM_THREADVAR ")
app(m.s[cfsVars], getTypeDesc(m, s.loc.t)) add(m.s[cfsVars], getTypeDesc(m, s.loc.t))
appf(m.s[cfsVars], " $1;$n", [s.loc.r]) addf(m.s[cfsVars], " $1;$n", [s.loc.r])
proc generateThreadLocalStorage(m: BModule) = proc generateThreadLocalStorage(m: BModule) =
if nimtv != nil and (m.usesThreadVars or sfMainModule in m.module.flags): if nimtv != nil and (m.usesThreadVars or sfMainModule in m.module.flags):
for t in items(nimtvDeps): discard getTypeDesc(m, t) for t in items(nimtvDeps): discard getTypeDesc(m, t)
appf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [nimtv]) addf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [nimtv])
proc generateThreadVarsSize(m: BModule) = proc generateThreadVarsSize(m: BModule) =
if nimtv != nil: if nimtv != nil:
let externc = if gCmd != cmdCompileToCpp and let externc = if gCmd != cmdCompileToCpp and
sfCompileToCpp in m.module.flags: "extern \"C\"" sfCompileToCpp in m.module.flags: "extern \"C\""
else: "" else: ""
appf(m.s[cfsProcs], addf(m.s[cfsProcs],
"$#NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}$n", "$#NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}$n",
[externc.toRope]) [externc.rope])

View file

@ -17,11 +17,11 @@ type
p: BProc p: BProc
visitorFrmt: string visitorFrmt: string
proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType)
proc genCaseRange(p: BProc, branch: PNode) proc genCaseRange(p: BProc, branch: PNode)
proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false)
proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, n: PNode) = proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, n: PNode) =
if n == nil: return if n == nil: return
case n.kind case n.kind
of nkRecList: of nkRecList:
@ -31,31 +31,31 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, n: PNode) =
if (n.sons[0].kind != nkSym): internalError(n.info, "genTraverseProc") if (n.sons[0].kind != nkSym): internalError(n.info, "genTraverseProc")
var p = c.p var p = c.p
let disc = n.sons[0].sym let disc = n.sons[0].sym
lineF(p, cpsStmts, "switch ($1.$2) {$n", accessor, disc.loc.r) lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
let branch = n.sons[i] let branch = n.sons[i]
assert branch.kind in {nkOfBranch, nkElse} assert branch.kind in {nkOfBranch, nkElse}
if branch.kind == nkOfBranch: if branch.kind == nkOfBranch:
genCaseRange(c.p, branch) genCaseRange(c.p, branch)
else: else:
lineF(p, cpsStmts, "default:$n") lineF(p, cpsStmts, "default:$n", [])
genTraverseProc(c, accessor, lastSon(branch)) genTraverseProc(c, accessor, lastSon(branch))
lineF(p, cpsStmts, "break;$n") lineF(p, cpsStmts, "break;$n", [])
lineF(p, cpsStmts, "} $n") lineF(p, cpsStmts, "} $n", [])
of nkSym: of nkSym:
let field = n.sym let field = n.sym
if field.loc.t == nil: if field.loc.t == nil:
internalError(n.info, "genTraverseProc()") internalError(n.info, "genTraverseProc()")
genTraverseProc(c, ropef("$1.$2", accessor, field.loc.r), field.loc.t) genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t)
else: internalError(n.info, "genTraverseProc()") else: internalError(n.info, "genTraverseProc()")
proc parentObj(accessor: PRope; m: BModule): PRope {.inline.} = proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
if not m.compileToCpp: if not m.compileToCpp:
result = ropef("$1.Sup", accessor) result = "$1.Sup" % [accessor]
else: else:
result = accessor result = accessor
proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) = proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType) =
if typ == nil: return if typ == nil: return
var p = c.p var p = c.p
case typ.kind case typ.kind
@ -66,9 +66,9 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) =
var i: TLoc var i: TLoc
getTemp(p, getSysType(tyInt), i) getTemp(p, getSysType(tyInt), i)
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
i.r, arraySize.toRope) i.r, arraySize.rope)
genTraverseProc(c, rfmt(nil, "$1[$2]", accessor, i.r), typ.sons[1]) genTraverseProc(c, rfmt(nil, "$1[$2]", accessor, i.r), typ.sons[1])
lineF(p, cpsStmts, "}$n") lineF(p, cpsStmts, "}$n", [])
of tyObject: of tyObject:
for i in countup(0, sonsLen(typ) - 1): for i in countup(0, sonsLen(typ) - 1):
genTraverseProc(c, accessor.parentObj(c.p.module), typ.sons[i]) genTraverseProc(c, accessor.parentObj(c.p.module), typ.sons[i])
@ -76,7 +76,7 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) =
of tyTuple: of tyTuple:
let typ = getUniqueType(typ) let typ = getUniqueType(typ)
for i in countup(0, sonsLen(typ) - 1): for i in countup(0, sonsLen(typ) - 1):
genTraverseProc(c, rfmt(nil, "$1.Field$2", accessor, i.toRope), typ.sons[i]) genTraverseProc(c, rfmt(nil, "$1.Field$2", accessor, i.rope), typ.sons[i])
of tyRef, tyString, tySequence: of tyRef, tyString, tySequence:
lineCg(p, cpsStmts, c.visitorFrmt, accessor) lineCg(p, cpsStmts, c.visitorFrmt, accessor)
of tyProc: of tyProc:
@ -85,17 +85,17 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) =
else: else:
discard discard
proc genTraverseProcSeq(c: var TTraversalClosure, accessor: PRope, typ: PType) = proc genTraverseProcSeq(c: var TTraversalClosure, accessor: Rope, typ: PType) =
var p = c.p var p = c.p
assert typ.kind == tySequence assert typ.kind == tySequence
var i: TLoc var i: TLoc
getTemp(p, getSysType(tyInt), i) getTemp(p, getSysType(tyInt), i)
lineF(p, cpsStmts, "for ($1 = 0; $1 < $2->$3; $1++) {$n", lineF(p, cpsStmts, "for ($1 = 0; $1 < $2->$3; $1++) {$n",
i.r, accessor, toRope(if c.p.module.compileToCpp: "len" else: "Sup.len")) [i.r, accessor, rope(if c.p.module.compileToCpp: "len" else: "Sup.len")])
genTraverseProc(c, ropef("$1->data[$2]", accessor, i.r), typ.sons[0]) genTraverseProc(c, "$1->data[$2]" % [accessor, i.r], typ.sons[0])
lineF(p, cpsStmts, "}$n") lineF(p, cpsStmts, "}$n", [])
proc genTraverseProc(m: BModule, typ: PType, reason: TTypeInfoReason): PRope = proc genTraverseProc(m: BModule, typ: PType, reason: TTypeInfoReason): Rope =
var c: TTraversalClosure var c: TTraversalClosure
var p = newProc(nil, m) var p = newProc(nil, m)
result = getGlobalTempName() result = getGlobalTempName()
@ -104,30 +104,30 @@ proc genTraverseProc(m: BModule, typ: PType, reason: TTypeInfoReason): PRope =
of tiNew: c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n" of tiNew: c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n"
else: assert false else: assert false
let header = ropef("N_NIMCALL(void, $1)(void* p, NI op)", result) let header = "N_NIMCALL(void, $1)(void* p, NI op)" % [result]
let t = getTypeDesc(m, typ) let t = getTypeDesc(m, typ)
lineF(p, cpsLocals, "$1 a;$n", t) lineF(p, cpsLocals, "$1 a;$n", [t])
lineF(p, cpsInit, "a = ($1)p;$n", t) lineF(p, cpsInit, "a = ($1)p;$n", [t])
c.p = p c.p = p
assert typ.kind != tyTypeDesc assert typ.kind != tyTypeDesc
if typ.kind == tySequence: if typ.kind == tySequence:
genTraverseProcSeq(c, "a".toRope, typ) genTraverseProcSeq(c, "a".rope, typ)
else: else:
if skipTypes(typ.sons[0], typedescInst).kind in {tyArrayConstr, tyArray}: if skipTypes(typ.sons[0], typedescInst).kind in {tyArrayConstr, tyArray}:
# C's arrays are broken beyond repair: # C's arrays are broken beyond repair:
genTraverseProc(c, "a".toRope, typ.sons[0]) genTraverseProc(c, "a".rope, typ.sons[0])
else: else:
genTraverseProc(c, "(*a)".toRope, typ.sons[0]) genTraverseProc(c, "(*a)".rope, typ.sons[0])
let generatedProc = ropef("$1 {$n$2$3$4}$n", let generatedProc = "$1 {$n$2$3$4}$n" %
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]) [header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
m.s[cfsProcHeaders].appf("$1;$n", header) m.s[cfsProcHeaders].addf("$1;$n", [header])
m.s[cfsProcs].app(generatedProc) m.s[cfsProcs].add(generatedProc)
proc genTraverseProcForGlobal(m: BModule, s: PSym): PRope = proc genTraverseProcForGlobal(m: BModule, s: PSym): Rope =
discard genTypeInfo(m, s.loc.t) discard genTypeInfo(m, s.loc.t)
var c: TTraversalClosure var c: TTraversalClosure
@ -137,15 +137,15 @@ proc genTraverseProcForGlobal(m: BModule, s: PSym): PRope =
if sfThread in s.flags and emulatedThreadVars(): if sfThread in s.flags and emulatedThreadVars():
accessThreadLocalVar(p, s) accessThreadLocalVar(p, s)
sLoc = con("NimTV->", sLoc) sLoc = "NimTV->" & sLoc
c.visitorFrmt = "#nimGCvisit((void*)$1, 0);$n" c.visitorFrmt = "#nimGCvisit((void*)$1, 0);$n"
c.p = p c.p = p
let header = ropef("N_NIMCALL(void, $1)()", result) let header = "N_NIMCALL(void, $1)()" % [result]
genTraverseProc(c, sLoc, s.loc.t) genTraverseProc(c, sLoc, s.loc.t)
let generatedProc = ropef("$1 {$n$2$3$4}$n", let generatedProc = "$1 {$n$2$3$4}$n" %
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]) [header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
m.s[cfsProcHeaders].appf("$1;$n", header) m.s[cfsProcHeaders].addf("$1;$n", [header])
m.s[cfsProcs].app(generatedProc) m.s[cfsProcs].add(generatedProc)

File diff suppressed because it is too large Load diff

View file

@ -70,13 +70,25 @@ when false:
for i in countup(low(TTypeKind), high(TTypeKind)): for i in countup(low(TTypeKind), high(TTypeKind)):
echo i, " ", gTypeTable[i].counter echo i, " ", gTypeTable[i].counter
proc slowSearch(key: PType; k: TTypeKind): PType =
# tuples are quite horrible as C does not support them directly and
# tuple[string, string] is a (strange) subtype of
# tuple[nameA, nameB: string]. This bites us here, so we
# use 'sameBackendType' instead of 'sameType'.
if idTableHasObjectAsKey(gTypeTable[k], key): return key
for h in countup(0, high(gTypeTable[k].data)):
var t = PType(gTypeTable[k].data[h].key)
if t != nil and sameBackendType(t, key):
return t
idTablePut(gTypeTable[k], key, key)
result = key
proc getUniqueType*(key: PType): PType = proc getUniqueType*(key: PType): PType =
# this is a hotspot in the compiler! # this is a hotspot in the compiler!
if key == nil: return if key == nil: return
var k = key.kind var k = key.kind
case k case k
of tyBool, tyChar, of tyBool, tyChar, tyInt..tyUInt64:
tyInt..tyUInt64:
# no canonicalization for integral types, so that e.g. ``pid_t`` is # no canonicalization for integral types, so that e.g. ``pid_t`` is
# produced instead of ``NI``. # produced instead of ``NI``.
result = key result = key
@ -86,9 +98,8 @@ proc getUniqueType*(key: PType): PType =
if result == nil: if result == nil:
gCanonicalTypes[k] = key gCanonicalTypes[k] = key
result = key result = key
of tyTypeDesc, tyTypeClasses, tyGenericParam, of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr, tyFieldAccessor:
tyFromExpr, tyFieldAccessor: internalError("getUniqueType")
internalError("GetUniqueType")
of tyDistinct: of tyDistinct:
if key.deepCopy != nil: result = key if key.deepCopy != nil: result = key
else: result = getUniqueType(lastSon(key)) else: result = getUniqueType(lastSon(key))
@ -98,23 +109,20 @@ proc getUniqueType*(key: PType): PType =
#if obj.sym != nil and obj.sym.name.s == "TOption": #if obj.sym != nil and obj.sym.name.s == "TOption":
# echo "for ", typeToString(key), " I returned " # echo "for ", typeToString(key), " I returned "
# debug result # debug result
of tyArrayConstr, tyGenericInvokation, tyGenericBody, of tyPtr, tyRef, tyVar:
let elemType = lastSon(key)
if elemType.kind in {tyBool, tyChar, tyInt..tyUInt64}:
# no canonicalization for integral types, so that e.g. ``ptr pid_t`` is
# produced instead of ``ptr NI``.
result = key
else:
result = slowSearch(key, k)
of tyArrayConstr, tyGenericInvocation, tyGenericBody,
tyOpenArray, tyArray, tySet, tyRange, tyTuple, tyOpenArray, tyArray, tySet, tyRange, tyTuple,
tyPtr, tyRef, tySequence, tyForward, tyVarargs, tyProxy, tyVar: tySequence, tyForward, tyVarargs, tyProxy:
# tuples are quite horrible as C does not support them directly and
# tuple[string, string] is a (strange) subtype of
# tuple[nameA, nameB: string]. This bites us here, so we
# use 'sameBackendType' instead of 'sameType'.
# we have to do a slow linear search because types may need # we have to do a slow linear search because types may need
# to be compared by their structure: # to be compared by their structure:
if idTableHasObjectAsKey(gTypeTable[k], key): return key result = slowSearch(key, k)
for h in countup(0, high(gTypeTable[k].data)):
var t = PType(gTypeTable[k].data[h].key)
if t != nil and sameBackendType(t, key):
return t
idTablePut(gTypeTable[k], key, key)
result = key
of tyObject: of tyObject:
if tfFromGeneric notin key.flags: if tfFromGeneric notin key.flags:
# fast case; lookup per id suffices: # fast case; lookup per id suffices:
@ -127,7 +135,7 @@ proc getUniqueType*(key: PType): PType =
if idTableHasObjectAsKey(gTypeTable[k], key): return key if idTableHasObjectAsKey(gTypeTable[k], key): return key
for h in countup(0, high(gTypeTable[k].data)): for h in countup(0, high(gTypeTable[k].data)):
var t = PType(gTypeTable[k].data[h].key) var t = PType(gTypeTable[k].data[h].key)
if t != nil and sameType(t, key): if t != nil and sameBackendType(t, key):
return t return t
idTablePut(gTypeTable[k], key, key) idTablePut(gTypeTable[k], key, key)
result = key result = key
@ -141,13 +149,7 @@ proc getUniqueType*(key: PType): PType =
result = key result = key
else: else:
# ugh, we need the canon here: # ugh, we need the canon here:
if idTableHasObjectAsKey(gTypeTable[k], key): return key result = slowSearch(key, k)
for h in countup(0, high(gTypeTable[k].data)):
var t = PType(gTypeTable[k].data[h].key)
if t != nil and sameBackendType(t, key):
return t
idTablePut(gTypeTable[k], key, key)
result = key
proc tableGetType*(tab: TIdTable, key: PType): RootRef = proc tableGetType*(tab: TIdTable, key: PType): RootRef =
# returns nil if we need to declare this type # returns nil if we need to declare this type
@ -174,7 +176,7 @@ proc mangle*(name: string): string =
result = newStringOfCap(name.len) result = newStringOfCap(name.len)
case name[0] case name[0]
of Letters: of Letters:
result.add(name[0].toLower) result.add(name[0])
of Digits: of Digits:
result.add("N" & name[0]) result.add("N" & name[0])
else: else:
@ -191,13 +193,13 @@ proc mangle*(name: string): string =
else: else:
add(result, "HEX" & toHex(ord(c), 2)) add(result, "HEX" & toHex(ord(c), 2))
proc makeLLVMString*(s: string): PRope = proc makeLLVMString*(s: string): Rope =
const MaxLineLength = 64 const MaxLineLength = 64
result = nil result = nil
var res = "c\"" var res = "c\""
for i in countup(0, len(s) - 1): for i in countup(0, len(s) - 1):
if (i + 1) mod MaxLineLength == 0: if (i + 1) mod MaxLineLength == 0:
app(result, toRope(res)) add(result, rope(res))
setLen(res, 0) setLen(res, 0)
case s[i] case s[i]
of '\0'..'\x1F', '\x80'..'\xFF', '\"', '\\': of '\0'..'\x1F', '\x80'..'\xFF', '\"', '\\':
@ -205,6 +207,6 @@ proc makeLLVMString*(s: string): PRope =
add(res, toHex(ord(s[i]), 2)) add(res, toHex(ord(s[i]), 2))
else: add(res, s[i]) else: add(res, s[i])
add(res, "\\00\"") add(res, "\\00\"")
app(result, toRope(res)) add(result, rope(res))
initTypeTables() initTypeTables()

File diff suppressed because it is too large Load diff

View file

@ -12,8 +12,10 @@
import import
ast, astalgo, ropes, passes, options, intsets, lists, platform ast, astalgo, ropes, passes, options, intsets, lists, platform
from msgs import TLineInfo
type type
TLabel* = PRope # for the C generator a label is just a rope TLabel* = Rope # for the C generator a label is just a rope
TCFileSection* = enum # the sections a generated C file consists of TCFileSection* = enum # the sections a generated C file consists of
cfsMergeInfo, # section containing merge information cfsMergeInfo, # section containing merge information
cfsHeaders, # section for C include file headers cfsHeaders, # section for C include file headers
@ -43,17 +45,17 @@ type
ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64, ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64,
ctArray, ctPtrToArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc, ctArray, ctPtrToArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc,
ctCString ctCString
TCFileSections* = array[TCFileSection, PRope] # represents a generated C file TCFileSections* = array[TCFileSection, Rope] # represents a generated C file
TCProcSection* = enum # the sections a generated C proc consists of TCProcSection* = enum # the sections a generated C proc consists of
cpsLocals, # section of local variables for C proc cpsLocals, # section of local variables for C proc
cpsInit, # section for init of variables for C proc cpsInit, # section for init of variables for C proc
cpsStmts # section of local statements for C proc cpsStmts # section of local statements for C proc
TCProcSections* = array[TCProcSection, PRope] # represents a generated C proc TCProcSections* = array[TCProcSection, Rope] # represents a generated C proc
BModule* = ref TCGen BModule* = ref TCGen
BProc* = ref TCProc BProc* = ref TCProc
TBlock*{.final.} = object TBlock*{.final.} = object
id*: int # the ID of the label; positive means that it id*: int # the ID of the label; positive means that it
label*: PRope # generated text for the label label*: Rope # generated text for the label
# nil if label is not used # nil if label is not used
sections*: TCProcSections # the code beloging sections*: TCProcSections # the code beloging
isLoop*: bool # whether block is a loop isLoop*: bool # whether block is a loop
@ -65,12 +67,13 @@ type
prc*: PSym # the Nim proc that this C proc belongs to prc*: PSym # the Nim proc that this C proc belongs to
beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed
threadVarAccessed*: bool # true if the proc already accessed some threadvar threadVarAccessed*: bool # true if the proc already accessed some threadvar
lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements
nestedTryStmts*: seq[PNode] # in how many nested try statements we are nestedTryStmts*: seq[PNode] # in how many nested try statements we are
# (the vars must be volatile then) # (the vars must be volatile then)
inExceptBlock*: int # are we currently inside an except block? inExceptBlock*: int # are we currently inside an except block?
# leaving such scopes by raise or by return must # leaving such scopes by raise or by return must
# execute any applicable finally blocks # execute any applicable finally blocks
finallySafePoints*: seq[PRope] # For correctly cleaning up exceptions when finallySafePoints*: seq[Rope] # For correctly cleaning up exceptions when
# using return in finally statements # using return in finally statements
labels*: Natural # for generating unique labels in the C proc labels*: Natural # for generating unique labels in the C proc
blocks*: seq[TBlock] # nested blocks blocks*: seq[TBlock] # nested blocks
@ -82,8 +85,11 @@ type
maxFrameLen*: int # max length of frame descriptor maxFrameLen*: int # max length of frame descriptor
module*: BModule # used to prevent excessive parameter passing module*: BModule # used to prevent excessive parameter passing
withinLoop*: int # > 0 if we are within a loop withinLoop*: int # > 0 if we are within a loop
splitDecls*: int # > 0 if we are in some context for C++ that
# requires 'T x = T()' to become 'T x; x = T()'
# (yes, C++ is weird like that)
gcFrameId*: Natural # for the GC stack marking gcFrameId*: Natural # for the GC stack marking
gcFrameType*: PRope # the struct {} we put the GC markers into gcFrameType*: Rope # the struct {} we put the GC markers into
TTypeSeq* = seq[PType] TTypeSeq* = seq[PType]
TCGen = object of TPassContext # represents a C source file TCGen = object of TPassContext # represents a C source file
@ -112,24 +118,24 @@ type
dataCache*: TNodeTable dataCache*: TNodeTable
forwardedProcs*: TSymSeq # keep forwarded procs here forwardedProcs*: TSymSeq # keep forwarded procs here
typeNodes*, nimTypes*: int # used for type info generation typeNodes*, nimTypes*: int # used for type info generation
typeNodesName*, nimTypesName*: PRope # used for type info generation typeNodesName*, nimTypesName*: Rope # used for type info generation
labels*: Natural # for generating unique module-scope names labels*: Natural # for generating unique module-scope names
extensionLoaders*: array['0'..'9', PRope] # special procs for the extensionLoaders*: array['0'..'9', Rope] # special procs for the
# OpenGL wrapper # OpenGL wrapper
injectStmt*: PRope injectStmt*: Rope
var var
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: PRope mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
# varuious parts of the main module # varuious parts of the main module
gMapping*: PRope # the generated mapping file (if requested) gMapping*: Rope # the generated mapping file (if requested)
gModules*: seq[BModule] = @[] # list of all compiled modules gModules*: seq[BModule] = @[] # list of all compiled modules
gForwardedProcsCounter*: int = 0 gForwardedProcsCounter*: int = 0
proc s*(p: BProc, s: TCProcSection): var PRope {.inline.} = proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} =
# section in the current block # section in the current block
result = p.blocks[p.blocks.len - 1].sections[s] result = p.blocks[p.blocks.len - 1].sections[s]
proc procSec*(p: BProc, s: TCProcSection): var PRope {.inline.} = proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} =
# top level proc sections # top level proc sections
result = p.blocks[0].sections[s] result = p.blocks[0].sections[s]

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -52,7 +52,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo)
const const
HelpMessage = "Nim Compiler Version $1 (" & CompileDate & ") [$2: $3]\n" & HelpMessage = "Nim Compiler Version $1 (" & CompileDate & ") [$2: $3]\n" &
"Copyright (c) 2006-2014 by Andreas Rumpf\n" "Copyright (c) 2006-2015 by Andreas Rumpf\n"
const const
Usage = slurp"doc/basicopt.txt".replace("//", "") Usage = slurp"doc/basicopt.txt".replace("//", "")
@ -65,14 +65,14 @@ proc getCommandLineDesc(): string =
proc helpOnError(pass: TCmdLinePass) = proc helpOnError(pass: TCmdLinePass) =
if pass == passCmd1: if pass == passCmd1:
msgWriteln(getCommandLineDesc()) msgWriteln(getCommandLineDesc())
quit(0) msgQuit(0)
proc writeAdvancedUsage(pass: TCmdLinePass) = proc writeAdvancedUsage(pass: TCmdLinePass) =
if pass == passCmd1: if pass == passCmd1:
msgWriteln(`%`(HelpMessage, [VersionAsString, msgWriteln(`%`(HelpMessage, [VersionAsString,
platform.OS[platform.hostOS].name, platform.OS[platform.hostOS].name,
CPU[platform.hostCPU].name]) & AdvancedUsage) CPU[platform.hostCPU].name]) & AdvancedUsage)
quit(0) msgQuit(0)
proc writeVersionInfo(pass: TCmdLinePass) = proc writeVersionInfo(pass: TCmdLinePass) =
if pass == passCmd1: if pass == passCmd1:
@ -80,14 +80,14 @@ proc writeVersionInfo(pass: TCmdLinePass) =
platform.OS[platform.hostOS].name, platform.OS[platform.hostOS].name,
CPU[platform.hostCPU].name])) CPU[platform.hostCPU].name]))
discard """const gitHash = gorge("git log -n 1 --format=%H") const gitHash = gorge("git log -n 1 --format=%H").strip
if gitHash.strip.len == 40: when gitHash.len == 40:
msgWriteln("git hash: " & gitHash)""" msgWriteln("git hash: " & gitHash)
msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine & msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine &
usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas & usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas &
usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedNoGC) usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedNoGC)
quit(0) msgQuit(0)
var var
helpWritten: bool helpWritten: bool
@ -141,7 +141,7 @@ proc expectNoArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if arg != "": localError(info, errCmdLineNoArgExpected, addPrefix(switch)) if arg != "": localError(info, errCmdLineNoArgExpected, addPrefix(switch))
proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass, proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass,
info: TLineInfo) = info: TLineInfo; orig: string) =
var id = "" # arg = "X]:on|off" var id = "" # arg = "X]:on|off"
var i = 0 var i = 0
var n = hintMin var n = hintMin
@ -149,17 +149,17 @@ proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass,
add(id, arg[i]) add(id, arg[i])
inc(i) inc(i)
if i < len(arg) and (arg[i] == ']'): inc(i) if i < len(arg) and (arg[i] == ']'): inc(i)
else: invalidCmdLineOption(pass, arg, info) else: invalidCmdLineOption(pass, orig, info)
if i < len(arg) and (arg[i] in {':', '='}): inc(i) if i < len(arg) and (arg[i] in {':', '='}): inc(i)
else: invalidCmdLineOption(pass, arg, info) else: invalidCmdLineOption(pass, orig, info)
if state == wHint: if state == wHint:
var x = findStr(msgs.HintsToStr, id) var x = findStr(msgs.HintsToStr, id)
if x >= 0: n = TNoteKind(x + ord(hintMin)) if x >= 0: n = TNoteKind(x + ord(hintMin))
else: invalidCmdLineOption(pass, arg, info) else: localError(info, "unknown hint: " & id)
else: else:
var x = findStr(msgs.WarningsToStr, id) var x = findStr(msgs.WarningsToStr, id)
if x >= 0: n = TNoteKind(x + ord(warnMin)) if x >= 0: n = TNoteKind(x + ord(warnMin))
else: invalidCmdLineOption(pass, arg, info) else: localError(info, "unknown warning: " & id)
case whichKeyword(substr(arg, i)) case whichKeyword(substr(arg, i))
of wOn: incl(gNotes, n) of wOn: incl(gNotes, n)
of wOff: excl(gNotes, n) of wOff: excl(gNotes, n)
@ -252,11 +252,11 @@ proc trackDirty(arg: string, info: TLineInfo) =
if parseUtils.parseInt(a[3], column) <= 0: if parseUtils.parseInt(a[3], column) <= 0:
localError(info, errInvalidNumber, a[2]) localError(info, errInvalidNumber, a[2])
gDirtyBufferIdx = a[0].fileInfoIdx let dirtyOriginalIdx = a[1].fileInfoIdx
gDirtyOriginalIdx = a[1].fileInfoIdx if dirtyOriginalIdx >= 0:
msgs.setDirtyFile(dirtyOriginalIdx, a[0])
optTrackPos = newLineInfo(gDirtyBufferIdx, line, column) gTrackPos = newLineInfo(dirtyOriginalIdx, line, column)
msgs.addCheckpoint(optTrackPos)
proc track(arg: string, info: TLineInfo) = proc track(arg: string, info: TLineInfo) =
var a = arg.split(',') var a = arg.split(',')
@ -266,8 +266,7 @@ proc track(arg: string, info: TLineInfo) =
localError(info, errInvalidNumber, a[1]) localError(info, errInvalidNumber, a[1])
if parseUtils.parseInt(a[2], column) <= 0: if parseUtils.parseInt(a[2], column) <= 0:
localError(info, errInvalidNumber, a[2]) localError(info, errInvalidNumber, a[2])
optTrackPos = newLineInfo(a[0], line, column) gTrackPos = newLineInfo(a[0], line, column)
msgs.addCheckpoint(optTrackPos)
proc dynlibOverride(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = proc dynlibOverride(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if pass in {passCmd2, passPP}: if pass in {passCmd2, passPP}:
@ -320,7 +319,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
undefSymbol(arg) undefSymbol(arg)
of "symbol": of "symbol":
expectArg(switch, arg, pass, info) expectArg(switch, arg, pass, info)
declareSymbol(arg) # deprecated, do nothing
of "compile": of "compile":
expectArg(switch, arg, pass, info) expectArg(switch, arg, pass, info)
if pass in {passCmd2, passPP}: processCompile(arg) if pass in {passCmd2, passPP}: processCompile(arg)
@ -369,16 +368,26 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
defineSymbol("nogc") defineSymbol("nogc")
else: localError(info, errNoneBoehmRefcExpectedButXFound, arg) else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
of "warnings", "w": processOnOffSwitch({optWarns}, arg, pass, info) of "warnings", "w": processOnOffSwitch({optWarns}, arg, pass, info)
of "warning": processSpecificNote(arg, wWarning, pass, info) of "warning": processSpecificNote(arg, wWarning, pass, info, switch)
of "hint": processSpecificNote(arg, wHint, pass, info) of "hint": processSpecificNote(arg, wHint, pass, info, switch)
of "hints": processOnOffSwitch({optHints}, arg, pass, info) of "hints": processOnOffSwitch({optHints}, arg, pass, info)
of "threadanalysis": processOnOffSwitchG({optThreadAnalysis}, arg, pass, info) of "threadanalysis": processOnOffSwitchG({optThreadAnalysis}, arg, pass, info)
of "stacktrace": processOnOffSwitch({optStackTrace}, arg, pass, info) of "stacktrace": processOnOffSwitch({optStackTrace}, arg, pass, info)
of "linetrace": processOnOffSwitch({optLineTrace}, arg, pass, info) of "linetrace": processOnOffSwitch({optLineTrace}, arg, pass, info)
of "debugger": of "debugger":
processOnOffSwitch({optEndb}, arg, pass, info) case arg.normalize
if optEndb in gOptions: defineSymbol("endb") of "on", "endb":
else: undefSymbol("endb") gOptions.incl optEndb
defineSymbol("endb")
of "off":
gOptions.excl optEndb
undefSymbol("endb")
of "native", "gdb":
incl(gGlobalOptions, optCDebug)
gOptions = gOptions + {optLineDir} - {optEndb}
undefSymbol("endb")
else:
localError(info, "expected endb|gdb but found " & arg)
of "profiler": of "profiler":
processOnOffSwitch({optProfiler}, arg, pass, info) processOnOffSwitch({optProfiler}, arg, pass, info)
if optProfiler in gOptions: defineSymbol("profiler") if optProfiler in gOptions: defineSymbol("profiler")
@ -398,7 +407,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
of "deadcodeelim": processOnOffSwitchG({optDeadCodeElim}, arg, pass, info) of "deadcodeelim": processOnOffSwitchG({optDeadCodeElim}, arg, pass, info)
of "threads": of "threads":
processOnOffSwitchG({optThreads}, arg, pass, info) processOnOffSwitchG({optThreads}, arg, pass, info)
if optThreads in gGlobalOptions: incl(gNotes, warnGcUnsafe) #if optThreads in gGlobalOptions: incl(gNotes, warnGcUnsafe)
of "tlsemulation": processOnOffSwitchG({optTlsEmulation}, arg, pass, info) of "tlsemulation": processOnOffSwitchG({optTlsEmulation}, arg, pass, info)
of "taintmode": processOnOffSwitchG({optTaintMode}, arg, pass, info) of "taintmode": processOnOffSwitchG({optTaintMode}, arg, pass, info)
of "implicitstatic": of "implicitstatic":
@ -479,7 +488,6 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if theOS == osNone: localError(info, errUnknownOS, arg) if theOS == osNone: localError(info, errUnknownOS, arg)
elif theOS != platform.hostOS: elif theOS != platform.hostOS:
setTarget(theOS, targetCPU) setTarget(theOS, targetCPU)
condsyms.initDefines()
of "cpu": of "cpu":
expectArg(switch, arg, pass, info) expectArg(switch, arg, pass, info)
if pass in {passCmd1, passPP}: if pass in {passCmd1, passPP}:
@ -487,7 +495,6 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
if cpu == cpuNone: localError(info, errUnknownCPU, arg) if cpu == cpuNone: localError(info, errUnknownCPU, arg)
elif cpu != platform.hostCPU: elif cpu != platform.hostCPU:
setTarget(targetOS, cpu) setTarget(targetOS, cpu)
condsyms.initDefines()
of "run", "r": of "run", "r":
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optRun) incl(gGlobalOptions, optRun)
@ -541,19 +548,19 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
trackDirty(arg, info) trackDirty(arg, info)
of "suggest": of "suggest":
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optSuggest) gIdeCmd = ideSug
of "def": of "def":
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optDef) gIdeCmd = ideDef
of "eval": of "eval":
expectArg(switch, arg, pass, info) expectArg(switch, arg, pass, info)
gEvalExpr = arg gEvalExpr = arg
of "context": of "context":
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optContext) gIdeCmd = ideCon
of "usages": of "usages":
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optUsages) gIdeCmd = ideUse
of "stdout": of "stdout":
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optStdout) incl(gGlobalOptions, optStdout)

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -12,66 +12,64 @@
import import
strtabs, platform, strutils, idents strtabs, platform, strutils, idents
# We need to use a PStringTable here as defined symbols are always guaranteed # We need to use a StringTableRef here as defined symbols are always guaranteed
# to be style insensitive. Otherwise hell would break lose. # to be style insensitive. Otherwise hell would break lose.
var gSymbols: StringTableRef var gSymbols: StringTableRef
const
catNone = "false"
proc defineSymbol*(symbol: string) = proc defineSymbol*(symbol: string) =
gSymbols[symbol] = "true" gSymbols[symbol] = "true"
proc declareSymbol*(symbol: string) =
gSymbols[symbol] = "unknown"
proc undefSymbol*(symbol: string) = proc undefSymbol*(symbol: string) =
gSymbols[symbol] = "false" gSymbols[symbol] = catNone
proc isDefined*(symbol: string): bool = proc isDefined*(symbol: string): bool =
if gSymbols.hasKey(symbol): if gSymbols.hasKey(symbol):
result = gSymbols[symbol] == "true" result = gSymbols[symbol] != catNone
elif cmpIgnoreStyle(symbol, CPU[targetCPU].name) == 0:
result = true
elif cmpIgnoreStyle(symbol, platform.OS[targetOS].name) == 0:
result = true
else:
case symbol.normalize
of "x86": result = targetCPU == cpuI386
of "itanium": result = targetCPU == cpuIa64
of "x8664": result = targetCPU == cpuAmd64
of "posix", "unix":
result = targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos,
osQnx, osAtari, osAix,
osHaiku, osVxWorks, osSolaris, osNetbsd,
osFreebsd, osOpenbsd, osMacosx}
of "bsd":
result = targetOS in {osNetbsd, osFreebsd, osOpenbsd}
of "emulatedthreadvars":
result = platform.OS[targetOS].props.contains(ospLacksThreadVars)
of "msdos": result = targetOS == osDos
of "mswindows", "win32": result = targetOS == osWindows
of "macintosh": result = targetOS in {osMacos, osMacosx}
of "sunos": result = targetOS == osSolaris
of "littleendian": result = CPU[targetCPU].endian == platform.littleEndian
of "bigendian": result = CPU[targetCPU].endian == platform.bigEndian
of "cpu8": result = CPU[targetCPU].bit == 8
of "cpu16": result = CPU[targetCPU].bit == 16
of "cpu32": result = CPU[targetCPU].bit == 32
of "cpu64": result = CPU[targetCPU].bit == 64
of "nimrawsetjmp":
result = targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd, osMacosx}
else: discard
proc isDefined*(symbol: PIdent): bool = isDefined(symbol.s) proc isDefined*(symbol: PIdent): bool = isDefined(symbol.s)
proc isDeclared*(symbol: PIdent): bool = gSymbols.hasKey(symbol.s)
iterator definedSymbolNames*: string = iterator definedSymbolNames*: string =
for key, val in pairs(gSymbols): for key, val in pairs(gSymbols):
if val == "true": yield key if val != catNone: yield key
proc countDefinedSymbols*(): int = proc countDefinedSymbols*(): int =
result = 0 result = 0
for key, val in pairs(gSymbols): for key, val in pairs(gSymbols):
if val == "true": inc(result) if val != catNone: inc(result)
# For ease of bootstrapping, we keep them here and not in the global config
# file for now:
const
additionalSymbols = """
x86 itanium x8664
msdos mswindows win32 unix posix sunos bsd macintosh RISCOS hpux
mac
hppa hp9000 hp9000s300 hp9000s700 hp9000s800 hp9000s820 ELATE sparcv9
ecmascript js nimrodvm nimffi nimdoc cpp objc
gcc llvmgcc clang lcc bcc dmc wcc vcc tcc pcc ucc icl
boehmgc gcmarkandsweep gcgenerational nogc gcUseBitvectors
endb profiler
executable guiapp consoleapp library dll staticlib
quick
release debug
useWinAnsi useFork useNimRtl useMalloc useRealtimeGC ssl memProfiler
nodejs kwin nimfix
usesysassert usegcassert tinyC useFFI
useStdoutAsStdmsg createNimRtl
booting fulldebug corruption nimsuperops noSignalHandler useGnuReadline
noCaas noDocGen noBusyWaiting nativeStackTrace useNodeIds selftest
reportMissedDeadlines avoidTimeMachine useClone ignoreAllocationSize
debugExecProcesses pcreDll useLipzipSrc
preventDeadlocks UNICODE winUnicode trackGcHeaders posixRealtime
nimStdSetjmp nimRawSetjmp nimSigSetjmp
""".split
proc initDefines*() = proc initDefines*() =
gSymbols = newStringTable(modeStyleInsensitive) gSymbols = newStringTable(modeStyleInsensitive)
@ -89,58 +87,4 @@ proc initDefines*() =
defineSymbol("nimparsebiggestfloatmagic") defineSymbol("nimparsebiggestfloatmagic")
defineSymbol("nimalias") defineSymbol("nimalias")
defineSymbol("nimlocks") defineSymbol("nimlocks")
defineSymbol("nimnode")
# add platform specific symbols:
for c in low(CPU)..high(CPU):
declareSymbol("cpu" & $CPU[c].bit)
declareSymbol(normalize(EndianToStr[CPU[c].endian]))
declareSymbol(CPU[c].name)
for o in low(platform.OS)..high(platform.OS):
declareSymbol(platform.OS[o].name)
for a in additionalSymbols:
declareSymbol(a)
# -----------------------------------------------------------
case targetCPU
of cpuI386: defineSymbol("x86")
of cpuIa64: defineSymbol("itanium")
of cpuAmd64: defineSymbol("x8664")
else: discard
case targetOS
of osDos:
defineSymbol("msdos")
of osWindows:
defineSymbol("mswindows")
defineSymbol("win32")
of osLinux, osMorphos, osSkyos, osIrix, osPalmos, osQnx, osAtari, osAix,
osHaiku:
# these are all 'unix-like'
defineSymbol("unix")
defineSymbol("posix")
of osSolaris:
defineSymbol("sunos")
defineSymbol("unix")
defineSymbol("posix")
of osNetbsd, osFreebsd, osOpenbsd:
defineSymbol("unix")
defineSymbol("bsd")
defineSymbol("posix")
of osMacos:
defineSymbol("macintosh")
of osMacosx:
defineSymbol("macintosh")
defineSymbol("unix")
defineSymbol("posix")
else: discard
defineSymbol("cpu" & $CPU[targetCPU].bit)
defineSymbol(normalize(EndianToStr[CPU[targetCPU].endian]))
defineSymbol(CPU[targetCPU].name)
defineSymbol(platform.OS[targetOS].name)
declareSymbol("emulatedthreadvars")
if platform.OS[targetOS].props.contains(ospLacksThreadVars):
defineSymbol("emulatedthreadvars")
case targetOS
of osSolaris, osNetbsd, osFreebsd, osOpenbsd, osMacosx:
defineSymbol("nimRawSetjmp")
else: discard

View file

@ -19,10 +19,10 @@ type
module*: PSym module*: PSym
PGen = ref TGen PGen = ref TGen
var gDotGraph: PRope # the generated DOT file; we need a global variable var gDotGraph: Rope # the generated DOT file; we need a global variable
proc addDependencyAux(importing, imported: string) = proc addDependencyAux(importing, imported: string) =
appf(gDotGraph, "$1 -> $2;$n", [toRope(importing), toRope(imported)]) addf(gDotGraph, "$1 -> $2;$n", [rope(importing), rope(imported)])
# s1 -> s2_4[label="[0-9]"]; # s1 -> s2_4[label="[0-9]"];
proc addDotDependency(c: PPassContext, n: PNode): PNode = proc addDotDependency(c: PPassContext, n: PNode): PNode =
@ -42,8 +42,8 @@ proc addDotDependency(c: PPassContext, n: PNode): PNode =
discard discard
proc generateDot(project: string) = proc generateDot(project: string) =
writeRope(ropef("digraph $1 {$n$2}$n", [ writeRope("digraph $1 {$n$2}$n" % [
toRope(changeFileExt(extractFilename(project), "")), gDotGraph]), rope(changeFileExt(extractFilename(project), "")), gDotGraph],
changeFileExt(project, "dot")) changeFileExt(project, "dot"))
proc myOpen(module: PSym): PPassContext = proc myOpen(module: PSym): PPassContext =

View file

@ -17,12 +17,13 @@ import
importer, sempass2, json, xmltree, cgi, typesrenderer importer, sempass2, json, xmltree, cgi, typesrenderer
type type
TSections = array[TSymKind, PRope] TSections = array[TSymKind, Rope]
TDocumentor = object of rstgen.TRstGenerator TDocumentor = object of rstgen.TRstGenerator
modDesc: PRope # module description modDesc: Rope # module description
id: int # for generating IDs id: int # for generating IDs
toc, section: TSections toc, section: TSections
indexValFilename: string indexValFilename: string
analytics: string # Google Analytics javascript, "" if doesn't exist
seenSymbols: StringTableRef # avoids duplicate symbol generation for HTML. seenSymbols: StringTableRef # avoids duplicate symbol generation for HTML.
PDoc* = ref TDocumentor ## Alias to type less. PDoc* = ref TDocumentor ## Alias to type less.
@ -61,12 +62,29 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc =
initRstGenerator(result[], (if gCmd != cmdRst2tex: outHtml else: outLatex), initRstGenerator(result[], (if gCmd != cmdRst2tex: outHtml else: outLatex),
options.gConfigVars, filename, {roSupportRawDirective}, options.gConfigVars, filename, {roSupportRawDirective},
docgenFindFile, compilerMsgHandler) docgenFindFile, compilerMsgHandler)
if config.hasKey("doc.googleAnalytics"):
result.analytics = """
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', '$1', 'auto');
ga('send', 'pageview');
</script>
""" % [config["doc.googleAnalytics"]]
else:
result.analytics = ""
result.seenSymbols = newStringTable(modeCaseInsensitive) result.seenSymbols = newStringTable(modeCaseInsensitive)
result.id = 100 result.id = 100
proc dispA(dest: var PRope, xml, tex: string, args: openArray[PRope]) = proc dispA(dest: var Rope, xml, tex: string, args: openArray[Rope]) =
if gCmd != cmdRst2tex: appf(dest, xml, args) if gCmd != cmdRst2tex: addf(dest, xml, args)
else: appf(dest, tex, args) else: addf(dest, tex, args)
proc getVarIdx(varnames: openArray[string], id: string): int = proc getVarIdx(varnames: openArray[string], id: string): int =
for i in countup(0, high(varnames)): for i in countup(0, high(varnames)):
@ -74,8 +92,8 @@ proc getVarIdx(varnames: openArray[string], id: string): int =
return i return i
result = -1 result = -1
proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string], proc ropeFormatNamedVars(frmt: FormatStr, varnames: openArray[string],
varvalues: openArray[PRope]): PRope = varvalues: openArray[Rope]): Rope =
var i = 0 var i = 0
var L = len(frmt) var L = len(frmt)
result = nil result = nil
@ -85,11 +103,11 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string],
inc(i) # skip '$' inc(i) # skip '$'
case frmt[i] case frmt[i]
of '#': of '#':
app(result, varvalues[num]) add(result, varvalues[num])
inc(num) inc(num)
inc(i) inc(i)
of '$': of '$':
app(result, "$") add(result, "$")
inc(i) inc(i)
of '0'..'9': of '0'..'9':
var j = 0 var j = 0
@ -99,7 +117,7 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string],
if (i > L + 0 - 1) or not (frmt[i] in {'0'..'9'}): break if (i > L + 0 - 1) or not (frmt[i] in {'0'..'9'}): break
if j > high(varvalues) + 1: internalError("ropeFormatNamedVars") if j > high(varvalues) + 1: internalError("ropeFormatNamedVars")
num = j num = j
app(result, varvalues[j - 1]) add(result, varvalues[j - 1])
of 'A'..'Z', 'a'..'z', '\x80'..'\xFF': of 'A'..'Z', 'a'..'z', '\x80'..'\xFF':
var id = "" var id = ""
while true: while true:
@ -107,8 +125,8 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string],
inc(i) inc(i)
if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break
var idx = getVarIdx(varnames, id) var idx = getVarIdx(varnames, id)
if idx >= 0: app(result, varvalues[idx]) if idx >= 0: add(result, varvalues[idx])
else: rawMessage(errUnkownSubstitionVar, id) else: rawMessage(errUnknownSubstitionVar, id)
of '{': of '{':
var id = "" var id = ""
inc(i) inc(i)
@ -119,14 +137,14 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openArray[string],
inc(i) # skip } inc(i) # skip }
# search for the variable: # search for the variable:
var idx = getVarIdx(varnames, id) var idx = getVarIdx(varnames, id)
if idx >= 0: app(result, varvalues[idx]) if idx >= 0: add(result, varvalues[idx])
else: rawMessage(errUnkownSubstitionVar, id) else: rawMessage(errUnknownSubstitionVar, id)
else: internalError("ropeFormatNamedVars") else: internalError("ropeFormatNamedVars")
var start = i var start = i
while i < L: while i < L:
if frmt[i] != '$': inc(i) if frmt[i] != '$': inc(i)
else: break else: break
if i - 1 >= start: app(result, substr(frmt, start, i - 1)) if i - 1 >= start: add(result, substr(frmt, start, i - 1))
proc genComment(d: PDoc, n: PNode): string = proc genComment(d: PDoc, n: PNode): string =
result = "" result = ""
@ -136,9 +154,9 @@ proc genComment(d: PDoc, n: PNode): string =
toLinenumber(n.info), toColumn(n.info), toLinenumber(n.info), toColumn(n.info),
dummyHasToc, d.options + {roSkipPounds}), result) dummyHasToc, d.options + {roSkipPounds}), result)
proc genRecComment(d: PDoc, n: PNode): PRope = proc genRecComment(d: PDoc, n: PNode): Rope =
if n == nil: return nil if n == nil: return nil
result = genComment(d, n).toRope result = genComment(d, n).rope
if result == nil: if result == nil:
if n.kind notin {nkEmpty..nkNilLit}: if n.kind notin {nkEmpty..nkNilLit}:
for i in countup(0, len(n)-1): for i in countup(0, len(n)-1):
@ -254,7 +272,7 @@ proc complexName(k: TSymKind, n: PNode, baseName: string): string =
## type)?(,param type)*``. The callable type part will be added only if the ## type)?(,param type)*``. The callable type part will be added only if the
## node is not a proc, as those are the common ones. The suffix will be a dot ## node is not a proc, as those are the common ones. The suffix will be a dot
## and a single letter representing the type of the callable. The parameter ## and a single letter representing the type of the callable. The parameter
## types will be added with a preceeding dash. Return types won't be added. ## types will be added with a preceding dash. Return types won't be added.
## ##
## If you modify the output of this proc, please update the anchor generation ## If you modify the output of this proc, please update the anchor generation
## section of ``doc/docgen.txt``. ## section of ``doc/docgen.txt``.
@ -313,9 +331,9 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
if not isVisible(nameNode): return if not isVisible(nameNode): return
let let
name = getName(d, nameNode) name = getName(d, nameNode)
nameRope = name.toRope nameRope = name.rope
plainDocstring = getPlainDocstring(n) # call here before genRecComment! plainDocstring = getPlainDocstring(n) # call here before genRecComment!
var result: PRope = nil var result: Rope = nil
var literal, plainName = "" var literal, plainName = ""
var kind = tkEof var kind = tkEof
var comm = genRecComment(d, n) # call this here for the side-effect! var comm = genRecComment(d, n) # call this here for the side-effect!
@ -338,69 +356,69 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
break break
of tkComment: of tkComment:
dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}", dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}",
[toRope(esc(d.target, literal))]) [rope(esc(d.target, literal))])
of tokKeywordLow..tokKeywordHigh: of tokKeywordLow..tokKeywordHigh:
dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}", dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}",
[toRope(literal)]) [rope(literal)])
of tkOpr: of tkOpr:
dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}", dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}",
[toRope(esc(d.target, literal))]) [rope(esc(d.target, literal))])
of tkStrLit..tkTripleStrLit: of tkStrLit..tkTripleStrLit:
dispA(result, "<span class=\"StringLit\">$1</span>", dispA(result, "<span class=\"StringLit\">$1</span>",
"\\spanStringLit{$1}", [toRope(esc(d.target, literal))]) "\\spanStringLit{$1}", [rope(esc(d.target, literal))])
of tkCharLit: of tkCharLit:
dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}", dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}",
[toRope(esc(d.target, literal))]) [rope(esc(d.target, literal))])
of tkIntLit..tkUInt64Lit: of tkIntLit..tkUInt64Lit:
dispA(result, "<span class=\"DecNumber\">$1</span>", dispA(result, "<span class=\"DecNumber\">$1</span>",
"\\spanDecNumber{$1}", [toRope(esc(d.target, literal))]) "\\spanDecNumber{$1}", [rope(esc(d.target, literal))])
of tkFloatLit..tkFloat128Lit: of tkFloatLit..tkFloat128Lit:
dispA(result, "<span class=\"FloatNumber\">$1</span>", dispA(result, "<span class=\"FloatNumber\">$1</span>",
"\\spanFloatNumber{$1}", [toRope(esc(d.target, literal))]) "\\spanFloatNumber{$1}", [rope(esc(d.target, literal))])
of tkSymbol: of tkSymbol:
dispA(result, "<span class=\"Identifier\">$1</span>", dispA(result, "<span class=\"Identifier\">$1</span>",
"\\spanIdentifier{$1}", [toRope(esc(d.target, literal))]) "\\spanIdentifier{$1}", [rope(esc(d.target, literal))])
of tkSpaces, tkInvalid: of tkSpaces, tkInvalid:
app(result, literal) add(result, literal)
of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi, of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe, tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe,
tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot,
tkAccent, tkColonColon, tkAccent, tkColonColon,
tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr: tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr:
dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}", dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}",
[toRope(esc(d.target, literal))]) [rope(esc(d.target, literal))])
inc(d.id) inc(d.id)
let let
plainNameRope = toRope(xmltree.escape(plainName.strip)) plainNameRope = rope(xmltree.escape(plainName.strip))
cleanPlainSymbol = renderPlainSymbolName(nameNode) cleanPlainSymbol = renderPlainSymbolName(nameNode)
complexSymbol = complexName(k, n, cleanPlainSymbol) complexSymbol = complexName(k, n, cleanPlainSymbol)
plainSymbolRope = toRope(cleanPlainSymbol) plainSymbolRope = rope(cleanPlainSymbol)
plainSymbolEncRope = toRope(encodeUrl(cleanPlainSymbol)) plainSymbolEncRope = rope(encodeUrl(cleanPlainSymbol))
itemIDRope = toRope(d.id) itemIDRope = rope(d.id)
symbolOrId = d.newUniquePlainSymbol(complexSymbol) symbolOrId = d.newUniquePlainSymbol(complexSymbol)
symbolOrIdRope = symbolOrId.toRope symbolOrIdRope = symbolOrId.rope
symbolOrIdEncRope = encodeUrl(symbolOrId).toRope symbolOrIdEncRope = encodeUrl(symbolOrId).rope
var seeSrcRope: PRope = nil var seeSrcRope: Rope = nil
let docItemSeeSrc = getConfigVar("doc.item.seesrc") let docItemSeeSrc = getConfigVar("doc.item.seesrc")
if docItemSeeSrc.len > 0 and options.docSeeSrcUrl.len > 0: if docItemSeeSrc.len > 0 and options.docSeeSrcUrl.len > 0:
# XXX toFilename doesn't really work. We need to ensure that this keeps # XXX toFilename doesn't really work. We need to ensure that this keeps
# returning a relative path. # returning a relative path.
let urlRope = ropeFormatNamedVars(options.docSeeSrcUrl, let urlRope = ropeFormatNamedVars(options.docSeeSrcUrl,
["path", "line"], [n.info.toFilename.toRope, toRope($n.info.line)]) ["path", "line"], [n.info.toFilename.rope, rope($n.info.line)])
dispA(seeSrcRope, "$1", "", [ropeFormatNamedVars(docItemSeeSrc, dispA(seeSrcRope, "$1", "", [ropeFormatNamedVars(docItemSeeSrc,
["path", "line", "url"], [n.info.toFilename.toRope, ["path", "line", "url"], [n.info.toFilename.rope,
toRope($n.info.line), urlRope])]) rope($n.info.line), urlRope])])
app(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"), add(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"),
["name", "header", "desc", "itemID", "header_plain", "itemSym", ["name", "header", "desc", "itemID", "header_plain", "itemSym",
"itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "seeSrc"], "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "seeSrc"],
[nameRope, result, comm, itemIDRope, plainNameRope, plainSymbolRope, [nameRope, result, comm, itemIDRope, plainNameRope, plainSymbolRope,
symbolOrIdRope, plainSymbolEncRope, symbolOrIdEncRope, seeSrcRope])) symbolOrIdRope, plainSymbolEncRope, symbolOrIdEncRope, seeSrcRope]))
app(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"), add(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"),
["name", "header", "desc", "itemID", "header_plain", "itemSym", ["name", "header", "desc", "itemID", "header_plain", "itemSym",
"itemSymOrID", "itemSymEnc", "itemSymOrIDEnc"], "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc"],
[toRope(getName(d, nameNode, d.splitAfter)), result, comm, [rope(getName(d, nameNode, d.splitAfter)), result, comm,
itemIDRope, plainNameRope, plainSymbolRope, symbolOrIdRope, itemIDRope, plainNameRope, plainSymbolRope, symbolOrIdRope,
plainSymbolEncRope, symbolOrIdEncRope])) plainSymbolEncRope, symbolOrIdEncRope]))
@ -418,7 +436,7 @@ proc genJSONItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode =
if not isVisible(nameNode): return if not isVisible(nameNode): return
var var
name = getName(d, nameNode) name = getName(d, nameNode)
comm = genRecComment(d, n).ropeToStr() comm = $genRecComment(d, n)
r: TSrcGen r: TSrcGen
initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments}) initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
@ -435,14 +453,14 @@ proc checkForFalse(n: PNode): bool =
proc traceDeps(d: PDoc, n: PNode) = proc traceDeps(d: PDoc, n: PNode) =
const k = skModule const k = skModule
if d.section[k] != nil: app(d.section[k], ", ") if d.section[k] != nil: add(d.section[k], ", ")
dispA(d.section[k], dispA(d.section[k],
"<a class=\"reference external\" href=\"$1.html\">$1</a>", "<a class=\"reference external\" href=\"$1.html\">$1</a>",
"$1", [toRope(getModuleName(n))]) "$1", [rope(getModuleName(n))])
proc generateDoc*(d: PDoc, n: PNode) = proc generateDoc*(d: PDoc, n: PNode) =
case n.kind case n.kind
of nkCommentStmt: app(d.modDesc, genComment(d, n)) of nkCommentStmt: add(d.modDesc, genComment(d, n))
of nkProcDef: of nkProcDef:
when useEffectSystem: documentRaises(n) when useEffectSystem: documentRaises(n)
genItem(d, n, n.sons[namePos], skProc) genItem(d, n, n.sons[namePos], skProc)
@ -503,13 +521,13 @@ proc generateJson(d: PDoc, n: PNode, jArray: JsonNode = nil): JsonNode =
result = genJSONItem(d, n.sons[i], n.sons[i].sons[0], result = genJSONItem(d, n.sons[i], n.sons[i].sons[0],
succ(skType, ord(n.kind)-ord(nkTypeSection))) succ(skType, ord(n.kind)-ord(nkTypeSection)))
of nkStmtList: of nkStmtList:
var elem = jArray result = if jArray != nil: jArray else: newJArray()
if elem == nil: elem = newJArray()
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var r = generateJson(d, n.sons[i], elem) var r = generateJson(d, n.sons[i], result)
if r != nil: if r != nil:
elem.add(r) result.add(r)
if result == nil: result = elem
of nkWhenStmt: of nkWhenStmt:
# generate documentation for the first branch only: # generate documentation for the first branch only:
if not checkForFalse(n.sons[0].sons[0]) and jArray != nil: if not checkForFalse(n.sons[0].sons[0]) and jArray != nil:
@ -522,28 +540,28 @@ proc genSection(d: PDoc, kind: TSymKind) =
"Iterators", "Iterators", "Converters", "Macros", "Templates" "Iterators", "Iterators", "Converters", "Macros", "Templates"
] ]
if d.section[kind] == nil: return if d.section[kind] == nil: return
var title = sectionNames[kind].toRope var title = sectionNames[kind].rope
d.section[kind] = ropeFormatNamedVars(getConfigVar("doc.section"), [ d.section[kind] = ropeFormatNamedVars(getConfigVar("doc.section"), [
"sectionid", "sectionTitle", "sectionTitleID", "content"], [ "sectionid", "sectionTitle", "sectionTitleID", "content"], [
ord(kind).toRope, title, toRope(ord(kind) + 50), d.section[kind]]) ord(kind).rope, title, rope(ord(kind) + 50), d.section[kind]])
d.toc[kind] = ropeFormatNamedVars(getConfigVar("doc.section.toc"), [ d.toc[kind] = ropeFormatNamedVars(getConfigVar("doc.section.toc"), [
"sectionid", "sectionTitle", "sectionTitleID", "content"], [ "sectionid", "sectionTitle", "sectionTitleID", "content"], [
ord(kind).toRope, title, toRope(ord(kind) + 50), d.toc[kind]]) ord(kind).rope, title, rope(ord(kind) + 50), d.toc[kind]])
proc genOutFile(d: PDoc): PRope = proc genOutFile(d: PDoc): Rope =
var var
code, content: PRope code, content: Rope
title = "" title = ""
var j = 0 var j = 0
var tmp = "" var tmp = ""
renderTocEntries(d[], j, 1, tmp) renderTocEntries(d[], j, 1, tmp)
var toc = tmp.toRope var toc = tmp.rope
for i in countup(low(TSymKind), high(TSymKind)): for i in countup(low(TSymKind), high(TSymKind)):
genSection(d, i) genSection(d, i)
app(toc, d.toc[i]) add(toc, d.toc[i])
if toc != nil: if toc != nil:
toc = ropeFormatNamedVars(getConfigVar("doc.toc"), ["content"], [toc]) toc = ropeFormatNamedVars(getConfigVar("doc.toc"), ["content"], [toc])
for i in countup(low(TSymKind), high(TSymKind)): app(code, d.section[i]) for i in countup(low(TSymKind), high(TSymKind)): add(code, d.section[i])
# Extract the title. Non API modules generate an entry in the index table. # Extract the title. Non API modules generate an entry in the index table.
if d.meta[metaTitle].len != 0: if d.meta[metaTitle].len != 0:
@ -556,16 +574,16 @@ proc genOutFile(d: PDoc): PRope =
let bodyname = if d.hasToc: "doc.body_toc" else: "doc.body_no_toc" let bodyname = if d.hasToc: "doc.body_toc" else: "doc.body_no_toc"
content = ropeFormatNamedVars(getConfigVar(bodyname), ["title", content = ropeFormatNamedVars(getConfigVar(bodyname), ["title",
"tableofcontents", "moduledesc", "date", "time", "content"], "tableofcontents", "moduledesc", "date", "time", "content"],
[title.toRope, toc, d.modDesc, toRope(getDateStr()), [title.rope, toc, d.modDesc, rope(getDateStr()),
toRope(getClockStr()), code]) rope(getClockStr()), code])
if optCompileOnly notin gGlobalOptions: if optCompileOnly notin gGlobalOptions:
# XXX what is this hack doing here? 'optCompileOnly' means raw output!? # XXX what is this hack doing here? 'optCompileOnly' means raw output!?
code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
"tableofcontents", "moduledesc", "date", "time", "tableofcontents", "moduledesc", "date", "time",
"content", "author", "version"], "content", "author", "version", "analytics"],
[title.toRope, toc, d.modDesc, toRope(getDateStr()), [title.rope, toc, d.modDesc, rope(getDateStr()),
toRope(getClockStr()), content, d.meta[metaAuthor].toRope, rope(getClockStr()), content, d.meta[metaAuthor].rope,
d.meta[metaVersion].toRope]) d.meta[metaVersion].rope, d.analytics.rope])
else: else:
code = content code = content
result = code result = code
@ -600,7 +618,7 @@ proc commandRstAux(filename, outExt: string) =
#d.modDesc = newMutableRope(30_000) #d.modDesc = newMutableRope(30_000)
renderRstToOut(d[], rst, modDesc) renderRstToOut(d[], rst, modDesc)
#freezeMutableRope(d.modDesc) #freezeMutableRope(d.modDesc)
d.modDesc = toRope(modDesc) d.modDesc = rope(modDesc)
writeOutput(d, filename, outExt) writeOutput(d, filename, outExt)
generateIndex(d) generateIndex(d)
@ -617,7 +635,7 @@ proc commandJSON*() =
var d = newDocumentor(gProjectFull, options.gConfigVars) var d = newDocumentor(gProjectFull, options.gConfigVars)
d.hasToc = true d.hasToc = true
var json = generateJson(d, ast) var json = generateJson(d, ast)
var content = newRope(pretty(json)) var content = rope(pretty(json))
if optStdout in gGlobalOptions: if optStdout in gGlobalOptions:
writeRope(stdout, content) writeRope(stdout, content)
@ -626,11 +644,12 @@ proc commandJSON*() =
writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false) writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false)
proc commandBuildIndex*() = proc commandBuildIndex*() =
var content = mergeIndexes(gProjectFull).toRope var content = mergeIndexes(gProjectFull).rope
let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
"tableofcontents", "moduledesc", "date", "time", "tableofcontents", "moduledesc", "date", "time",
"content", "author", "version"], "content", "author", "version", "analytics"],
["Index".toRope, nil, nil, toRope(getDateStr()), ["Index".rope, nil, nil, rope(getDateStr()),
toRope(getClockStr()), content, nil, nil]) rope(getClockStr()), content, nil, nil, nil])
# no analytics because context is not available
writeRope(code, getOutFile("theindex", HtmlExt)) writeRope(code, getOutFile("theindex", HtmlExt))

View file

@ -1,495 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This file implements the FFI part of the evaluator for Nim code.
import ast, astalgo, ropes, types, options, tables, dynlib, libffi, msgs, os
when defined(windows):
const libcDll = "msvcrt.dll"
else:
const libcDll = "libc.so(.6|.5|)"
type
TDllCache = tables.TTable[string, TLibHandle]
var
gDllCache = initTable[string, TLibHandle]()
when defined(windows):
var gExeHandle = loadLib(os.getAppFilename())
else:
var gExeHandle = loadLib()
proc getDll(cache: var TDllCache; dll: string; info: TLineInfo): pointer =
result = cache[dll]
if result.isNil:
var libs: seq[string] = @[]
libCandidates(dll, libs)
for c in libs:
result = loadLib(c)
if not result.isNil: break
if result.isNil:
globalError(info, "cannot load: " & dll)
cache[dll] = result
const
nkPtrLit = nkIntLit # hopefully we can get rid of this hack soon
var myerrno {.importc: "errno", header: "<errno.h>".}: cint ## error variable
proc importcSymbol*(sym: PSym): PNode =
let name = ropeToStr(sym.loc.r)
# the AST does not support untyped pointers directly, so we use an nkIntLit
# that contains the address instead:
result = newNodeIT(nkPtrLit, sym.info, sym.typ)
case name
of "stdin": result.intVal = cast[TAddress](system.stdin)
of "stdout": result.intVal = cast[TAddress](system.stdout)
of "stderr": result.intVal = cast[TAddress](system.stderr)
of "vmErrnoWrapper": result.intVal = cast[TAddress](myerrno)
else:
let lib = sym.annex
if lib != nil and lib.path.kind notin {nkStrLit..nkTripleStrLit}:
globalError(sym.info, "dynlib needs to be a string lit for the REPL")
var theAddr: pointer
if lib.isNil and not gExehandle.isNil:
# first try this exe itself:
theAddr = gExehandle.symAddr(name)
# then try libc:
if theAddr.isNil:
let dllhandle = gDllCache.getDll(libcDll, sym.info)
theAddr = dllhandle.symAddr(name)
elif not lib.isNil:
let dllhandle = gDllCache.getDll(if lib.kind == libHeader: libcDll
else: lib.path.strVal, sym.info)
theAddr = dllhandle.symAddr(name)
if theAddr.isNil: globalError(sym.info, "cannot import: " & sym.name.s)
result.intVal = cast[TAddress](theAddr)
proc mapType(t: ast.PType): ptr libffi.TType =
if t == nil: return addr libffi.type_void
case t.kind
of tyBool, tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64, tySet:
case t.getSize
of 1: result = addr libffi.type_uint8
of 2: result = addr libffi.type_sint16
of 4: result = addr libffi.type_sint32
of 8: result = addr libffi.type_sint64
else: result = nil
of tyFloat, tyFloat64: result = addr libffi.type_double
of tyFloat32: result = addr libffi.type_float
of tyVar, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr,
tyStmt, tyTypeDesc, tyProc, tyArray, tyArrayConstr, tyStatic, tyNil:
result = addr libffi.type_pointer
of tyDistinct:
result = mapType(t.sons[0])
else:
result = nil
# too risky:
#of tyFloat128: result = addr libffi.type_longdouble
proc mapCallConv(cc: TCallingConvention, info: TLineInfo): TABI =
case cc
of ccDefault: result = DEFAULT_ABI
of ccStdCall: result = when defined(windows): STDCALL else: DEFAULT_ABI
of ccCDecl: result = DEFAULT_ABI
else:
globalError(info, "cannot map calling convention to FFI")
template rd(T, p: expr): expr {.immediate.} = (cast[ptr T](p))[]
template wr(T, p, v: expr) {.immediate.} = (cast[ptr T](p))[] = v
template `+!`(x, y: expr): expr {.immediate.} =
cast[pointer](cast[TAddress](x) + y)
proc packSize(v: PNode, typ: PType): int =
## computes the size of the blob
case typ.kind
of tyPtr, tyRef, tyVar:
if v.kind in {nkNilLit, nkPtrLit}:
result = sizeof(pointer)
else:
result = sizeof(pointer) + packSize(v.sons[0], typ.lastSon)
of tyDistinct, tyGenericInst:
result = packSize(v, typ.sons[0])
of tyArray, tyArrayConstr:
# consider: ptr array[0..1000_000, int] which is common for interfacing;
# we use the real length here instead
if v.kind in {nkNilLit, nkPtrLit}:
result = sizeof(pointer)
elif v.len != 0:
result = v.len * packSize(v.sons[0], typ.sons[1])
else:
result = typ.getSize.int
proc pack(v: PNode, typ: PType, res: pointer)
proc getField(n: PNode; position: int): PSym =
case n.kind
of nkRecList:
for i in countup(0, sonsLen(n) - 1):
result = getField(n.sons[i], position)
if result != nil: return
of nkRecCase:
result = getField(n.sons[0], position)
if result != nil: return
for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind
of nkOfBranch, nkElse:
result = getField(lastSon(n.sons[i]), position)
if result != nil: return
else: internalError(n.info, "getField(record case branch)")
of nkSym:
if n.sym.position == position: result = n.sym
else: discard
proc packObject(x: PNode, typ: PType, res: pointer) =
internalAssert x.kind in {nkObjConstr, nkPar}
# compute the field's offsets:
discard typ.getSize
for i in countup(ord(x.kind == nkObjConstr), sonsLen(x) - 1):
var it = x.sons[i]
if it.kind == nkExprColonExpr:
internalAssert it.sons[0].kind == nkSym
let field = it.sons[0].sym
pack(it.sons[1], field.typ, res +! field.offset)
elif typ.n != nil:
let field = getField(typ.n, i)
pack(it, field.typ, res +! field.offset)
else:
globalError(x.info, "cannot pack unnamed tuple")
const maxPackDepth = 20
var packRecCheck = 0
proc pack(v: PNode, typ: PType, res: pointer) =
template awr(T, v: expr) {.immediate, dirty.} =
wr(T, res, v)
case typ.kind
of tyBool: awr(bool, v.intVal != 0)
of tyChar: awr(char, v.intVal.chr)
of tyInt: awr(int, v.intVal.int)
of tyInt8: awr(int8, v.intVal.int8)
of tyInt16: awr(int16, v.intVal.int16)
of tyInt32: awr(int32, v.intVal.int32)
of tyInt64: awr(int64, v.intVal.int64)
of tyUInt: awr(uint, v.intVal.uint)
of tyUInt8: awr(uint8, v.intVal.uint8)
of tyUInt16: awr(uint16, v.intVal.uint16)
of tyUInt32: awr(uint32, v.intVal.uint32)
of tyUInt64: awr(uint64, v.intVal.uint64)
of tyEnum, tySet:
case v.typ.getSize
of 1: awr(uint8, v.intVal.uint8)
of 2: awr(uint16, v.intVal.uint16)
of 4: awr(int32, v.intVal.int32)
of 8: awr(int64, v.intVal.int64)
else:
globalError(v.info, "cannot map value to FFI (tyEnum, tySet)")
of tyFloat: awr(float, v.floatVal)
of tyFloat32: awr(float32, v.floatVal)
of tyFloat64: awr(float64, v.floatVal)
of tyPointer, tyProc, tyCString, tyString:
if v.kind == nkNilLit:
# nothing to do since the memory is 0 initialized anyway
discard
elif v.kind == nkPtrLit:
awr(pointer, cast[pointer](v.intVal))
elif v.kind in {nkStrLit..nkTripleStrLit}:
awr(cstring, cstring(v.strVal))
else:
globalError(v.info, "cannot map pointer/proc value to FFI")
of tyPtr, tyRef, tyVar:
if v.kind == nkNilLit:
# nothing to do since the memory is 0 initialized anyway
discard
elif v.kind == nkPtrLit:
awr(pointer, cast[pointer](v.intVal))
else:
if packRecCheck > maxPackDepth:
packRecCheck = 0
globalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
inc packRecCheck
pack(v.sons[0], typ.lastSon, res +! sizeof(pointer))
dec packRecCheck
awr(pointer, res +! sizeof(pointer))
of tyArray, tyArrayConstr:
let baseSize = typ.sons[1].getSize
for i in 0 .. <v.len:
pack(v.sons[i], typ.sons[1], res +! i * baseSize)
of tyObject, tyTuple:
packObject(v, typ, res)
of tyNil:
discard
of tyDistinct, tyGenericInst:
pack(v, typ.sons[0], res)
else:
globalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
proc unpack(x: pointer, typ: PType, n: PNode): PNode
proc unpackObjectAdd(x: pointer, n, result: PNode) =
case n.kind
of nkRecList:
for i in countup(0, sonsLen(n) - 1):
unpackObjectAdd(x, n.sons[i], result)
of nkRecCase:
globalError(result.info, "case objects cannot be unpacked")
of nkSym:
var pair = newNodeI(nkExprColonExpr, result.info, 2)
pair.sons[0] = n
pair.sons[1] = unpack(x +! n.sym.offset, n.sym.typ, nil)
#echo "offset: ", n.sym.name.s, " ", n.sym.offset
result.add pair
else: discard
proc unpackObject(x: pointer, typ: PType, n: PNode): PNode =
# compute the field's offsets:
discard typ.getSize
# iterate over any actual field of 'n' ... if n is nil we need to create
# the nkPar node:
if n.isNil:
result = newNode(nkPar)
result.typ = typ
if typ.n.isNil:
internalError("cannot unpack unnamed tuple")
unpackObjectAdd(x, typ.n, result)
else:
result = n
if result.kind notin {nkObjConstr, nkPar}:
globalError(n.info, "cannot map value from FFI")
if typ.n.isNil:
globalError(n.info, "cannot unpack unnamed tuple")
for i in countup(ord(n.kind == nkObjConstr), sonsLen(n) - 1):
var it = n.sons[i]
if it.kind == nkExprColonExpr:
internalAssert it.sons[0].kind == nkSym
let field = it.sons[0].sym
it.sons[1] = unpack(x +! field.offset, field.typ, it.sons[1])
else:
let field = getField(typ.n, i)
n.sons[i] = unpack(x +! field.offset, field.typ, it)
proc unpackArray(x: pointer, typ: PType, n: PNode): PNode =
if n.isNil:
result = newNode(nkBracket)
result.typ = typ
newSeq(result.sons, lengthOrd(typ).int)
else:
result = n
if result.kind != nkBracket:
globalError(n.info, "cannot map value from FFI")
let baseSize = typ.sons[1].getSize
for i in 0 .. < result.len:
result.sons[i] = unpack(x +! i * baseSize, typ.sons[1], result.sons[i])
proc canonNodeKind(k: TNodeKind): TNodeKind =
case k
of nkCharLit..nkUInt64Lit: result = nkIntLit
of nkFloatLit..nkFloat128Lit: result = nkFloatLit
of nkStrLit..nkTripleStrLit: result = nkStrLit
else: result = k
proc unpack(x: pointer, typ: PType, n: PNode): PNode =
template aw(k, v, field: expr) {.immediate, dirty.} =
if n.isNil:
result = newNode(k)
result.typ = typ
else:
# check we have the right field:
result = n
if result.kind.canonNodeKind != k.canonNodeKind:
#echo "expected ", k, " but got ", result.kind
#debug result
return newNodeI(nkExceptBranch, n.info)
#globalError(n.info, "cannot map value from FFI")
result.field = v
template setNil() =
if n.isNil:
result = newNode(nkNilLit)
result.typ = typ
else:
reset n[]
result = n
result.kind = nkNilLit
result.typ = typ
template awi(kind, v: expr) {.immediate, dirty.} = aw(kind, v, intVal)
template awf(kind, v: expr) {.immediate, dirty.} = aw(kind, v, floatVal)
template aws(kind, v: expr) {.immediate, dirty.} = aw(kind, v, strVal)
case typ.kind
of tyBool: awi(nkIntLit, rd(bool, x).ord)
of tyChar: awi(nkCharLit, rd(char, x).ord)
of tyInt: awi(nkIntLit, rd(int, x))
of tyInt8: awi(nkInt8Lit, rd(int8, x))
of tyInt16: awi(nkInt16Lit, rd(int16, x))
of tyInt32: awi(nkInt32Lit, rd(int32, x))
of tyInt64: awi(nkInt64Lit, rd(int64, x))
of tyUInt: awi(nkUIntLit, rd(uint, x).BiggestInt)
of tyUInt8: awi(nkUInt8Lit, rd(uint8, x).BiggestInt)
of tyUInt16: awi(nkUInt16Lit, rd(uint16, x).BiggestInt)
of tyUInt32: awi(nkUInt32Lit, rd(uint32, x).BiggestInt)
of tyUInt64: awi(nkUInt64Lit, rd(uint64, x).BiggestInt)
of tyEnum:
case typ.getSize
of 1: awi(nkIntLit, rd(uint8, x).BiggestInt)
of 2: awi(nkIntLit, rd(uint16, x).BiggestInt)
of 4: awi(nkIntLit, rd(int32, x).BiggestInt)
of 8: awi(nkIntLit, rd(int64, x).BiggestInt)
else:
globalError(n.info, "cannot map value from FFI (tyEnum, tySet)")
of tyFloat: awf(nkFloatLit, rd(float, x))
of tyFloat32: awf(nkFloat32Lit, rd(float32, x))
of tyFloat64: awf(nkFloat64Lit, rd(float64, x))
of tyPointer, tyProc:
let p = rd(pointer, x)
if p.isNil:
setNil()
elif n != nil and n.kind == nkStrLit:
# we passed a string literal as a pointer; however strings are already
# in their unboxed representation so nothing it to be unpacked:
result = n
else:
awi(nkPtrLit, cast[TAddress](p))
of tyPtr, tyRef, tyVar:
let p = rd(pointer, x)
if p.isNil:
setNil()
elif n == nil or n.kind == nkPtrLit:
awi(nkPtrLit, cast[TAddress](p))
elif n != nil and n.len == 1:
internalAssert n.kind == nkRefTy
n.sons[0] = unpack(p, typ.lastSon, n.sons[0])
result = n
else:
globalError(n.info, "cannot map value from FFI " & typeToString(typ))
of tyObject, tyTuple:
result = unpackObject(x, typ, n)
of tyArray, tyArrayConstr:
result = unpackArray(x, typ, n)
of tyCString, tyString:
let p = rd(cstring, x)
if p.isNil:
setNil()
else:
aws(nkStrLit, $p)
of tyNil:
setNil()
of tyDistinct, tyGenericInst:
result = unpack(x, typ.sons[0], n)
else:
# XXX what to do with 'array' here?
globalError(n.info, "cannot map value from FFI " & typeToString(typ))
proc fficast*(x: PNode, destTyp: PType): PNode =
if x.kind == nkPtrLit and x.typ.kind in {tyPtr, tyRef, tyVar, tyPointer,
tyProc, tyCString, tyString,
tySequence}:
result = newNodeIT(x.kind, x.info, destTyp)
result.intVal = x.intVal
elif x.kind == nkNilLit:
result = newNodeIT(x.kind, x.info, destTyp)
else:
# we play safe here and allocate the max possible size:
let size = max(packSize(x, x.typ), packSize(x, destTyp))
var a = alloc0(size)
pack(x, x.typ, a)
# cast through a pointer needs a new inner object:
let y = if x.kind == nkRefTy: newNodeI(nkRefTy, x.info, 1)
else: x.copyTree
y.typ = x.typ
result = unpack(a, destTyp, y)
dealloc a
proc callForeignFunction*(call: PNode): PNode =
internalAssert call.sons[0].kind == nkPtrLit
var cif: TCif
var sig: TParamList
# use the arguments' types for varargs support:
for i in 1..call.len-1:
sig[i-1] = mapType(call.sons[i].typ)
if sig[i-1].isNil:
globalError(call.info, "cannot map FFI type")
let typ = call.sons[0].typ
if prep_cif(cif, mapCallConv(typ.callConv, call.info), cuint(call.len-1),
mapType(typ.sons[0]), sig) != OK:
globalError(call.info, "error in FFI call")
var args: TArgList
let fn = cast[pointer](call.sons[0].intVal)
for i in 1 .. call.len-1:
var t = call.sons[i].typ
args[i-1] = alloc0(packSize(call.sons[i], t))
pack(call.sons[i], t, args[i-1])
let retVal = if isEmptyType(typ.sons[0]): pointer(nil)
else: alloc(typ.sons[0].getSize.int)
libffi.call(cif, fn, retVal, args)
if retVal.isNil:
result = emptyNode
else:
result = unpack(retVal, typ.sons[0], nil)
result.info = call.info
if retVal != nil: dealloc retVal
for i in 1 .. call.len-1:
call.sons[i] = unpack(args[i-1], typ.sons[i], call[i])
dealloc args[i-1]
proc callForeignFunction*(fn: PNode, fntyp: PType,
args: var TNodeSeq, start, len: int,
info: TLineInfo): PNode =
internalAssert fn.kind == nkPtrLit
var cif: TCif
var sig: TParamList
for i in 0..len-1:
var aTyp = args[i+start].typ
if aTyp.isNil:
internalAssert i+1 < fntyp.len
aTyp = fntyp.sons[i+1]
args[i+start].typ = aTyp
sig[i] = mapType(aTyp)
if sig[i].isNil: globalError(info, "cannot map FFI type")
if prep_cif(cif, mapCallConv(fntyp.callConv, info), cuint(len),
mapType(fntyp.sons[0]), sig) != OK:
globalError(info, "error in FFI call")
var cargs: TArgList
let fn = cast[pointer](fn.intVal)
for i in 0 .. len-1:
let t = args[i+start].typ
cargs[i] = alloc0(packSize(args[i+start], t))
pack(args[i+start], t, cargs[i])
let retVal = if isEmptyType(fntyp.sons[0]): pointer(nil)
else: alloc(fntyp.sons[0].getSize.int)
libffi.call(cif, fn, retVal, cargs)
if retVal.isNil:
result = emptyNode
else:
result = unpack(retVal, fntyp.sons[0], nil)
result.info = info
if retVal != nil: dealloc retVal
for i in 0 .. len-1:
let t = args[i+start].typ
args[i+start] = unpack(cargs[i], t, args[i+start])
dealloc cargs[i]

View file

@ -29,7 +29,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
of nkSym: of nkSym:
var s = templ.sym var s = templ.sym
if s.owner.id == c.owner.id: if s.owner.id == c.owner.id:
if s.kind == skParam: if s.kind == skParam and sfGenSym notin s.flags:
let x = actual.sons[s.position] let x = actual.sons[s.position]
if x.kind == nkArgList: if x.kind == nkArgList:
for y in items(x): result.add(y) for y in items(x): result.add(y)
@ -93,7 +93,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym): PNode =
evalTemplateAux(body, args, ctx, result) evalTemplateAux(body, args, ctx, result)
if result.len == 1: result = result.sons[0] if result.len == 1: result = result.sons[0]
else: else:
globalError(result.info, errIllFormedAstX, localError(result.info, errIllFormedAstX,
renderTree(result, {renderNoComments})) renderTree(result, {renderNoComments}))
else: else:
result = copyNode(body) result = copyNode(body)

View file

@ -332,7 +332,6 @@ const
ucc(), ucc(),
icl()] icl()]
const
hExt* = ".h" hExt* = ".h"
var var
@ -364,7 +363,16 @@ proc nameToCC*(name: string): TSystemCC =
proc getConfigVar(c: TSystemCC, suffix: string): string = proc getConfigVar(c: TSystemCC, suffix: string): string =
# use ``cpu.os.cc`` for cross compilation, unless ``--compileOnly`` is given # use ``cpu.os.cc`` for cross compilation, unless ``--compileOnly`` is given
# for niminst support # for niminst support
let fullSuffix = (if gCmd == cmdCompileToCpp: ".cpp" & suffix else: suffix) let fullSuffix =
if gCmd == cmdCompileToCpp:
".cpp" & suffix
elif gCmd == cmdCompileToOC:
".objc" & suffix
elif gCmd == cmdCompileToJS:
".js" & suffix
else:
suffix
if (platform.hostOS != targetOS or platform.hostCPU != targetCPU) and if (platform.hostOS != targetOS or platform.hostCPU != targetCPU) and
optCompileOnly notin gGlobalOptions: optCompileOnly notin gGlobalOptions:
let fullCCname = platform.CPU[targetCPU].name & '.' & let fullCCname = platform.CPU[targetCPU].name & '.' &
@ -381,7 +389,7 @@ proc setCC*(ccname: string) =
cCompiler = nameToCC(ccname) cCompiler = nameToCC(ccname)
if cCompiler == ccNone: rawMessage(errUnknownCcompiler, ccname) if cCompiler == ccNone: rawMessage(errUnknownCcompiler, ccname)
compileOptions = getConfigVar(cCompiler, ".options.always") compileOptions = getConfigVar(cCompiler, ".options.always")
linkOptions = getConfigVar(cCompiler, ".options.linker") linkOptions = ""
ccompilerpath = getConfigVar(cCompiler, ".path") ccompilerpath = getConfigVar(cCompiler, ".path")
for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name) for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
defineSymbol(CC[cCompiler].name) defineSymbol(CC[cCompiler].name)
@ -391,7 +399,7 @@ proc addOpt(dest: var string, src: string) =
add(dest, src) add(dest, src)
proc addLinkOption*(option: string) = proc addLinkOption*(option: string) =
if find(linkOptions, option, 0) < 0: addOpt(linkOptions, option) addOpt(linkOptions, option)
proc addCompileOption*(option: string) = proc addCompileOption*(option: string) =
if strutils.find(compileOptions, option, 0) < 0: if strutils.find(compileOptions, option, 0) < 0:
@ -402,7 +410,7 @@ proc initVars*() =
for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name) for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
defineSymbol(CC[cCompiler].name) defineSymbol(CC[cCompiler].name)
addCompileOption(getConfigVar(cCompiler, ".options.always")) addCompileOption(getConfigVar(cCompiler, ".options.always"))
addLinkOption(getConfigVar(cCompiler, ".options.linker")) #addLinkOption(getConfigVar(cCompiler, ".options.linker"))
if len(ccompilerpath) == 0: if len(ccompilerpath) == 0:
ccompilerpath = getConfigVar(cCompiler, ".path") ccompilerpath = getConfigVar(cCompiler, ".path")
@ -429,15 +437,19 @@ proc addFileToLink*(filename: string) =
prependStr(toLink, filename) prependStr(toLink, filename)
# BUGFIX: was ``appendStr`` # BUGFIX: was ``appendStr``
proc execExternalProgram*(cmd: string, prettyCmd = "") = proc execWithEcho(cmd: string, prettyCmd = ""): int =
if optListCmd in gGlobalOptions or gVerbosity > 0: if optListCmd in gGlobalOptions or gVerbosity > 0:
if prettyCmd != "": if prettyCmd != "":
msgWriteln(prettyCmd) msgWriteln(prettyCmd)
else: else:
msgWriteln(cmd) msgWriteln(cmd)
if execCmd(cmd) != 0: rawMessage(errExecutionOfProgramFailed, "") result = execCmd(cmd)
proc generateScript(projectFile: string, script: PRope) = proc execExternalProgram*(cmd: string, prettyCmd = "") =
if execWithEcho(cmd, prettyCmd) != 0:
rawMessage(errExecutionOfProgramFailed, "")
proc generateScript(projectFile: string, script: Rope) =
let (dir, name, ext) = splitFile(projectFile) let (dir, name, ext) = splitFile(projectFile)
writeRope(script, dir / addFileExt("compile_" & name, writeRope(script, dir / addFileExt("compile_" & name,
platform.OS[targetOS].scriptExt)) platform.OS[targetOS].scriptExt))
@ -547,15 +559,16 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
else: else:
completeCFilePath(toObjFile(cfile)) completeCFilePath(toObjFile(cfile))
objfile = quoteShell(objfile) objfile = quoteShell(objfile)
cfile = quoteShell(cfile)
result = quoteShell(compilePattern % [ result = quoteShell(compilePattern % [
"file", cfile, "objfile", objfile, "options", options, "file", cfile, "objfile", objfile, "options", options,
"include", includeCmd, "nimrod", getPrefixDir(), "include", includeCmd, "nim", getPrefixDir(),
"nim", getPrefixDir(), "lib", libpath]) "nim", getPrefixDir(), "lib", libpath])
add(result, ' ') add(result, ' ')
addf(result, CC[c].compileTmpl, [ addf(result, CC[c].compileTmpl, [
"file", cfile, "objfile", objfile, "file", cfile, "objfile", objfile,
"options", options, "include", includeCmd, "options", options, "include", includeCmd,
"nimrod", quoteShell(getPrefixDir()), "nim", quoteShell(getPrefixDir()),
"nim", quoteShell(getPrefixDir()), "nim", quoteShell(getPrefixDir()),
"lib", quoteShell(libpath)]) "lib", quoteShell(libpath)])
@ -568,6 +581,9 @@ proc footprint(filename: string): TCrc32 =
getCompileCFileCmd(filename, true) getCompileCFileCmd(filename, true)
proc externalFileChanged(filename: string): bool = proc externalFileChanged(filename: string): bool =
if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
return false
var crcFile = toGeneratedFile(filename.withPackageName, "crc") var crcFile = toGeneratedFile(filename.withPackageName, "crc")
var currentCrc = int(footprint(filename)) var currentCrc = int(footprint(filename))
var f: File var f: File
@ -588,7 +604,7 @@ proc addExternalFileToCompile*(filename: string) =
if optForceFullMake in gGlobalOptions or externalFileChanged(filename): if optForceFullMake in gGlobalOptions or externalFileChanged(filename):
appendStr(externalToCompile, filename) appendStr(externalToCompile, filename)
proc compileCFile(list: TLinkedList, script: var PRope, cmds: var TStringSeq, proc compileCFile(list: TLinkedList, script: var Rope, cmds: var TStringSeq,
prettyCmds: var TStringSeq, isExternal: bool) = prettyCmds: var TStringSeq, isExternal: bool) =
var it = PStrEntry(list.head) var it = PStrEntry(list.head)
while it != nil: while it != nil:
@ -599,8 +615,8 @@ proc compileCFile(list: TLinkedList, script: var PRope, cmds: var TStringSeq,
let (dir, name, ext) = splitFile(it.data) let (dir, name, ext) = splitFile(it.data)
add(prettyCmds, "CC: " & name) add(prettyCmds, "CC: " & name)
if optGenScript in gGlobalOptions: if optGenScript in gGlobalOptions:
app(script, compileCmd) add(script, compileCmd)
app(script, tnl) add(script, tnl)
it = PStrEntry(it.next) it = PStrEntry(it.next)
proc callCCompiler*(projectfile: string) = proc callCCompiler*(projectfile: string) =
@ -611,7 +627,7 @@ proc callCCompiler*(projectfile: string) =
# generated # generated
fileCounter = 0 fileCounter = 0
var c = cCompiler var c = cCompiler
var script: PRope = nil var script: Rope = nil
var cmds: TStringSeq = @[] var cmds: TStringSeq = @[]
var prettyCmds: TStringSeq = @[] var prettyCmds: TStringSeq = @[]
let prettyCb = proc (idx: int) = let prettyCb = proc (idx: int) =
@ -622,15 +638,17 @@ proc callCCompiler*(projectfile: string) =
if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors() if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors()
var res = 0 var res = 0
if gNumberOfProcessors <= 1: if gNumberOfProcessors <= 1:
for i in countup(0, high(cmds)): res = max(execCmd(cmds[i]), res) for i in countup(0, high(cmds)):
res = execWithEcho(cmds[i])
if res != 0: rawMessage(errExecutionOfProgramFailed, [])
elif optListCmd in gGlobalOptions or gVerbosity > 1: elif optListCmd in gGlobalOptions or gVerbosity > 1:
res = execProcesses(cmds, {poEchoCmd, poUseShell, poParentStreams}, res = execProcesses(cmds, {poEchoCmd, poUsePath, poParentStreams},
gNumberOfProcessors) gNumberOfProcessors)
elif gVerbosity == 1: elif gVerbosity == 1:
res = execProcesses(cmds, {poUseShell, poParentStreams}, res = execProcesses(cmds, {poUsePath, poParentStreams},
gNumberOfProcessors, prettyCb) gNumberOfProcessors, prettyCb)
else: else:
res = execProcesses(cmds, {poUseShell, poParentStreams}, res = execProcesses(cmds, {poUsePath, poParentStreams},
gNumberOfProcessors) gNumberOfProcessors)
if res != 0: if res != 0:
if gNumberOfProcessors <= 1: if gNumberOfProcessors <= 1:
@ -673,15 +691,16 @@ proc callCCompiler*(projectfile: string) =
if not exefile.isAbsolute(): if not exefile.isAbsolute():
exefile = joinPath(splitFile(projectfile).dir, exefile) exefile = joinPath(splitFile(projectfile).dir, exefile)
exefile = quoteShell(exefile) exefile = quoteShell(exefile)
let linkOptions = getLinkOptions() let linkOptions = getLinkOptions() & " " &
getConfigVar(cCompiler, ".options.linker")
linkCmd = quoteShell(linkCmd % ["builddll", builddll, linkCmd = quoteShell(linkCmd % ["builddll", builddll,
"buildgui", buildgui, "options", linkOptions, "objfiles", objfiles, "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
"exefile", exefile, "nimrod", getPrefixDir(), "lib", libpath]) "exefile", exefile, "nim", getPrefixDir(), "lib", libpath])
linkCmd.add ' ' linkCmd.add ' '
addf(linkCmd, CC[c].linkTmpl, ["builddll", builddll, addf(linkCmd, CC[c].linkTmpl, ["builddll", builddll,
"buildgui", buildgui, "options", linkOptions, "buildgui", buildgui, "options", linkOptions,
"objfiles", objfiles, "exefile", exefile, "objfiles", objfiles, "exefile", exefile,
"nimrod", quoteShell(getPrefixDir()), "nim", quoteShell(getPrefixDir()),
"lib", quoteShell(libpath)]) "lib", quoteShell(libpath)])
if optCompileOnly notin gGlobalOptions: if optCompileOnly notin gGlobalOptions:
if gVerbosity == 1: if gVerbosity == 1:
@ -691,30 +710,30 @@ proc callCCompiler*(projectfile: string) =
else: else:
linkCmd = "" linkCmd = ""
if optGenScript in gGlobalOptions: if optGenScript in gGlobalOptions:
app(script, linkCmd) add(script, linkCmd)
app(script, tnl) add(script, tnl)
generateScript(projectfile, script) generateScript(projectfile, script)
proc genMappingFiles(list: TLinkedList): PRope = proc genMappingFiles(list: TLinkedList): Rope =
var it = PStrEntry(list.head) var it = PStrEntry(list.head)
while it != nil: while it != nil:
appf(result, "--file:r\"$1\"$N", [toRope(it.data)]) addf(result, "--file:r\"$1\"$N", [rope(it.data)])
it = PStrEntry(it.next) it = PStrEntry(it.next)
proc writeMapping*(gSymbolMapping: PRope) = proc writeMapping*(gSymbolMapping: Rope) =
if optGenMapping notin gGlobalOptions: return if optGenMapping notin gGlobalOptions: return
var code = toRope("[C_Files]\n") var code = rope("[C_Files]\n")
app(code, genMappingFiles(toCompile)) add(code, genMappingFiles(toCompile))
app(code, genMappingFiles(externalToCompile)) add(code, genMappingFiles(externalToCompile))
app(code, "\n[C_Compiler]\nFlags=") add(code, "\n[C_Compiler]\nFlags=")
app(code, strutils.escape(getCompileOptions())) add(code, strutils.escape(getCompileOptions()))
app(code, "\n[Linker]\nFlags=") add(code, "\n[Linker]\nFlags=")
app(code, strutils.escape(getLinkOptions())) add(code, strutils.escape(getLinkOptions() & " " &
getConfigVar(cCompiler, ".options.linker")))
app(code, "\n[Environment]\nlibpath=") add(code, "\n[Environment]\nlibpath=")
app(code, strutils.escape(libpath)) add(code, strutils.escape(libpath))
appf(code, "\n[Symbols]$n$1", [gSymbolMapping]) addf(code, "\n[Symbols]$n$1", [gSymbolMapping])
writeRope(code, joinPath(gProjectPath, "mapping.txt")) writeRope(code, joinPath(gProjectPath, "mapping.txt"))

View file

@ -37,11 +37,11 @@ const
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'} PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'}
proc newLine(p: var TTmplParser) = proc newLine(p: var TTmplParser) =
llStreamWrite(p.outp, repeatChar(p.emitPar, ')')) llStreamWrite(p.outp, repeat(')', p.emitPar))
p.emitPar = 0 p.emitPar = 0
if p.info.line > int16(1): llStreamWrite(p.outp, "\n") if p.info.line > int16(1): llStreamWrite(p.outp, "\n")
if p.pendingExprLine: if p.pendingExprLine:
llStreamWrite(p.outp, repeatChar(2)) llStreamWrite(p.outp, spaces(2))
p.pendingExprLine = false p.pendingExprLine = false
proc scanPar(p: var TTmplParser, d: int) = proc scanPar(p: var TTmplParser, d: int) =
@ -88,24 +88,24 @@ proc parseLine(p: var TTmplParser) =
else: else:
p.info.col = int16(j) p.info.col = int16(j)
localError(p.info, errXNotAllowedHere, "end") localError(p.info, errXNotAllowedHere, "end")
llStreamWrite(p.outp, repeatChar(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, "#end") llStreamWrite(p.outp, "#end")
of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator, of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator,
wConverter, wMacro, wTemplate, wMethod: wConverter, wMacro, wTemplate, wMethod:
llStreamWrite(p.outp, repeatChar(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, substr(p.x, d)) llStreamWrite(p.outp, substr(p.x, d))
inc(p.indent, 2) inc(p.indent, 2)
of wElif, wOf, wElse, wExcept, wFinally: of wElif, wOf, wElse, wExcept, wFinally:
llStreamWrite(p.outp, repeatChar(p.indent - 2)) llStreamWrite(p.outp, spaces(p.indent - 2))
llStreamWrite(p.outp, substr(p.x, d)) llStreamWrite(p.outp, substr(p.x, d))
of wLet, wVar, wConst, wType: of wLet, wVar, wConst, wType:
llStreamWrite(p.outp, repeatChar(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, substr(p.x, d)) llStreamWrite(p.outp, substr(p.x, d))
if not p.x.contains({':', '='}): if not p.x.contains({':', '='}):
# no inline element --> treat as block: # no inline element --> treat as block:
inc(p.indent, 2) inc(p.indent, 2)
else: else:
llStreamWrite(p.outp, repeatChar(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, substr(p.x, d)) llStreamWrite(p.outp, substr(p.x, d))
p.state = psDirective p.state = psDirective
else: else:
@ -120,11 +120,11 @@ proc parseLine(p: var TTmplParser) =
# next line of string literal: # next line of string literal:
llStreamWrite(p.outp, p.conc) llStreamWrite(p.outp, p.conc)
llStreamWrite(p.outp, "\n") llStreamWrite(p.outp, "\n")
llStreamWrite(p.outp, repeatChar(p.indent + 2)) llStreamWrite(p.outp, spaces(p.indent + 2))
llStreamWrite(p.outp, "\"") llStreamWrite(p.outp, "\"")
of psDirective: of psDirective:
newLine(p) newLine(p)
llStreamWrite(p.outp, repeatChar(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, p.emit) llStreamWrite(p.outp, p.emit)
llStreamWrite(p.outp, "(\"") llStreamWrite(p.outp, "(\"")
inc(p.emitPar) inc(p.emitPar)

89
compiler/forloops.nim Normal file
View file

@ -0,0 +1,89 @@
#
#
# The Nim Compiler
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements for loop detection for better C code generation.
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,
mLtCh, mLtB, mLtPtr}
proc isCounter(s: PSym): bool {.inline.} =
s.kind in {skResult, skVar, skLet, skTemp} and
{sfGlobal, sfAddrTaken} * s.flags == {}
proc isCall(n: PNode): bool {.inline.} =
n.kind in nkCallKinds and n[0].kind == nkSym
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
((op.name.s == "+=" or op.name.s == "-=") and op.fromSystem):
if op[1].kind == nkSym and isCounter(op[1].sym):
result = op[1].sym
proc counterInTree(n, loop: PNode; counter: PSym): bool =
# prune the search tree: within the loop the counter may be used:
if n == loop: return
case n.kind
of nkSym:
if n.sym == counter: return true
of nkVarSection, nkLetSection:
# definitions are fine!
for it in n:
if counterInTree(it.lastSon): return true
else:
for i in 0 .. <safeLen(n):
if counterInTree(n[i], loop, counter): return true
proc copyExcept(n: PNode, x, dest: PNode) =
if x == n: return
if n.kind in {nkStmtList, nkStmtListExpr}:
for i in 0 .. <n.len: copyExcept(n[i], x, dest)
else:
dest.add n
type
ForLoop* = object
counter*: PSym
init*, cond*, increment*, body*: PNode
proc extractForLoop*(loop, fullTree: PNode): ForLoop =
## returns 'counter == nil' if the while loop 'n' is not a for loop:
assert loop.kind == nkWhileStmt
let cond == loop[0]
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
let counter = getCounter(lastStmt)
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
# ok, success, fill in the fields:
result.counter = counter
result.init = counter.ast
result.cond = cond
result.increment = lastStmt
result.body = newNodeI(nkStmtList, loop[1].info)
copyExcept(loop[1], lastStmt, result.body)

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -22,7 +22,8 @@ const
someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum, someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum,
mLtCh, mLtB, mLtPtr, mLtStr} mLtCh, mLtB, mLtPtr, mLtStr}
someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq} someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq,
mXLenStr, mXLenSeq}
someIn = {mInRange, mInSet} someIn = {mInRange, mInSet}
@ -34,8 +35,8 @@ const
someMul = {mMulI, mMulI64, mMulF64} someMul = {mMulI, mMulI64, mMulF64}
someDiv = {mDivI, mDivI64, mDivF64} someDiv = {mDivI, mDivI64, mDivF64}
someMod = {mModI, mModI64} someMod = {mModI, mModI64}
someMax = {mMaxI, mMaxI64, mMaxF64} someMax = {mMaxI, mMaxF64}
someMin = {mMinI, mMinI64, mMinF64} someMin = {mMinI, mMinF64}
proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit} proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit}
proc isLocation(n: PNode): bool = not n.isValue proc isLocation(n: PNode): bool = not n.isValue
@ -81,18 +82,12 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true) proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true)
proc createMagic*(name: string, m: TMagic): PSym =
result = newSym(skProc, getIdent(name), nil, unknownLineInfo())
result.magic = m
let let
opLe = createMagic("<=", mLeI) opLe = createMagic("<=", mLeI)
opLt = createMagic("<", mLtI) opLt = createMagic("<", mLtI)
opAnd = createMagic("and", mAnd) opAnd = createMagic("and", mAnd)
opOr = createMagic("or", mOr) opOr = createMagic("or", mOr)
opNot = createMagic("not", mNot)
opIsNil = createMagic("isnil", mIsNil) opIsNil = createMagic("isnil", mIsNil)
opContains = createMagic("contains", mInSet)
opEq = createMagic("==", mEqI) opEq = createMagic("==", mEqI)
opAdd = createMagic("+", mAddI) opAdd = createMagic("+", mAddI)
opSub = createMagic("-", mSubI) opSub = createMagic("-", mSubI)
@ -128,7 +123,7 @@ proc neg(n: PNode): PNode =
let eAsNode = newIntNode(nkIntLit, e.sym.position) let eAsNode = newIntNode(nkIntLit, e.sym.position)
if not inSet(n.sons[1], eAsNode): s.add eAsNode if not inSet(n.sons[1], eAsNode): s.add eAsNode
result.sons[1] = s result.sons[1] = s
elif lengthOrd(t) < 1000: elif t.kind notin {tyString, tySequence} and lengthOrd(t) < 1000:
result.sons[1] = complement(n.sons[1]) result.sons[1] = complement(n.sons[1])
else: else:
# not ({2, 3, 4}.contains(x)) x != 2 and x != 3 and x != 4 # not ({2, 3, 4}.contains(x)) x != 2 and x != 3 and x != 4
@ -200,8 +195,12 @@ proc lowBound*(x: PNode): PNode =
result.info = x.info result.info = x.info
proc highBound*(x: PNode): PNode = proc highBound*(x: PNode): PNode =
result = if x.typ.skipTypes(abstractInst).kind == tyArray: let typ = x.typ.skipTypes(abstractInst)
nkIntLit.newIntNode(lastOrd(x.typ)) result = if typ.kind in {tyArrayConstr, tyArray}:
nkIntLit.newIntNode(lastOrd(typ))
elif typ.kind == tySequence and x.kind == nkSym and
x.sym.kind == skConst:
nkIntLit.newIntNode(x.sym.ast.len-1)
else: else:
opAdd.buildCall(opLen.buildCall(x), minusOne()) opAdd.buildCall(opLen.buildCall(x), minusOne())
result.info = x.info result.info = x.info
@ -220,12 +219,23 @@ proc reassociation(n: PNode): PNode =
result = opAdd.buildCall(result[1][1], result[1][2] |*| result[2]) result = opAdd.buildCall(result[1][1], result[1][2] |*| result[2])
else: discard else: discard
proc pred(n: PNode): PNode =
if n.kind in {nkCharLit..nkUInt64Lit} and n.intVal != low(BiggestInt):
result = copyNode(n)
dec result.intVal
else:
result = n
proc canon*(n: PNode): PNode = proc canon*(n: PNode): PNode =
# XXX for now only the new code in 'semparallel' uses this # XXX for now only the new code in 'semparallel' uses this
if n.safeLen >= 1: if n.safeLen >= 1:
result = shallowCopy(n) result = shallowCopy(n)
for i in 0 .. < n.len: for i in 0 .. < n.len:
result.sons[i] = canon(n.sons[i]) 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):
result = n.sym.ast.copyTree
else: else:
result = n result = n
case result.getMagic case result.getMagic
@ -237,20 +247,26 @@ proc canon*(n: PNode): PNode =
of someHigh: of someHigh:
# high == len+(-1) # high == len+(-1)
result = opAdd.buildCall(opLen.buildCall(result[1]), minusOne()) result = opAdd.buildCall(opLen.buildCall(result[1]), minusOne())
of mUnaryMinusI, mUnaryMinusI64: of mUnaryLt:
result = buildCall(opAdd, result[1], newIntNode(nkIntLit, -1)) result = buildCall(opAdd, result[1], newIntNode(nkIntLit, -1))
of someSub: of someSub:
# x - 4 --> x + (-4) # x - 4 --> x + (-4)
result = negate(result[1], result[2], result) result = negate(result[1], result[2], result)
of someLen: of someLen:
result.sons[0] = opLen.newSymNode result.sons[0] = opLen.newSymNode
of someLt:
# x < y same as x <= y-1:
let y = n[2].canon
let p = pred(y)
let minus = if p != y: p else: opAdd.buildCall(y, minusOne()).canon
result = opLe.buildCall(n[1].canon, minus)
else: discard else: discard
result = skipConv(result) result = skipConv(result)
result = reassociation(result) result = reassociation(result)
# most important rule: (x-4) < a.len --> x < a.len+4 # most important rule: (x-4) <= a.len --> x <= a.len+4
case result.getMagic case result.getMagic
of someLe, someLt: of someLe:
let x = result[1] let x = result[1]
let y = result[2] let y = result[2]
if x.kind in nkCallKinds and x.len == 3 and x[2].isValue and if x.kind in nkCallKinds and x.len == 3 and x[2].isValue and
@ -358,13 +374,28 @@ proc addFactNeg*(m: var TModel, n: PNode) =
let n = n.neg let n = n.neg
if n != nil: addFact(m, n) if n != nil: addFact(m, n)
proc canonOpr(opr: PSym): PSym =
case opr.magic
of someEq: result = opEq
of someLe: result = opLe
of someLt: result = opLt
of someLen: result = opLen
of someAdd: result = opAdd
of someSub: result = opSub
of someMul: result = opMul
of someDiv: result = opDiv
else: result = opr
proc sameTree*(a, b: PNode): bool = proc sameTree*(a, b: PNode): bool =
result = false result = false
if a == b: if a == b:
result = true result = true
elif (a != nil) and (b != nil) and (a.kind == b.kind): elif a != nil and b != nil and a.kind == b.kind:
case a.kind case a.kind
of nkSym: result = a.sym == b.sym of nkSym:
result = a.sym == b.sym
if not result and a.sym.magic != mNone:
result = a.sym.magic == b.sym.magic or canonOpr(a.sym) == canonOpr(b.sym)
of nkIdent: result = a.ident.id == b.ident.id of nkIdent: result = a.ident.id == b.ident.id
of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
@ -408,13 +439,6 @@ proc valuesUnequal(a, b: PNode): bool =
if a.isValue and b.isValue: if a.isValue and b.isValue:
result = not sameValue(a, b) result = not sameValue(a, b)
proc pred(n: PNode): PNode =
if n.kind in {nkCharLit..nkUInt64Lit} and n.intVal != low(BiggestInt):
result = copyNode(n)
dec result.intVal
else:
result = n
proc impliesEq(fact, eq: PNode): TImplication = proc impliesEq(fact, eq: PNode): TImplication =
let (loc, val) = if isLocation(eq.sons[1]): (1, 2) else: (2, 1) let (loc, val) = if isLocation(eq.sons[1]): (1, 2) else: (2, 1)
@ -635,7 +659,7 @@ proc factImplies(fact, prop: PNode): TImplication =
if a == b: return ~a if a == b: return ~a
return impUnknown return impUnknown
else: else:
internalError(fact.info, "invalid fact") return impUnknown
of mAnd: of mAnd:
result = factImplies(fact.sons[1], prop) result = factImplies(fact.sons[1], prop)
if result != impUnknown: return result if result != impUnknown: return result
@ -649,7 +673,7 @@ proc factImplies(fact, prop: PNode): TImplication =
of someLe: result = impliesLe(fact, prop.sons[1], prop.sons[2]) of someLe: result = impliesLe(fact, prop.sons[1], prop.sons[2])
of someLt: result = impliesLt(fact, prop.sons[1], prop.sons[2]) of someLt: result = impliesLt(fact, prop.sons[1], prop.sons[2])
of mInSet: result = impliesIn(fact, prop.sons[2], prop.sons[1]) of mInSet: result = impliesIn(fact, prop.sons[2], prop.sons[1])
else: internalError(prop.info, "invalid proposition") else: result = impUnknown
proc doesImply*(facts: TModel, prop: PNode): TImplication = proc doesImply*(facts: TModel, prop: PNode): TImplication =
assert prop.kind in nkCallKinds assert prop.kind in nkCallKinds
@ -677,6 +701,7 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication
proc ple(m: TModel; a, b: 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,a,b) == impYes
# 0 <= 3 # 0 <= 3
if a.isValue and b.isValue: if a.isValue and b.isValue:
return if leValue(a, b): impYes else: impNo return if leValue(a, b): impYes else: impNo
@ -750,16 +775,21 @@ proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
# mark as used: # mark as used:
m[i] = nil m[i] = nil
if ple(m, a, x) == impYes: if ple(m, a, x) == impYes:
if ple(m, y, b) == impYes: return impYes if ple(m, y, b) == impYes:
return impYes
#if pleViaModelRec(m, y, b): return impYes #if pleViaModelRec(m, y, b): return impYes
# fact: 16 <= i # fact: 16 <= i
# x y # x y
# question: i <= 15? no! # question: i <= 15? no!
result = impliesLe(fact, a, b) result = impliesLe(fact, a, b)
if result != impUnknown: return result if result != impUnknown:
if sameTree(y, a): return result
result = ple(m, b, x) when false:
if result != impUnknown: return result # given: x <= y; y==a; x <= a this means: a <= b if x <= b
if sameTree(y, a):
result = ple(m, b, x)
if result != impUnknown:
return result
proc pleViaModel(model: TModel; aa, bb: PNode): TImplication = proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
# compute replacements: # compute replacements:
@ -878,5 +908,5 @@ proc buildProperFieldCheck(access, check: PNode): PNode =
proc checkFieldAccess*(m: TModel, n: PNode) = proc checkFieldAccess*(m: TModel, n: PNode) =
for i in 1..n.len-1: for i in 1..n.len-1:
let check = buildProperFieldCheck(n.sons[0], n.sons[i]) let check = buildProperFieldCheck(n.sons[0], n.sons[i])
if m.doesImply(check) != impYes: if check != nil and m.doesImply(check) != impYes:
message(n.info, warnProveField, renderTree(n.sons[0])); break message(n.info, warnProveField, renderTree(n.sons[0])); break

View file

@ -25,7 +25,7 @@ type
next*: PIdent # for hash-table chaining next*: PIdent # for hash-table chaining
h*: THash # hash value of s h*: THash # hash value of s
var firstCharIsCS*: bool var firstCharIsCS*: bool = true
var buckets*: array[0..4096 * 2 - 1, PIdent] var buckets*: array[0..4096 * 2 - 1, PIdent]
proc cmpIgnoreStyle(a, b: cstring, blen: int): int = proc cmpIgnoreStyle(a, b: cstring, blen: int): int =

View file

@ -27,7 +27,7 @@ proc getModuleName*(n: PNode): string =
result = n.ident.s result = n.ident.s
of nkSym: of nkSym:
result = n.sym.name.s result = n.sym.name.s
of nkInfix: of nkInfix, nkPrefix:
if n.sons[0].kind == nkIdent and n.sons[0].ident.id == getIdent("as").id: if n.sons[0].kind == nkIdent and n.sons[0].ident.id == getIdent("as").id:
# XXX hack ahead: # XXX hack ahead:
n.kind = nkImportAs n.kind = nkImportAs
@ -143,7 +143,7 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet) =
of nkExportExceptStmt: of nkExportExceptStmt:
localError(n.info, errGenerated, "'export except' not implemented") localError(n.info, errGenerated, "'export except' not implemented")
else: else:
for i in 0 ..safeLen(n)-1: for i in 0..safeLen(n)-1:
importForwarded(c, n.sons[i], exceptSet) importForwarded(c, n.sons[i], exceptSet)
proc importModuleAs(n: PNode, realModule: PSym): PSym = proc importModuleAs(n: PNode, realModule: PSym): PSym =

View file

@ -51,23 +51,35 @@ Files: "configure;makefile"
Files: "*.ini" Files: "*.ini"
Files: "koch.nim" Files: "koch.nim"
Files: "icons/nimrod.ico" Files: "icons/nim.ico"
Files: "icons/nimrod.rc" Files: "icons/nim.rc"
Files: "icons/nimrod.res" Files: "icons/nim.res"
Files: "icons/nimrod_icon.o" Files: "icons/nim_icon.o"
Files: "icons/koch.ico" Files: "icons/koch.ico"
Files: "icons/koch.rc" Files: "icons/koch.rc"
Files: "icons/koch.res" Files: "icons/koch.res"
Files: "icons/koch_icon.o" Files: "icons/koch_icon.o"
Files: "compiler/readme.txt" Files: "compiler/readme.txt"
Files: "compiler/nim.ini" Files: "compiler/installer.ini"
Files: "compiler/nim.nimrod.cfg" Files: "compiler/nim.nim.cfg"
Files: "compiler/*.nim" Files: "compiler/*.nim"
Files: "doc/*.txt" Files: "doc/*.txt"
Files: "doc/manual/*.txt"
Files: "doc/*.nim"
Files: "doc/*.cfg"
Files: "compiler/nimfix/*.nim" Files: "compiler/nimfix/*.nim"
Files: "compiler/nimfix/*.cfg" Files: "compiler/nimfix/*.cfg"
Files: "tools/*.nim"
Files: "tools/*.cfg"
Files: "tools/*.tmpl"
Files: "tools/niminst/*.nim"
Files: "tools/niminst/*.cfg"
Files: "tools/niminst/*.tmpl"
Files: "tools/niminst/*.nsh"
Files: "web/website.ini"
Files: "web/*.nim"
Files: "web/*.txt"
[Lib] [Lib]
Files: "lib/nimbase.h" Files: "lib/nimbase.h"
@ -77,8 +89,11 @@ Files: "lib/*.cfg"
Files: "lib/system/*.nim" Files: "lib/system/*.nim"
Files: "lib/core/*.nim" Files: "lib/core/*.nim"
Files: "lib/pure/*.nim" Files: "lib/pure/*.nim"
Files: "lib/pure/*.cfg"
Files: "lib/pure/collections/*.nim" Files: "lib/pure/collections/*.nim"
Files: "lib/pure/concurrency/*.nim" Files: "lib/pure/concurrency/*.nim"
Files: "lib/pure/unidecode/*.nim"
Files: "lib/pure/concurrency/*.cfg"
Files: "lib/impure/*.nim" Files: "lib/impure/*.nim"
Files: "lib/wrappers/*.nim" Files: "lib/wrappers/*.nim"
@ -111,12 +126,109 @@ Files: "examples/*.txt"
Files: "examples/*.cfg" Files: "examples/*.cfg"
Files: "examples/*.tmpl" Files: "examples/*.tmpl"
Files: "tests/actiontable/*.nim"
Files: "tests/alias/*.nim"
Files: "tests/ambsym/*.nim"
Files: "tests/array/*.nim"
Files: "tests/assign/*.nim"
Files: "tests/astoverload/*.nim"
Files: "tests/async/*.nim"
Files: "tests/benchmarks/*.nim"
Files: "tests/bind/*.nim"
Files: "tests/borrow/*.nim"
Files: "tests/casestmt/*.nim"
Files: "tests/ccgbugs/*.nim"
Files: "tests/clearmsg/*.nim"
Files: "tests/closure/*.nim"
Files: "tests/cnstseq/*.nim"
Files: "tests/collections/*.nim"
Files: "tests/compiles/*.nim"
Files: "tests/concat/*.nim"
Files: "tests/concepts/*.nim"
Files: "tests/constr/*.nim"
Files: "tests/constraints/*.nim"
Files: "tests/controlflow/*.nim"
Files: "tests/converter/*.nim"
Files: "tests/cpp/*.nim"
Files: "tests/defaultprocparam/*.nim"
Files: "tests/deprecated/*.nim"
Files: "tests/destructor/*.nim"
Files: "tests/dir with space/*.nim"
Files: "tests/discard/*.nim"
Files: "tests/distinct/*.nim"
Files: "tests/dll/*.nim"
Files: "tests/effects/*.nim"
Files: "tests/enum/*.nim"
Files: "tests/exception/*.nim"
Files: "tests/exprs/*.nim"
Files: "tests/fields/*.nim"
Files: "tests/float/*.nim"
Files: "tests/friends/*.nim"
Files: "tests/gc/*.nim"
Files: "tests/generics/*.nim"
Files: "tests/gensym/*.nim"
Files: "tests/global/*.nim"
Files: "tests/implicit/*.nim"
Files: "tests/init/*.nim"
Files: "tests/iter/*.nim"
Files: "tests/js/*.nim"
Files: "tests/js/*.cfg"
Files: "tests/let/*.nim"
Files: "tests/lexer/*.nim"
Files: "tests/lookups/*.nim"
Files: "tests/macros/*.nim"
Files: "tests/magics/*.nim"
Files: "tests/metatype/*.nim"
Files: "tests/method/*.nim"
Files: "tests/misc/*.nim"
Files: "tests/modules/*.nim"
Files: "tests/namedparams/*.nim"
Files: "tests/notnil/*.nim"
Files: "tests/objects/*.nim"
Files: "tests/objvariant/*.nim"
Files: "tests/openarray/*.nim"
Files: "tests/osproc/*.nim"
Files: "tests/overflw/*.nim"
Files: "tests/overload/*.nim"
Files: "tests/parallel/*.nim"
Files: "tests/parallel/*.cfg"
Files: "tests/parser/*.nim"
Files: "tests/pragmas/*.nim"
Files: "tests/proc/*.nim"
Files: "tests/procvar/*.nim"
Files: "tests/range/*.nim"
Files: "tests/rodfiles/*.nim"
Files: "tests/seq/*.nim"
Files: "tests/sets/*.nim"
Files: "tests/showoff/*.nim"
Files: "tests/specialops/*.nim"
Files: "tests/stdlib/*.nim"
Files: "tests/system/*.nim"
Files: "tests/template/*.nim"
Files: "tests/testament/*.nim"
Files: "tests/testdata/*.nim"
Files: "tests/threads/*.nim"
Files: "tests/threads/*.cfg"
Files: "tests/trmacros/*.nim"
Files: "tests/tuples/*.nim"
Files: "tests/typerel/*.nim"
Files: "tests/types/*.nim"
Files: "tests/usingstmt/*.nim"
Files: "tests/varres/*.nim"
Files: "tests/varstmt/*.nim"
Files: "tests/vm/*.nim"
Files: "tests/readme.txt"
Files: "tests/testament/css/*.css"
Files: "tests/testament/*.cfg"
Files: "lib/pure/unidecode/unidecode.dat"
[Windows] [Windows]
Files: "bin/nim.exe" Files: "bin/nim.exe"
Files: "bin/nim_debug.exe"
Files: "bin/c2nim.exe" Files: "bin/c2nim.exe"
Files: "bin/nimgrep.exe" Files: "bin/nimgrep.exe"
Files: "bin/nimsuggest.exe"
Files: "bin/nimble.exe"
Files: "bin/*.dll"
Files: "dist/*.dll" Files: "dist/*.dll"
Files: "koch.exe" Files: "koch.exe"
@ -125,9 +237,9 @@ Files: "start.bat"
BinPath: r"bin;dist\mingw\bin;dist" BinPath: r"bin;dist\mingw\bin;dist"
; Section | dir | zipFile | size hint (in KB) | url | exe start menu entry ; 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|doc\overview.html" 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"C Compiler (MingW)|dist|mingw.zip|82944|http://nim-lang.org/download/${mingw}.zip"
Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.1.3.zip|aporia\bin\aporia.exe" Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.3.0.zip|aporia\bin\aporia.exe"
; for now only NSIS supports optional downloads ; for now only NSIS supports optional downloads
[UnixBin] [UnixBin]

File diff suppressed because it is too large Load diff

View file

@ -9,10 +9,10 @@
## Type info generation for the JS backend. ## Type info generation for the JS backend.
proc genTypeInfo(p: PProc, typ: PType): PRope proc genTypeInfo(p: PProc, typ: PType): Rope
proc genObjectFields(p: PProc, typ: PType, n: PNode): PRope = proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
var var
s, u: PRope s, u: Rope
length: int length: int
field: PSym field: PSym
b: PNode b: PNode
@ -25,16 +25,16 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): PRope =
else: else:
s = nil s = nil
for i in countup(0, length - 1): for i in countup(0, length - 1):
if i > 0: app(s, ", " & tnl) if i > 0: add(s, ", " & tnl)
app(s, genObjectFields(p, typ, n.sons[i])) add(s, genObjectFields(p, typ, n.sons[i]))
result = ropef("{kind: 2, len: $1, offset: 0, " & result = ("{kind: 2, len: $1, offset: 0, " &
"typ: null, name: null, sons: [$2]}", [toRope(length), s]) "typ: null, name: null, sons: [$2]}") % [rope(length), s]
of nkSym: of nkSym:
field = n.sym field = n.sym
s = genTypeInfo(p, field.typ) s = genTypeInfo(p, field.typ)
result = ropef("{kind: 1, offset: \"$1\", len: 0, " & result = ("{kind: 1, offset: \"$1\", len: 0, " &
"typ: $2, name: $3, sons: null}", "typ: $2, name: $3, sons: null}") %
[mangleName(field), s, makeJSString(field.name.s)]) [mangleName(field), s, makeJSString(field.name.s)]
of nkRecCase: of nkRecCase:
length = sonsLen(n) length = sonsLen(n)
if (n.sons[0].kind != nkSym): internalError(n.info, "genObjectFields") if (n.sons[0].kind != nkSym): internalError(n.info, "genObjectFields")
@ -48,99 +48,99 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): PRope =
if sonsLen(b) < 2: if sonsLen(b) < 2:
internalError(b.info, "genObjectFields; nkOfBranch broken") internalError(b.info, "genObjectFields; nkOfBranch broken")
for j in countup(0, sonsLen(b) - 2): for j in countup(0, sonsLen(b) - 2):
if u != nil: app(u, ", ") if u != nil: add(u, ", ")
if b.sons[j].kind == nkRange: if b.sons[j].kind == nkRange:
appf(u, "[$1, $2]", [toRope(getOrdValue(b.sons[j].sons[0])), addf(u, "[$1, $2]", [rope(getOrdValue(b.sons[j].sons[0])),
toRope(getOrdValue(b.sons[j].sons[1]))]) rope(getOrdValue(b.sons[j].sons[1]))])
else: else:
app(u, toRope(getOrdValue(b.sons[j]))) add(u, rope(getOrdValue(b.sons[j])))
of nkElse: of nkElse:
u = toRope(lengthOrd(field.typ)) u = rope(lengthOrd(field.typ))
else: internalError(n.info, "genObjectFields(nkRecCase)") else: internalError(n.info, "genObjectFields(nkRecCase)")
if result != nil: app(result, ", " & tnl) if result != nil: add(result, ", " & tnl)
appf(result, "[SetConstr($1), $2]", addf(result, "[SetConstr($1), $2]",
[u, genObjectFields(p, typ, lastSon(b))]) [u, genObjectFields(p, typ, lastSon(b))])
result = ropef("{kind: 3, offset: \"$1\", len: $3, " & result = ("{kind: 3, offset: \"$1\", len: $3, " &
"typ: $2, name: $4, sons: [$5]}", [mangleName(field), s, "typ: $2, name: $4, sons: [$5]}") % [mangleName(field), s,
toRope(lengthOrd(field.typ)), makeJSString(field.name.s), result]) rope(lengthOrd(field.typ)), makeJSString(field.name.s), result]
else: internalError(n.info, "genObjectFields") else: internalError(n.info, "genObjectFields")
proc genObjectInfo(p: PProc, typ: PType, name: PRope) = proc genObjectInfo(p: PProc, typ: PType, name: Rope) =
var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " & var s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " &
"finalizer: null};$n", [name, toRope(ord(typ.kind))]) "finalizer: null};$n") % [name, rope(ord(typ.kind))]
prepend(p.g.typeInfo, s) prepend(p.g.typeInfo, s)
appf(p.g.typeInfo, "var NNI$1 = $2;$n", addf(p.g.typeInfo, "var NNI$1 = $2;$n",
[toRope(typ.id), genObjectFields(p, typ, typ.n)]) [rope(typ.id), genObjectFields(p, typ, typ.n)])
appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)]) addf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, rope(typ.id)])
if (typ.kind == tyObject) and (typ.sons[0] != nil): if (typ.kind == tyObject) and (typ.sons[0] != nil):
appf(p.g.typeInfo, "$1.base = $2;$n", addf(p.g.typeInfo, "$1.base = $2;$n",
[name, genTypeInfo(p, typ.sons[0])]) [name, genTypeInfo(p, typ.sons[0])])
proc genTupleFields(p: PProc, typ: PType): PRope = proc genTupleFields(p: PProc, typ: PType): Rope =
var s: PRope = nil var s: Rope = nil
for i in 0 .. <typ.len: for i in 0 .. <typ.len:
if i > 0: app(s, ", " & tnl) if i > 0: add(s, ", " & tnl)
s.appf("{kind: 1, offset: \"Field$1\", len: 0, " & s.addf("{kind: 1, offset: \"Field$1\", len: 0, " &
"typ: $2, name: \"Field$1\", sons: null}", "typ: $2, name: \"Field$1\", sons: null}",
[i.toRope, genTypeInfo(p, typ.sons[i])]) [i.rope, genTypeInfo(p, typ.sons[i])])
result = ropef("{kind: 2, len: $1, offset: 0, " & result = ("{kind: 2, len: $1, offset: 0, " &
"typ: null, name: null, sons: [$2]}", [toRope(typ.len), s]) "typ: null, name: null, sons: [$2]}") % [rope(typ.len), s]
proc genTupleInfo(p: PProc, typ: PType, name: PRope) = proc genTupleInfo(p: PProc, typ: PType, name: Rope) =
var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " & var s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " &
"finalizer: null};$n", [name, toRope(ord(typ.kind))]) "finalizer: null};$n") % [name, rope(ord(typ.kind))]
prepend(p.g.typeInfo, s) prepend(p.g.typeInfo, s)
appf(p.g.typeInfo, "var NNI$1 = $2;$n", addf(p.g.typeInfo, "var NNI$1 = $2;$n",
[toRope(typ.id), genTupleFields(p, typ)]) [rope(typ.id), genTupleFields(p, typ)])
appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)]) addf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, rope(typ.id)])
proc genEnumInfo(p: PProc, typ: PType, name: PRope) = proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
let length = sonsLen(typ.n) let length = sonsLen(typ.n)
var s: PRope = nil var s: Rope = nil
for i in countup(0, length - 1): for i in countup(0, length - 1):
if (typ.n.sons[i].kind != nkSym): internalError(typ.n.info, "genEnumInfo") if (typ.n.sons[i].kind != nkSym): internalError(typ.n.info, "genEnumInfo")
let field = typ.n.sons[i].sym let field = typ.n.sons[i].sym
if i > 0: app(s, ", " & tnl) if i > 0: add(s, ", " & tnl)
let extName = if field.ast == nil: field.name.s else: field.ast.strVal let extName = if field.ast == nil: field.name.s else: field.ast.strVal
appf(s, "{kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}", addf(s, "{kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}",
[toRope(field.position), name, makeJSString(extName)]) [rope(field.position), name, makeJSString(extName)])
var n = ropef("var NNI$1 = {kind: 2, offset: 0, typ: null, " & var n = ("var NNI$1 = {kind: 2, offset: 0, typ: null, " &
"name: null, len: $2, sons: [$3]};$n", [toRope(typ.id), toRope(length), s]) "name: null, len: $2, sons: [$3]};$n") % [rope(typ.id), rope(length), s]
s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " & s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " &
"finalizer: null};$n", [name, toRope(ord(typ.kind))]) "finalizer: null};$n") % [name, rope(ord(typ.kind))]
prepend(p.g.typeInfo, s) prepend(p.g.typeInfo, s)
app(p.g.typeInfo, n) add(p.g.typeInfo, n)
appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)]) addf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, rope(typ.id)])
if typ.sons[0] != nil: if typ.sons[0] != nil:
appf(p.g.typeInfo, "$1.base = $2;$n", addf(p.g.typeInfo, "$1.base = $2;$n",
[name, genTypeInfo(p, typ.sons[0])]) [name, genTypeInfo(p, typ.sons[0])])
proc genTypeInfo(p: PProc, typ: PType): PRope = proc genTypeInfo(p: PProc, typ: PType): Rope =
var t = typ var t = typ
if t.kind == tyGenericInst: t = lastSon(t) if t.kind == tyGenericInst: t = lastSon(t)
result = ropef("NTI$1", [toRope(t.id)]) result = "NTI$1" % [rope(t.id)]
if containsOrIncl(p.g.typeInfoGenerated, t.id): return if containsOrIncl(p.g.typeInfoGenerated, t.id): return
case t.kind case t.kind
of tyDistinct: of tyDistinct:
result = genTypeInfo(p, typ.sons[0]) result = genTypeInfo(p, typ.sons[0])
of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyFloat128: of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64:
var s = ropef( var s =
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
[result, toRope(ord(t.kind))]) [result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s) prepend(p.g.typeInfo, s)
of tyVar, tyRef, tyPtr, tySequence, tyRange, tySet: of tyVar, tyRef, tyPtr, tySequence, tyRange, tySet:
var s = ropef( var s =
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
[result, toRope(ord(t.kind))]) [result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s) prepend(p.g.typeInfo, s)
appf(p.g.typeInfo, "$1.base = $2;$n", addf(p.g.typeInfo, "$1.base = $2;$n",
[result, genTypeInfo(p, typ.lastSon)]) [result, genTypeInfo(p, typ.lastSon)])
of tyArrayConstr, tyArray: of tyArrayConstr, tyArray:
var s = ropef( var s =
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
[result, toRope(ord(t.kind))]) [result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s) prepend(p.g.typeInfo, s)
appf(p.g.typeInfo, "$1.base = $2;$n", addf(p.g.typeInfo, "$1.base = $2;$n",
[result, genTypeInfo(p, typ.sons[1])]) [result, genTypeInfo(p, typ.sons[1])])
of tyEnum: genEnumInfo(p, t, result) of tyEnum: genEnumInfo(p, t, result)
of tyObject: genObjectInfo(p, t, result) of tyObject: genObjectInfo(p, t, result)

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -15,7 +15,7 @@ import
discard """ discard """
The basic approach is that captured vars need to be put on the heap and The basic approach is that captured vars need to be put on the heap and
that the calling chain needs to be explicitely modelled. Things to consider: that the calling chain needs to be explicitly modelled. Things to consider:
proc a = proc a =
var v = 0 var v = 0
@ -187,6 +187,7 @@ proc addHiddenParam(routine: PSym, param: PSym) =
param.position = params.len-1 param.position = params.len-1
addSon(params, newSymNode(param)) addSon(params, newSymNode(param))
incl(routine.typ.flags, tfCapturesEnv) incl(routine.typ.flags, tfCapturesEnv)
assert sfFromGeneric in param.flags
#echo "produced environment: ", param.id, " for ", routine.name.s #echo "produced environment: ", param.id, " for ", routine.name.s
proc getHiddenParam(routine: PSym): PSym = proc getHiddenParam(routine: PSym): PSym =
@ -194,12 +195,14 @@ proc getHiddenParam(routine: PSym): PSym =
let hidden = lastSon(params) let hidden = lastSon(params)
internalAssert hidden.kind == nkSym and hidden.sym.kind == skParam internalAssert hidden.kind == nkSym and hidden.sym.kind == skParam
result = hidden.sym result = hidden.sym
assert sfFromGeneric in result.flags
proc getEnvParam(routine: PSym): PSym = proc getEnvParam(routine: PSym): PSym =
let params = routine.ast.sons[paramsPos] let params = routine.ast.sons[paramsPos]
let hidden = lastSon(params) let hidden = lastSon(params)
if hidden.kind == nkSym and hidden.sym.name.s == paramName: if hidden.kind == nkSym and hidden.sym.name.s == paramName:
result = hidden.sym result = hidden.sym
assert sfFromGeneric in result.flags
proc initIter(iter: PSym): TIter = proc initIter(iter: PSym): TIter =
result.fn = iter result.fn = iter
@ -580,7 +583,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode, env: PEnv) =
elif it.kind == nkIdentDefs: elif it.kind == nkIdentDefs:
var L = sonsLen(it) var L = sonsLen(it)
if it.sons[0].kind == nkSym: if it.sons[0].kind == nkSym:
# this can be false for recursive invokations that already # this can be false for recursive invocations that already
# transformed it into 'env.varName': # transformed it into 'env.varName':
env.vars.incl(it.sons[0].sym.id) env.vars.incl(it.sons[0].sym.id)
searchForInnerProcs(o, it.sons[L-1], env) searchForInnerProcs(o, it.sons[L-1], env)
@ -716,15 +719,17 @@ proc outerProcSons(o: POuterContext, n: PNode, it: TIter) =
let x = transformOuterProc(o, n.sons[i], it) let x = transformOuterProc(o, n.sons[i], it)
if x != nil: n.sons[i] = x if x != nil: n.sons[i] = x
proc liftIterSym*(n: PNode): PNode = proc liftIterSym(n: PNode; owner: PSym): PNode =
# transforms (iter) to (let env = newClosure[iter](); (iter, env)) # transforms (iter) to (let env = newClosure[iter](); (iter, env))
let iter = n.sym let iter = n.sym
assert iter.kind == skClosureIterator assert iter.kind == skClosureIterator
result = newNodeIT(nkStmtListExpr, n.info, n.typ) result = newNodeIT(nkStmtListExpr, n.info, n.typ)
var env = copySym(getHiddenParam(iter)) let hp = getHiddenParam(iter)
env.kind = skLet let env = newSym(skLet, iter.name, owner, n.info)
env.typ = hp.typ
env.flags = hp.flags
var v = newNodeI(nkVarSection, n.info) var v = newNodeI(nkVarSection, n.info)
addVar(v, newSymNode(env)) addVar(v, newSymNode(env))
result.add(v) result.add(v)
@ -853,7 +858,6 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
addUniqueField(it.obj, local) addUniqueField(it.obj, local)
return indirectAccess(newSymNode(it.closureParam), local, n.info) return indirectAccess(newSymNode(it.closureParam), local, n.info)
var closure = PEnv(idTableGet(o.lambdasToEnv, local))
if local.kind == skClosureIterator: if local.kind == skClosureIterator:
# consider: [i1, i2, i1] Since we merged the iterator's closure # consider: [i1, i2, i1] Since we merged the iterator's closure
# with the captured owning variables, we need to generate the # with the captured owning variables, we need to generate the
@ -861,13 +865,25 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
if local == o.fn or local == it.fn: if local == o.fn or local == it.fn:
message(n.info, errRecursiveDependencyX, local.name.s) message(n.info, errRecursiveDependencyX, local.name.s)
# XXX why doesn't this work? # XXX why doesn't this work?
var closure = PEnv(idTableGet(o.lambdasToEnv, local))
if closure.isNil: if closure.isNil:
return liftIterSym(n) return liftIterSym(n, o.fn)
else: else:
let createdVar = generateIterClosureCreation(o, closure, let createdVar = generateIterClosureCreation(o, closure,
closure.attachedNode) closure.attachedNode)
let lpt = getHiddenParam(local).typ
if lpt != createdVar.typ:
assert lpt.kind == tyRef and createdVar.typ.kind == tyRef
# fix bug 'tshallowcopy_closures' but report if this gets any weirder:
if createdVar.typ.sons[0].len == 1 and lpt.sons[0].len >= 1:
createdVar.typ = lpt
if createdVar.kind == nkSym: createdVar.sym.typ = lpt
closure.obj = lpt.sons[0]
else:
internalError(n.info, "environment computation failed")
return makeClosure(local, createdVar, n.info) return makeClosure(local, createdVar, n.info)
var closure = PEnv(idTableGet(o.lambdasToEnv, local))
if closure != nil: if closure != nil:
# we need to replace the lambda with '(lambda, env)': # we need to replace the lambda with '(lambda, env)':
let a = closure.createdVar let a = closure.createdVar
@ -935,7 +951,7 @@ proc liftLambdas*(fn: PSym, body: PNode): PNode =
# ignore forward declaration: # ignore forward declaration:
result = body result = body
else: else:
#if fn.name.s == "cbOuter": #if fn.name.s == "sort":
# echo rendertree(fn.ast, {renderIds}) # echo rendertree(fn.ast, {renderIds})
var o = newOuterContext(fn) var o = newOuterContext(fn)
let ex = closureCreationPoint(body) let ex = closureCreationPoint(body)
@ -983,7 +999,7 @@ proc liftForLoop*(body: PNode): PNode =
# proc invoke(iter: iterator(): int) = # proc invoke(iter: iterator(): int) =
# for x in iter(): echo x # for x in iter(): echo x
# #
# --> When to create the closure? --> for the (count) occurence! # --> When to create the closure? --> for the (count) occurrence!
discard """ discard """
for i in foo(): ... for i in foo(): ...

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -35,7 +35,7 @@ type
tkSymbol, # keywords: tkSymbol, # keywords:
tkAddr, tkAnd, tkAs, tkAsm, tkAtomic, tkAddr, tkAnd, tkAs, tkAsm, tkAtomic,
tkBind, tkBlock, tkBreak, tkCase, tkCast, tkBind, tkBlock, tkBreak, tkCase, tkCast,
tkConst, tkContinue, tkConverter, tkConcept, tkConst, tkContinue, tkConverter,
tkDefer, tkDiscard, tkDistinct, tkDiv, tkDo, tkDefer, tkDiscard, tkDistinct, tkDiv, tkDo,
tkElif, tkElse, tkEnd, tkEnum, tkExcept, tkExport, tkElif, tkElse, tkEnd, tkEnum, tkExcept, tkExport,
tkFinally, tkFor, tkFrom, tkFunc, tkFinally, tkFor, tkFrom, tkFunc,
@ -61,7 +61,7 @@ type
tkComma, tkSemiColon, tkComma, tkSemiColon,
tkColon, tkColonColon, tkEquals, tkDot, tkDotDot, tkColon, tkColonColon, tkEquals, tkDot, tkDotDot,
tkOpr, tkComment, tkAccent, tkOpr, tkComment, tkAccent,
tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr, tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr
TTokTypes* = set[TTokType] TTokTypes* = set[TTokType]
@ -72,7 +72,7 @@ const
"tkSymbol", "tkSymbol",
"addr", "and", "as", "asm", "atomic", "addr", "and", "as", "asm", "atomic",
"bind", "block", "break", "case", "cast", "bind", "block", "break", "case", "cast",
"const", "continue", "converter", "concept", "const", "continue", "converter",
"defer", "discard", "distinct", "div", "do", "defer", "discard", "distinct", "div", "do",
"elif", "else", "end", "enum", "except", "export", "elif", "else", "end", "enum", "except", "export",
"finally", "for", "from", "func", "generic", "if", "finally", "for", "from", "func", "generic", "if",
@ -107,7 +107,7 @@ type
TToken* = object # a Nim token TToken* = object # a Nim token
tokType*: TTokType # the type of the token tokType*: TTokType # the type of the token
indent*: int # the indentation; != -1 if the token has been indent*: int # the indentation; != -1 if the token has been
# preceeded with indentation # preceded with indentation
ident*: PIdent # the parsed identifier ident*: PIdent # the parsed identifier
iNumber*: BiggestInt # the parsed integer literal iNumber*: BiggestInt # the parsed integer literal
fNumber*: BiggestFloat # the parsed floating point literal fNumber*: BiggestFloat # the parsed floating point literal
@ -131,24 +131,10 @@ type
var gLinesCompiled*: int # all lines that have been compiled var gLinesCompiled*: int # all lines that have been compiled
proc isKeyword*(kind: TTokType): bool
proc openLexer*(lex: var TLexer, fileidx: int32, inputstream: PLLStream)
proc rawGetTok*(L: var TLexer, tok: var TToken)
# reads in the next token into tok and skips it
proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} = proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} =
newLineInfo(L.fileIdx, tok.line, tok.col) newLineInfo(L.fileIdx, tok.line, tok.col)
proc closeLexer*(lex: var TLexer) proc isKeyword*(kind: TTokType): bool =
proc printTok*(tok: TToken)
proc tokToStr*(tok: TToken): string
proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream) =
openLexer(lex, filename.fileInfoIdx, inputstream)
proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "")
proc isKeyword(kind: TTokType): bool =
result = (kind >= tokKeywordLow) and (kind <= tokKeywordHigh) result = (kind >= tokKeywordLow) and (kind <= tokKeywordHigh)
proc isNimIdentifier*(s: string): bool = proc isNimIdentifier*(s: string): bool =
@ -181,10 +167,8 @@ proc prettyTok*(tok: TToken): string =
else: result = tokToStr(tok) else: result = tokToStr(tok)
proc printTok*(tok: TToken) = proc printTok*(tok: TToken) =
write(stdout, tok.line, ":", tok.col, "\t") msgWriteln($tok.line & ":" & $tok.col & "\t" &
write(stdout, TokTypeToStr[tok.tokType]) TokTypeToStr[tok.tokType] & " " & tokToStr(tok))
write(stdout, " ")
writeln(stdout, tokToStr(tok))
var dummyIdent: PIdent var dummyIdent: PIdent
@ -208,14 +192,17 @@ proc fillToken(L: var TToken) =
L.base = base10 L.base = base10
L.ident = dummyIdent L.ident = dummyIdent
proc openLexer(lex: var TLexer, fileIdx: int32, inputstream: PLLStream) = proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream) =
openBaseLexer(lex, inputstream) openBaseLexer(lex, inputstream)
lex.fileIdx = fileidx lex.fileIdx = fileidx
lex.indentAhead = - 1 lex.indentAhead = - 1
lex.currLineIndent = 0 lex.currLineIndent = 0
inc(lex.lineNumber, inputstream.lineOffset) inc(lex.lineNumber, inputstream.lineOffset)
proc closeLexer(lex: var TLexer) = proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream) =
openLexer(lex, filename.fileInfoIdx, inputstream)
proc closeLexer*(lex: var TLexer) =
inc(gLinesCompiled, lex.lineNumber) inc(gLinesCompiled, lex.lineNumber)
closeBaseLexer(lex) closeBaseLexer(lex)
@ -231,9 +218,13 @@ proc dispMessage(L: TLexer; info: TLineInfo; msg: TMsgKind; arg: string) =
else: else:
L.errorHandler(info, msg, arg) L.errorHandler(info, msg, arg)
proc lexMessage(L: TLexer, msg: TMsgKind, arg = "") = proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") =
L.dispMessage(getLineInfo(L), msg, arg) L.dispMessage(getLineInfo(L), msg, arg)
proc lexMessageTok*(L: TLexer, msg: TMsgKind, tok: TToken, arg = "") =
var info = newLineInfo(L.fileIdx, tok.line, tok.col)
L.dispMessage(info, msg, arg)
proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") = proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart) var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart)
L.dispMessage(info, msg, arg) L.dispMessage(info, msg, arg)
@ -264,6 +255,19 @@ proc isFloatLiteral(s: string): bool =
return true return true
result = false result = false
{.push overflowChecks: off.}
# We need to parse the largest uint literal without overflow checks
proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int =
var i = start
if s[i] in {'0'..'9'}:
b = 0
while s[i] in {'0'..'9'}:
b = b * 10 + (ord(s[i]) - ord('0'))
inc(i)
while s[i] == '_': inc(i) # underscores are allowed and ignored
result = i - start
{.pop.} # overflowChecks
proc getNumber(L: var TLexer): TToken = proc getNumber(L: var TLexer): TToken =
var var
pos, endpos: int pos, endpos: int
@ -425,6 +429,12 @@ proc getNumber(L: var TLexer): TToken =
(result.tokType == tkFloat64Lit): (result.tokType == tkFloat64Lit):
result.fNumber = parseFloat(result.literal) result.fNumber = parseFloat(result.literal)
if result.tokType == tkIntLit: result.tokType = tkFloatLit 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
else: else:
result.iNumber = parseBiggestInt(result.literal) result.iNumber = parseBiggestInt(result.literal)
if (result.iNumber < low(int32)) or (result.iNumber > high(int32)): if (result.iNumber < low(int32)) or (result.iNumber > high(int32)):
@ -660,7 +670,7 @@ proc getOperator(L: var TLexer, tok: var TToken) =
inc(pos) inc(pos)
endOperator(L, tok, pos, h) endOperator(L, tok, pos, h)
# advance pos but don't store it in L.bufpos so the next token (which might # advance pos but don't store it in L.bufpos so the next token (which might
# be an operator too) gets the preceeding spaces: # be an operator too) gets the preceding spaces:
tok.strongSpaceB = 0 tok.strongSpaceB = 0
while buf[pos] == ' ': while buf[pos] == ' ':
inc pos inc pos
@ -674,7 +684,15 @@ proc scanComment(L: var TLexer, tok: var TToken) =
when not defined(nimfix): when not defined(nimfix):
assert buf[pos+1] == '#' assert buf[pos+1] == '#'
if buf[pos+2] == '[': if buf[pos+2] == '[':
lexMessagePos(L, warnDeprecated, pos, "use '## [' instead; '##['") if buf[pos+3] == ']':
# ##[] is the (rather complex) "cursor token" for idetools
tok.tokType = tkComment
tok.literal = "[]"
inc(L.bufpos, 4)
return
else:
lexMessagePos(L, warnDeprecated, pos, "use '## [' instead; '##['")
tok.tokType = tkComment tok.tokType = tkComment
# iNumber contains the number of '\n' in the token # iNumber contains the number of '\n' in the token
tok.iNumber = 0 tok.iNumber = 0
@ -760,7 +778,7 @@ proc skip(L: var TLexer, tok: var TToken) =
break # EndOfFile also leaves the loop break # EndOfFile also leaves the loop
L.bufpos = pos L.bufpos = pos
proc rawGetTok(L: var TLexer, tok: var TToken) = proc rawGetTok*(L: var TLexer, tok: var TToken) =
fillToken(tok) fillToken(tok)
if L.indentAhead >= 0: if L.indentAhead >= 0:
tok.indent = L.indentAhead tok.indent = L.indentAhead
@ -849,6 +867,15 @@ proc rawGetTok(L: var TLexer, tok: var TToken) =
of '`': of '`':
tok.tokType = tkAccent tok.tokType = tkAccent
inc(L.bufpos) inc(L.bufpos)
of '_':
inc(L.bufpos)
if L.buf[L.bufpos] notin SymChars:
tok.tokType = tkSymbol
tok.ident = getIdent("_")
else:
tok.literal = $c
tok.tokType = tkInvalid
lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
of '\"': of '\"':
# check for extended raw string literal: # check for extended raw string literal:
var rawMode = L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars var rawMode = L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars

View file

@ -30,47 +30,32 @@ type
PLLStream* = ref TLLStream PLLStream* = ref TLLStream
proc llStreamOpen*(data: string): PLLStream proc llStreamOpen*(data: string): PLLStream =
proc llStreamOpen*(f: var File): PLLStream
proc llStreamOpen*(filename: string, mode: FileMode): PLLStream
proc llStreamOpen*(): PLLStream
proc llStreamOpenStdIn*(): PLLStream
proc llStreamClose*(s: PLLStream)
proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int
proc llStreamReadLine*(s: PLLStream, line: var string): bool
proc llStreamReadAll*(s: PLLStream): string
proc llStreamWrite*(s: PLLStream, data: string)
proc llStreamWrite*(s: PLLStream, data: char)
proc llStreamWrite*(s: PLLStream, buf: pointer, buflen: int)
proc llStreamWriteln*(s: PLLStream, data: string)
# implementation
proc llStreamOpen(data: string): PLLStream =
new(result) new(result)
result.s = data result.s = data
result.kind = llsString result.kind = llsString
proc llStreamOpen(f: var File): PLLStream = proc llStreamOpen*(f: File): PLLStream =
new(result) new(result)
result.f = f result.f = f
result.kind = llsFile result.kind = llsFile
proc llStreamOpen(filename: string, mode: FileMode): PLLStream = proc llStreamOpen*(filename: string, mode: FileMode): PLLStream =
new(result) new(result)
result.kind = llsFile result.kind = llsFile
if not open(result.f, filename, mode): result = nil if not open(result.f, filename, mode): result = nil
proc llStreamOpen(): PLLStream = proc llStreamOpen*(): PLLStream =
new(result) new(result)
result.kind = llsNone result.kind = llsNone
proc llStreamOpenStdIn(): PLLStream = proc llStreamOpenStdIn*(): PLLStream =
new(result) new(result)
result.kind = llsStdIn result.kind = llsStdIn
result.s = "" result.s = ""
result.lineOffset = -1 result.lineOffset = -1
proc llStreamClose(s: PLLStream) = proc llStreamClose*(s: PLLStream) =
case s.kind case s.kind
of llsNone, llsString, llsStdIn: of llsNone, llsString, llsStdIn:
discard discard
@ -130,7 +115,7 @@ proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
copyMem(buf, addr(s.s[s.rd]), result) copyMem(buf, addr(s.s[s.rd]), result)
inc(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 case s.kind
of llsNone: of llsNone:
result = 0 result = 0
@ -144,7 +129,7 @@ proc llStreamRead(s: PLLStream, buf: pointer, bufLen: int): int =
of llsStdIn: of llsStdIn:
result = llReadFromStdin(s, buf, bufLen) result = llReadFromStdin(s, buf, bufLen)
proc llStreamReadLine(s: PLLStream, line: var string): bool = proc llStreamReadLine*(s: PLLStream, line: var string): bool =
setLen(line, 0) setLen(line, 0)
case s.kind case s.kind
of llsNone: of llsNone:
@ -168,7 +153,7 @@ proc llStreamReadLine(s: PLLStream, line: var string): bool =
of llsStdIn: of llsStdIn:
result = readLine(stdin, line) result = readLine(stdin, line)
proc llStreamWrite(s: PLLStream, data: string) = proc llStreamWrite*(s: PLLStream, data: string) =
case s.kind case s.kind
of llsNone, llsStdIn: of llsNone, llsStdIn:
discard discard
@ -178,11 +163,11 @@ proc llStreamWrite(s: PLLStream, data: string) =
of llsFile: of llsFile:
write(s.f, data) write(s.f, data)
proc llStreamWriteln(s: PLLStream, data: string) = proc llStreamWriteln*(s: PLLStream, data: string) =
llStreamWrite(s, data) llStreamWrite(s, data)
llStreamWrite(s, "\n") llStreamWrite(s, "\n")
proc llStreamWrite(s: PLLStream, data: char) = proc llStreamWrite*(s: PLLStream, data: char) =
var c: char var c: char
case s.kind case s.kind
of llsNone, llsStdIn: of llsNone, llsStdIn:
@ -194,7 +179,7 @@ proc llStreamWrite(s: PLLStream, data: char) =
c = data c = data
discard writeBuffer(s.f, addr(c), sizeof(c)) 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 case s.kind
of llsNone, llsStdIn: of llsNone, llsStdIn:
discard discard
@ -206,7 +191,7 @@ proc llStreamWrite(s: PLLStream, buf: pointer, buflen: int) =
of llsFile: of llsFile:
discard writeBuffer(s.f, buf, buflen) discard writeBuffer(s.f, buf, buflen)
proc llStreamReadAll(s: PLLStream): string = proc llStreamReadAll*(s: PLLStream): string =
const const
bufSize = 2048 bufSize = 2048
case s.kind case s.kind

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -22,7 +22,9 @@ proc considerQuotedIdent*(n: PNode): PIdent =
of nkSym: result = n.sym.name of nkSym: result = n.sym.name
of nkAccQuoted: of nkAccQuoted:
case n.len case n.len
of 0: globalError(n.info, errIdentifierExpected, renderTree(n)) of 0:
localError(n.info, errIdentifierExpected, renderTree(n))
result = getIdent"<Error>"
of 1: result = considerQuotedIdent(n.sons[0]) of 1: result = considerQuotedIdent(n.sons[0])
else: else:
var id = "" var id = ""
@ -31,11 +33,14 @@ proc considerQuotedIdent*(n: PNode): PIdent =
case x.kind case x.kind
of nkIdent: id.add(x.ident.s) of nkIdent: id.add(x.ident.s)
of nkSym: id.add(x.sym.name.s) of nkSym: id.add(x.sym.name.s)
else: globalError(n.info, errIdentifierExpected, renderTree(n)) else:
localError(n.info, errIdentifierExpected, renderTree(n))
return getIdent"<Error>"
result = getIdent(id) result = getIdent(id)
of nkOpenSymChoice, nkClosedSymChoice: result = n.sons[0].sym.name of nkOpenSymChoice, nkClosedSymChoice: result = n.sons[0].sym.name
else: else:
globalError(n.info, errIdentifierExpected, renderTree(n)) localError(n.info, errIdentifierExpected, renderTree(n))
result = getIdent"<Error>"
template addSym*(scope: PScope, s: PSym) = template addSym*(scope: PScope, s: PSym) =
strTableAdd(scope.symbols, s) strTableAdd(scope.symbols, s)
@ -82,6 +87,16 @@ proc searchInScopes*(c: PContext, s: PIdent): PSym =
if result != nil: return if result != nil: return
result = nil result = nil
proc debugScopes*(c: PContext; limit=0) {.deprecated.} =
var i = 0
for scope in walkScopes(c.currentScope):
echo "scope ", i
for h in 0 .. high(scope.symbols.data):
if scope.symbols.data[h] != nil:
echo scope.symbols.data[h].name.s
if i == limit: break
inc i
proc searchInScopes*(c: PContext, s: PIdent, filter: TSymKinds): PSym = proc searchInScopes*(c: PContext, s: PIdent, filter: TSymKinds): PSym =
for scope in walkScopes(c.currentScope): for scope in walkScopes(c.currentScope):
result = strTableGet(scope.symbols, s) result = strTableGet(scope.symbols, s)
@ -161,7 +176,7 @@ proc addInterfaceDeclAux(c: PContext, sym: PSym) =
if sfExported in sym.flags: if sfExported in sym.flags:
# add to interface: # add to interface:
if c.module != nil: strTableAdd(c.module.tab, sym) if c.module != nil: strTableAdd(c.module.tab, sym)
else: internalError(sym.info, "AddInterfaceDeclAux") else: internalError(sym.info, "addInterfaceDeclAux")
proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) = proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) =
addDeclAt(scope, sym) addDeclAt(scope, sym)

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -13,9 +13,12 @@ const
genPrefix* = ":tmp" # prefix for generated names genPrefix* = ":tmp" # prefix for generated names
import ast, astalgo, types, idents, magicsys, msgs, options import ast, astalgo, types, idents, magicsys, msgs, options
from guards import createMagic
from trees import getMagic from trees import getMagic
proc newDeref*(n: PNode): PNode {.inline.} =
result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
addSon(result, n)
proc newTupleAccess*(tup: PNode, i: int): PNode = proc newTupleAccess*(tup: PNode, i: int): PNode =
result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes( result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
abstractInst).sons[i]) abstractInst).sons[i])
@ -204,6 +207,17 @@ proc flowVarKind(t: PType): TFlowVarKind =
elif containsGarbageCollectedRef(t): fvInvalid elif containsGarbageCollectedRef(t): fvInvalid
else: fvBlob else: fvBlob
proc typeNeedsNoDeepCopy(t: PType): bool =
var t = t.skipTypes(abstractInst)
# for the tconvexhull example (and others) we're a bit lax here and pretend
# seqs and strings are *by value* only and 'shallow' doesn't exist!
if t.kind == tyString: return true
# note that seq[T] is fine, but 'var seq[T]' is not, so we need to skip 'var'
# for the stricter check and likewise we can skip 'seq' for a less
# strict check:
if t.kind in {tyVar, tySequence}: t = t.sons[0]
result = not containsGarbageCollectedRef(t)
proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType; proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType;
v: PNode; useShallowCopy=false): PSym = v: PNode; useShallowCopy=false): PSym =
result = newSym(skTemp, getIdent(genPrefix), owner, varSection.info) result = newSym(skTemp, getIdent(genPrefix), owner, varSection.info)
@ -216,7 +230,7 @@ proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType;
vpart.sons[2] = if varInit.isNil: v else: ast.emptyNode vpart.sons[2] = if varInit.isNil: v else: ast.emptyNode
varSection.add vpart varSection.add vpart
if varInit != nil: if varInit != nil:
if useShallowCopy: if useShallowCopy and typeNeedsNoDeepCopy(typ):
varInit.add newFastAsgnStmt(newSymNode(result), v) varInit.add newFastAsgnStmt(newSymNode(result), v)
else: else:
let deepCopyCall = newNodeI(nkCall, varInit.info, 3) let deepCopyCall = newNodeI(nkCall, varInit.info, 3)
@ -372,11 +386,11 @@ proc getRoot*(n: PNode): PSym =
if getMagic(n) == mSlice: result = getRoot(n.sons[1]) if getMagic(n) == mSlice: result = getRoot(n.sons[1])
else: discard else: discard
proc newIntLit(value: BiggestInt): PNode = proc newIntLit*(value: BiggestInt): PNode =
result = nkIntLit.newIntNode(value) result = nkIntLit.newIntNode(value)
result.typ = getSysType(tyInt) result.typ = getSysType(tyInt)
proc genHigh(n: PNode): PNode = proc genHigh*(n: PNode): PNode =
if skipTypes(n.typ, abstractVar).kind in {tyArrayConstr, tyArray}: if skipTypes(n.typ, abstractVar).kind in {tyArrayConstr, tyArray}:
result = newIntLit(lastOrd(skipTypes(n.typ, abstractVar))) result = newIntLit(lastOrd(skipTypes(n.typ, abstractVar)))
else: else:

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -19,12 +19,6 @@ import
from magicsys import systemModule, resetSysTypes from magicsys import systemModule, resetSysTypes
const
hasLLVM_Backend = false
when hasLLVM_Backend:
import llvmgen
proc rodPass = proc rodPass =
if optSymbolFiles in gGlobalOptions: if optSymbolFiles in gGlobalOptions:
registerPass(rodwritePass) registerPass(rodwritePass)
@ -60,20 +54,16 @@ proc commandDoc2 =
finishDoc2Pass(gProjectName) finishDoc2Pass(gProjectName)
proc commandCompileToC = proc commandCompileToC =
extccomp.initVars()
semanticPasses() semanticPasses()
registerPass(cgenPass) registerPass(cgenPass)
rodPass() rodPass()
#registerPass(cleanupPass()) #registerPass(cleanupPass())
if optCaasEnabled in gGlobalOptions:
# echo "BEFORE CHECK DEP"
# discard checkDepMem(gProjectMainIdx)
# echo "CHECK DEP COMPLETE"
discard
compileProject() compileProject()
cgenWriteModules() cgenWriteModules()
if gCmd != cmdRun: if gCmd != cmdRun:
extccomp.callCCompiler(changeFileExt(gProjectFull, "")) extccomp.callCCompiler(if gProjectName == "-": "stdinfile" else: changeFileExt(gProjectFull, ""))
if isServing: if isServing:
# caas will keep track only of the compilation commands # caas will keep track only of the compilation commands
@ -111,14 +101,6 @@ proc commandCompileToC =
ccgutils.resetCaches() ccgutils.resetCaches()
GC_fullCollect() GC_fullCollect()
when hasLLVM_Backend:
proc commandCompileToLLVM =
semanticPasses()
registerPass(llvmgen.llvmgenPass())
rodPass()
#registerPass(cleanupPass())
compileProject()
proc commandCompileToJS = proc commandCompileToJS =
#incl(gGlobalOptions, optSafeCode) #incl(gGlobalOptions, optSafeCode)
setTarget(osJS, cpuJS) setTarget(osJS, cpuJS)
@ -188,20 +170,18 @@ proc commandSuggest =
# cache in a state where "no recompilation is necessary", but the # cache in a state where "no recompilation is necessary", but the
# cgen pass was never executed at all. # cgen pass was never executed at all.
commandCompileToC() commandCompileToC()
if gDirtyBufferIdx != 0: let gDirtyBufferIdx = gTrackPos.fileIndex
discard compileModule(gDirtyBufferIdx, {sfDirty}) discard compileModule(gDirtyBufferIdx, {sfDirty})
resetModule(gDirtyBufferIdx) resetModule(gDirtyBufferIdx)
if optDef in gGlobalOptions:
defFromSourceMap(optTrackPos)
else: else:
msgs.gErrorMax = high(int) # do not stop after first error msgs.gErrorMax = high(int) # do not stop after first error
semanticPasses() semanticPasses()
rodPass() rodPass()
# XXX: this handles the case when the dirty buffer is the main file, # XXX: this handles the case when the dirty buffer is the main file,
# but doesn't handle the case when it's imported module # but doesn't handle the case when it's imported module
var projFile = if gProjectMainIdx == gDirtyOriginalIdx: gDirtyBufferIdx #var projFile = if gProjectMainIdx == gDirtyOriginalIdx: gDirtyBufferIdx
else: gProjectMainIdx # else: gProjectMainIdx
compileProject(projFile) compileProject() #(projFile)
proc resetMemory = proc resetMemory =
resetCompilationLists() resetCompilationLists()
@ -232,7 +212,6 @@ proc resetMemory =
# rodread.gMods # rodread.gMods
# !! ropes.cache # !! ropes.cache
# semthreads.computed?
# #
# suggest.usageSym # suggest.usageSym
# #
@ -273,7 +252,6 @@ proc mainCommand* =
commandCompileToC() commandCompileToC()
of "cpp", "compiletocpp": of "cpp", "compiletocpp":
gCmd = cmdCompileToCpp gCmd = cmdCompileToCpp
if cCompiler == ccGcc: setCC("gcc")
defineSymbol("cpp") defineSymbol("cpp")
commandCompileToC() commandCompileToC()
of "objc", "compiletooc": of "objc", "compiletooc":
@ -290,12 +268,6 @@ proc mainCommand* =
of "js", "compiletojs": of "js", "compiletojs":
gCmd = cmdCompileToJS gCmd = cmdCompileToJS
commandCompileToJS() commandCompileToJS()
of "compiletollvm":
gCmd = cmdCompileToLLVM
when hasLLVM_Backend:
CommandCompileToLLVM()
else:
rawMessage(errInvalidCommandX, command)
of "doc": of "doc":
wantMainModule() wantMainModule()
gCmd = cmdDoc gCmd = cmdDoc
@ -392,7 +364,9 @@ proc mainCommand* =
gVerbosity > 0): gVerbosity > 0):
rawMessage(hintSuccessX, [$gLinesCompiled, rawMessage(hintSuccessX, [$gLinesCompiled,
formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3), formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3),
formatSize(getTotalMem())]) formatSize(getTotalMem()),
if condSyms.isDefined("release"): "Release Build"
else: "Debug Build"])
when PrintRopeCacheStats: when PrintRopeCacheStats:
echo "rope cache stats: " echo "rope cache stats: "

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -34,9 +34,6 @@ proc getModule(fileIdx: int32): PSym =
if fileIdx >= 0 and fileIdx < gCompiledModules.len: if fileIdx >= 0 and fileIdx < gCompiledModules.len:
result = gCompiledModules[fileIdx] result = gCompiledModules[fileIdx]
template compiledAt(x: PSym): expr =
gMemCacheData[x.position].compiledAt
template crc(x: PSym): expr = template crc(x: PSym): expr =
gMemCacheData[x.position].crc gMemCacheData[x.position].crc
@ -74,10 +71,12 @@ proc addDep(x: PSym, dep: int32) =
proc resetModule*(fileIdx: int32) = proc resetModule*(fileIdx: int32) =
# echo "HARD RESETTING ", fileIdx.toFilename # echo "HARD RESETTING ", fileIdx.toFilename
gMemCacheData[fileIdx].needsRecompile = Yes if fileIdx <% gMemCacheData.len:
gCompiledModules[fileIdx] = nil gMemCacheData[fileIdx].needsRecompile = Yes
cgendata.gModules[fileIdx] = nil if fileIdx <% gCompiledModules.len:
resetSourceMap(fileIdx) gCompiledModules[fileIdx] = nil
if fileIdx <% cgendata.gModules.len:
cgendata.gModules[fileIdx] = nil
proc resetAllModules* = proc resetAllModules* =
for i in 0..gCompiledModules.high: for i in 0..gCompiledModules.high:
@ -117,7 +116,7 @@ proc newModule(fileIdx: int32): PSym =
result.kind = skModule result.kind = skModule
let filename = fileIdx.toFullPath let filename = fileIdx.toFullPath
result.name = getIdent(splitFile(filename).name) result.name = getIdent(splitFile(filename).name)
if not isNimIdentifier(result.name.s): if result.name.s != "-" and not isNimIdentifier(result.name.s):
rawMessage(errInvalidModuleName, result.name.s) rawMessage(errInvalidModuleName, result.name.s)
result.info = newLineInfo(fileIdx, 1, 1) result.info = newLineInfo(fileIdx, 1, 1)

View file

@ -10,9 +10,6 @@
import import
options, strutils, os, tables, ropes, platform options, strutils, os, tables, ropes, platform
when useCaas:
import sockets
type type
TMsgKind* = enum TMsgKind* = enum
errUnknown, errIllFormedAstX, errInternal, errCannotOpenFile, errGenerated, errUnknown, errIllFormedAstX, errInternal, errCannotOpenFile, errGenerated,
@ -72,7 +69,7 @@ type
errInvalidOrderInArrayConstructor, errInvalidOrderInArrayConstructor,
errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry, errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry,
errOptionExpected, errXisNoLabel, errNotAllCasesCovered, errOptionExpected, errXisNoLabel, errNotAllCasesCovered,
errUnkownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable, errUnknownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable,
errNoPragmasAllowedForX, errNoGenericParamsAllowedForX, errNoPragmasAllowedForX, errNoGenericParamsAllowedForX,
errInvalidParamKindX, errDefaultArgumentInvalid, errNamedParamHasToBeIdent, errInvalidParamKindX, errDefaultArgumentInvalid, errNamedParamHasToBeIdent,
errNoReturnTypeForX, errConvNeedsOneArg, errInvalidPragmaX, errNoReturnTypeForX, errConvNeedsOneArg, errInvalidPragmaX,
@ -92,7 +89,7 @@ type
errTIsNotAConcreteType, errTIsNotAConcreteType,
errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely, errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly,
errOnlyACallOpCanBeDelegator, errUsingNoSymbol, errOnlyACallOpCanBeDelegator, errUsingNoSymbol,
errMacroBodyDependsOnGenericTypes, errMacroBodyDependsOnGenericTypes,
errDestructorNotGenericEnough, errDestructorNotGenericEnough,
@ -116,11 +113,12 @@ type
warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
warnUnknownSubstitutionX, warnLanguageXNotSupported, warnUnknownSubstitutionX, warnLanguageXNotSupported,
warnFieldXNotSupported, warnCommentXIgnored, warnFieldXNotSupported, warnCommentXIgnored,
warnNilStatement, warnAnalysisLoophole, warnNilStatement, warnTypelessParam,
warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode, warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode,
warnEachIdentIsTuple, warnShadowIdent, warnEachIdentIsTuple, warnShadowIdent,
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
warnUninit, warnGcMem, warnLockLevel, warnUser, warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
warnUser,
hintSuccess, hintSuccessX, hintSuccess, hintSuccessX,
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
@ -282,7 +280,7 @@ const
errOptionExpected: "option expected, but found \'$1\'", errOptionExpected: "option expected, but found \'$1\'",
errXisNoLabel: "\'$1\' is not a label", errXisNoLabel: "\'$1\' is not a label",
errNotAllCasesCovered: "not all cases are covered", errNotAllCasesCovered: "not all cases are covered",
errUnkownSubstitionVar: "unknown substitution variable: \'$1\'", errUnknownSubstitionVar: "unknown substitution variable: \'$1\'",
errComplexStmtRequiresInd: "complex statement requires indentation", errComplexStmtRequiresInd: "complex statement requires indentation",
errXisNotCallable: "\'$1\' is not callable", errXisNotCallable: "\'$1\' is not callable",
errNoPragmasAllowedForX: "no pragmas allowed for $1", errNoPragmasAllowedForX: "no pragmas allowed for $1",
@ -314,7 +312,7 @@ const
errXOnlyAtModuleScope: "\'$1\' is only allowed at top level", errXOnlyAtModuleScope: "\'$1\' is only allowed at top level",
errXNeedsParamObjectType: "'$1' needs a parameter that has an object type", errXNeedsParamObjectType: "'$1' needs a parameter that has an object type",
errTemplateInstantiationTooNested: "template/macro instantiation too nested", errTemplateInstantiationTooNested: "template/macro instantiation too nested",
errInstantiationFrom: "instantiation from here", errInstantiationFrom: "template/generic instantiation from here",
errInvalidIndexValueForTuple: "invalid index value for tuple subscript", errInvalidIndexValueForTuple: "invalid index value for tuple subscript",
errCommandExpectsFilename: "command expects a filename argument", errCommandExpectsFilename: "command expects a filename argument",
errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file", errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
@ -328,12 +326,12 @@ const
errXisNoValidIndexFile: "\'$1\' is no valid index file", errXisNoValidIndexFile: "\'$1\' is no valid index file",
errCannotRenderX: "cannot render reStructuredText element \'$1\'", errCannotRenderX: "cannot render reStructuredText element \'$1\'",
errVarVarTypeNotAllowed: "type \'var var\' is not allowed", errVarVarTypeNotAllowed: "type \'var var\' is not allowed",
errInstantiateXExplicitely: "instantiate '$1' explicitely", errInstantiateXExplicitly: "instantiate '$1' explicitly",
errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator", errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator",
errUsingNoSymbol: "'$1' is not a variable, constant or a proc name", errUsingNoSymbol: "'$1' is not a variable, constant or a proc name",
errMacroBodyDependsOnGenericTypes: "the macro body cannot be compiled, " & errMacroBodyDependsOnGenericTypes: "the macro body cannot be compiled, " &
"because the parameter '$1' has a generic type", "because the parameter '$1' has a generic type",
errDestructorNotGenericEnough: "Destructor signarue is too specific. " & errDestructorNotGenericEnough: "Destructor signature is too specific. " &
"A destructor must be associated will all instantiations of a generic type", "A destructor must be associated will all instantiations of a generic type",
errInlineIteratorsAsProcParams: "inline iterators can be used as parameters only for " & errInlineIteratorsAsProcParams: "inline iterators can be used as parameters only for " &
"templates, macros and other inline iterators", "templates, macros and other inline iterators",
@ -362,7 +360,7 @@ const
errCannotInferReturnType: "cannot infer the return type of the proc", errCannotInferReturnType: "cannot infer the return type of the proc",
errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " & errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " &
"it is used as an operand to another routine and the types " & "it is used as an operand to another routine and the types " &
"of the generic paramers can be infered from the expected signature.", "of the generic paramers can be inferred from the expected signature.",
errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target", errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target",
errUser: "$1", errUser: "$1",
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]", warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
@ -379,7 +377,7 @@ const
warnFieldXNotSupported: "field \'$1\' not supported [FieldXNotSupported]", warnFieldXNotSupported: "field \'$1\' not supported [FieldXNotSupported]",
warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]", warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]",
warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead [NilStmt]", warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead [NilStmt]",
warnAnalysisLoophole: "thread analysis incomplete due to unknown call '$1' [AnalysisLoophole]", warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template' [TypelessParam]",
warnDifferentHeaps: "possible inconsistency of thread local heaps [DifferentHeaps]", warnDifferentHeaps: "possible inconsistency of thread local heaps [DifferentHeaps]",
warnWriteToForeignHeap: "write to foreign heap [WriteToForeignHeap]", warnWriteToForeignHeap: "write to foreign heap [WriteToForeignHeap]",
warnUnsafeCode: "unsafe code: '$1' [UnsafeCode]", warnUnsafeCode: "unsafe code: '$1' [UnsafeCode]",
@ -389,13 +387,15 @@ const
warnProveField: "cannot prove that field '$1' is accessible [ProveField]", warnProveField: "cannot prove that field '$1' is accessible [ProveField]",
warnProveIndex: "cannot prove index '$1' is valid [ProveIndex]", warnProveIndex: "cannot prove index '$1' is valid [ProveIndex]",
warnGcUnsafe: "not GC-safe: '$1' [GcUnsafe]", warnGcUnsafe: "not GC-safe: '$1' [GcUnsafe]",
warnGcUnsafe2: "cannot prove '$1' is GC-safe. Does not compile with --threads:on.", warnGcUnsafe2: "$1",
warnUninit: "'$1' might not have been initialized [Uninit]", warnUninit: "'$1' might not have been initialized [Uninit]",
warnGcMem: "'$1' uses GC'ed memory [GcMem]", 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]", warnLockLevel: "$1 [LockLevel]",
warnResultShadowed: "Special variable 'result' is shadowed. [ResultShadowed]",
warnUser: "$1 [User]", warnUser: "$1 [User]",
hintSuccess: "operation successful [Success]", hintSuccess: "operation successful [Success]",
hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]", hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#) [SuccessX]",
hintLineTooLong: "line too long [LineTooLong]", hintLineTooLong: "line too long [LineTooLong]",
hintXDeclaredButNotUsed: "\'$1\' is declared but not used [XDeclaredButNotUsed]", hintXDeclaredButNotUsed: "\'$1\' is declared but not used [XDeclaredButNotUsed]",
hintConvToBaseNotNeeded: "conversion to base object is not needed [ConvToBaseNotNeeded]", hintConvToBaseNotNeeded: "conversion to base object is not needed [ConvToBaseNotNeeded]",
@ -413,17 +413,17 @@ const
hintUser: "$1 [User]"] hintUser: "$1 [User]"]
const const
WarningsToStr*: array[0..28, string] = ["CannotOpenFile", "OctalEscape", WarningsToStr*: array[0..30, string] = ["CannotOpenFile", "OctalEscape",
"XIsNeverRead", "XmightNotBeenInit", "XIsNeverRead", "XmightNotBeenInit",
"Deprecated", "ConfigDeprecated", "Deprecated", "ConfigDeprecated",
"SmallLshouldNotBeUsed", "UnknownMagic", "SmallLshouldNotBeUsed", "UnknownMagic",
"RedefinitionOfLabel", "UnknownSubstitutionX", "RedefinitionOfLabel", "UnknownSubstitutionX",
"LanguageXNotSupported", "FieldXNotSupported", "LanguageXNotSupported", "FieldXNotSupported",
"CommentXIgnored", "NilStmt", "CommentXIgnored", "NilStmt",
"AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap", "TypelessParam", "DifferentHeaps", "WriteToForeignHeap",
"UnsafeCode", "EachIdentIsTuple", "ShadowIdent", "UnsafeCode", "EachIdentIsTuple", "ShadowIdent",
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
"GcMem", "LockLevel", "User"] "GcMem", "Destructor", "LockLevel", "ResultShadowed", "User"]
HintsToStr*: array[0..16, string] = ["Success", "SuccessX", "LineTooLong", HintsToStr*: array[0..16, string] = ["Success", "SuccessX", "LineTooLong",
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
@ -445,21 +445,24 @@ type
TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
TNoteKinds* = set[TNoteKind] TNoteKinds* = set[TNoteKind]
TFileInfo*{.final.} = object TFileInfo* = object
fullPath: string # This is a canonical full filesystem path fullPath: string # This is a canonical full filesystem path
projPath*: string # This is relative to the project's root projPath*: string # This is relative to the project's root
shortName*: string # short name of the module shortName*: string # short name of the module
quotedName*: PRope # cached quoted short name for codegen quotedName*: Rope # cached quoted short name for codegen
# purposes # purposes
lines*: seq[PRope] # the source code of the module lines*: seq[Rope] # the source code of the module
# used for better error messages and # used for better error messages and
# embedding the original source in the # embedding the original source in the
# generated code # generated code
dirtyfile: string # the file that is actually read into memory
# and parsed; usually 'nil' but is used
# for 'nimsuggest'
TLineInfo*{.final.} = object # This is designed to be as small as possible, TLineInfo* = object # This is designed to be as small as possible,
# because it is used # because it is used
# in syntax nodes. We safe space here by using # in syntax nodes. We save space here by using
# two int16 and an int32. # two int16 and an int32.
# On 64 bit and on 32 bit systems this is # On 64 bit and on 32 bit systems this is
# only 8 bytes. # only 8 bytes.
@ -490,24 +493,20 @@ proc toCChar*(c: char): string =
of '\'', '\"', '\\': result = '\\' & c of '\'', '\"', '\\': result = '\\' & c
else: result = $(c) else: result = $(c)
proc makeCString*(s: string): PRope = proc makeCString*(s: string): Rope =
# BUGFIX: We have to split long strings into many ropes. Otherwise
# this could trigger an InternalError(). See the ropes module for
# further information.
const const
MaxLineLength = 64 MaxLineLength = 64
result = nil result = nil
var res = "\"" var res = newStringOfCap(int(s.len.toFloat * 1.1) + 1)
add(res, "\"")
for i in countup(0, len(s) - 1): for i in countup(0, len(s) - 1):
if (i + 1) mod MaxLineLength == 0: if (i + 1) mod MaxLineLength == 0:
add(res, '\"') add(res, '\"')
add(res, tnl) add(res, tnl)
app(result, toRope(res)) # reset: add(res, '\"')
setLen(res, 1)
res[0] = '\"'
add(res, toCChar(s[i])) add(res, toCChar(s[i]))
add(res, '\"') add(res, '\"')
app(result, toRope(res)) add(result, rope(res))
proc newFileInfo(fullPath, projPath: string): TFileInfo = proc newFileInfo(fullPath, projPath: string): TFileInfo =
@ -521,7 +520,7 @@ proc newFileInfo(fullPath, projPath: string): TFileInfo =
if optEmbedOrigSrc in gGlobalOptions or true: if optEmbedOrigSrc in gGlobalOptions or true:
result.lines = @[] result.lines = @[]
proc fileInfoIdx*(filename: string): int32 = proc fileInfoIdx*(filename: string; isKnownFile: var bool): int32 =
var var
canon: string canon: string
pseudoPath = false pseudoPath = false
@ -538,11 +537,16 @@ proc fileInfoIdx*(filename: string): int32 =
if filenameToIndexTbl.hasKey(canon): if filenameToIndexTbl.hasKey(canon):
result = filenameToIndexTbl[canon] result = filenameToIndexTbl[canon]
else: else:
isKnownFile = false
result = fileInfos.len.int32 result = fileInfos.len.int32
fileInfos.add(newFileInfo(canon, if pseudoPath: filename fileInfos.add(newFileInfo(canon, if pseudoPath: filename
else: canon.shortenDir)) else: canon.shortenDir))
filenameToIndexTbl[canon] = result filenameToIndexTbl[canon] = result
proc fileInfoIdx*(filename: string): int32 =
var dummy: bool
result = fileInfoIdx(filename, dummy)
proc newLineInfo*(fileInfoIdx: int32, line, col: int): TLineInfo = proc newLineInfo*(fileInfoIdx: int32, line, col: int): TLineInfo =
result.fileIndex = fileInfoIdx result.fileIndex = fileInfoIdx
result.line = int16(line) result.line = int16(line)
@ -560,7 +564,7 @@ var gCodegenLineInfo* = newLineInfo(int32(1), 1, 1)
proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} = proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} =
raise newException(ERecoverableError, msg) raise newException(ERecoverableError, msg)
proc sourceLine*(i: TLineInfo): PRope proc sourceLine*(i: TLineInfo): Rope
var var
gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} - gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} -
@ -571,9 +575,6 @@ var
gWarnCounter*: int = 0 gWarnCounter*: int = 0
gErrorMax*: int = 1 # stop after gErrorMax errors gErrorMax*: int = 1 # stop after gErrorMax errors
when useCaas:
var stdoutSocket*: Socket
proc unknownLineInfo*(): TLineInfo = proc unknownLineInfo*(): TLineInfo =
result.line = int16(-1) result.line = int16(-1)
result.col = int16(-1) result.col = int16(-1)
@ -582,35 +583,20 @@ proc unknownLineInfo*(): TLineInfo =
var var
msgContext: seq[TLineInfo] = @[] msgContext: seq[TLineInfo] = @[]
lastError = unknownLineInfo() lastError = unknownLineInfo()
bufferedMsgs*: seq[string]
errorOutputs* = {eStdOut, eStdErr} errorOutputs* = {eStdOut, eStdErr}
writelnHook*: proc (output: string) {.closure.}
proc clearBufferedMsgs* =
bufferedMsgs = nil
proc suggestWriteln*(s: string) = proc suggestWriteln*(s: string) =
if eStdOut in errorOutputs: if eStdOut in errorOutputs:
when useCaas: if isNil(writelnHook): writeln(stdout, s)
if isNil(stdoutSocket): writeln(stdout, s) else: writelnHook(s)
else:
writeln(stdout, s)
stdoutSocket.send(s & "\c\L")
else:
writeln(stdout, s)
if eInMemory in errorOutputs: proc msgQuit*(x: int8) = quit x
bufferedMsgs.safeAdd(s) proc msgQuit*(x: string) = quit x
proc suggestQuit*() = proc suggestQuit*() =
if not isServing: raise newException(ESuggestDone, "suggest done")
quit(0)
elif isWorkingWithDirtyBuffer:
# No need to compile the rest if we are working with a
# throw-away buffer. Incomplete dot expressions frequently
# found in dirty buffers will result in errors few steps
# from now anyway.
raise newException(ESuggestDone, "suggest done")
# this format is understood by many text editors: it is the same that # this format is understood by many text editors: it is the same that
# Borland and Freepascal use # Borland and Freepascal use
@ -646,6 +632,18 @@ proc toFullPath*(fileIdx: int32): string =
if fileIdx < 0: result = "???" if fileIdx < 0: result = "???"
else: result = fileInfos[fileIdx].fullPath else: result = fileInfos[fileIdx].fullPath
proc setDirtyFile*(fileIdx: int32; filename: string) =
assert fileIdx >= 0
fileInfos[fileIdx].dirtyFile = filename
proc toFullPathConsiderDirty*(fileIdx: int32): string =
if fileIdx < 0:
result = "???"
elif not fileInfos[fileIdx].dirtyFile.isNil:
result = fileInfos[fileIdx].dirtyFile
else:
result = fileInfos[fileIdx].fullPath
template toFilename*(info: TLineInfo): string = template toFilename*(info: TLineInfo): string =
info.fileIndex.toFilename info.fileIndex.toFilename
@ -653,12 +651,12 @@ template toFullPath*(info: TLineInfo): string =
info.fileIndex.toFullPath info.fileIndex.toFullPath
proc toMsgFilename*(info: TLineInfo): string = proc toMsgFilename*(info: TLineInfo): string =
if info.fileIndex < 0: result = "???" if info.fileIndex < 0:
result = "???"
elif gListFullPaths:
result = fileInfos[info.fileIndex].fullPath
else: else:
if gListFullPaths: result = fileInfos[info.fileIndex].projPath
result = fileInfos[info.fileIndex].fullPath
else:
result = fileInfos[info.fileIndex].projPath
proc toLinenumber*(info: TLineInfo): int {.inline.} = proc toLinenumber*(info: TLineInfo): int {.inline.} =
result = info.line result = info.line
@ -672,20 +670,13 @@ proc toFileLine*(info: TLineInfo): string {.inline.} =
proc toFileLineCol*(info: TLineInfo): string {.inline.} = proc toFileLineCol*(info: TLineInfo): string {.inline.} =
result = info.toFilename & "(" & $info.line & "," & $info.col & ")" result = info.toFilename & "(" & $info.line & "," & $info.col & ")"
template `$`*(info: TLineInfo): expr = toFileLineCol(info) proc `$`*(info: TLineInfo): string = toFileLineCol(info)
proc `??`* (info: TLineInfo, filename: string): bool = proc `??`* (info: TLineInfo, filename: string): bool =
# only for debugging purposes # only for debugging purposes
result = filename in info.toFilename result = filename in info.toFilename
var checkPoints*: seq[TLineInfo] = @[] var gTrackPos*: TLineInfo
var optTrackPos*: TLineInfo
proc addCheckpoint*(info: TLineInfo) =
checkPoints.add(info)
proc addCheckpoint*(filename: string, line: int) =
addCheckpoint(newLineInfo(filename, line, - 1))
proc outWriteln*(s: string) = proc outWriteln*(s: string) =
## Writes to stdout. Always. ## Writes to stdout. Always.
@ -693,15 +684,16 @@ proc outWriteln*(s: string) =
proc msgWriteln*(s: string) = proc msgWriteln*(s: string) =
## Writes to stdout. If --stdout option is given, writes to stderr instead. ## Writes to stdout. If --stdout option is given, writes to stderr instead.
if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
if optStdout in gGlobalOptions: #if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
if not isNil(writelnHook):
writelnHook(s)
elif optStdout in gGlobalOptions:
if eStdErr in errorOutputs: writeln(stderr, s) if eStdErr in errorOutputs: writeln(stderr, s)
else: else:
if eStdOut in errorOutputs: writeln(stdout, s) if eStdOut in errorOutputs: writeln(stdout, s)
if eInMemory in errorOutputs: bufferedMsgs.safeAdd(s)
proc coordToStr(coord: int): string = proc coordToStr(coord: int): string =
if coord == -1: result = "???" if coord == -1: result = "???"
else: result = $coord else: result = $coord
@ -713,26 +705,16 @@ proc msgKindToString*(kind: TMsgKind): string =
proc getMessageStr(msg: TMsgKind, arg: string): string = proc getMessageStr(msg: TMsgKind, arg: string): string =
result = msgKindToString(msg) % [arg] result = msgKindToString(msg) % [arg]
type
TCheckPointResult* = enum
cpNone, cpFuzzy, cpExact
proc inCheckpoint*(current: TLineInfo): TCheckPointResult =
for i in countup(0, high(checkPoints)):
if current.fileIndex == checkPoints[i].fileIndex:
if current.line == checkPoints[i].line and
abs(current.col-checkPoints[i].col) < 4:
return cpExact
if current.line >= checkPoints[i].line:
return cpFuzzy
type type
TErrorHandling = enum doNothing, doAbort, doRaise TErrorHandling = enum doNothing, doAbort, doRaise
proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) = proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) =
template quit = template quit =
if defined(debug) or gVerbosity >= 3 or msg == errInternal: if defined(debug) or gVerbosity >= 3 or msg == errInternal:
writeStackTrace() if stackTraceAvailable() and isNil(writelnHook):
writeStackTrace()
else:
msgWriteln("No stack traceback available\nTo create a stacktrace, rerun compilation with ./koch temp " & options.command & " <file>")
quit 1 quit 1
if msg >= fatalMin and msg <= fatalMax: if msg >= fatalMin and msg <= fatalMax:
@ -756,10 +738,13 @@ proc writeContext(lastinfo: TLineInfo) =
if msgContext[i] != lastinfo and msgContext[i] != info: if msgContext[i] != lastinfo and msgContext[i] != info:
msgWriteln(PosContextFormat % [toMsgFilename(msgContext[i]), msgWriteln(PosContextFormat % [toMsgFilename(msgContext[i]),
coordToStr(msgContext[i].line), coordToStr(msgContext[i].line),
coordToStr(msgContext[i].col), coordToStr(msgContext[i].col+1),
getMessageStr(errInstantiationFrom, "")]) getMessageStr(errInstantiationFrom, "")])
info = msgContext[i] info = msgContext[i]
proc ignoreMsgBecauseOfIdeTools(msg: TMsgKind): bool =
msg >= errGenerated and gCmd == cmdIdeTools and optIdeDebug notin gGlobalOptions
proc rawMessage*(msg: TMsgKind, args: openArray[string]) = proc rawMessage*(msg: TMsgKind, args: openArray[string]) =
var frmt: string var frmt: string
case msg case msg
@ -778,7 +763,8 @@ proc rawMessage*(msg: TMsgKind, args: openArray[string]) =
frmt = RawHintFormat frmt = RawHintFormat
inc(gHintCounter) inc(gHintCounter)
let s = `%`(frmt, `%`(msgKindToString(msg), args)) let s = `%`(frmt, `%`(msgKindToString(msg), args))
msgWriteln(s) if not ignoreMsgBecauseOfIdeTools(msg):
msgWriteln(s)
handleError(msg, doAbort, s) handleError(msg, doAbort, s)
proc rawMessage*(msg: TMsgKind, arg: string) = proc rawMessage*(msg: TMsgKind, arg: string) =
@ -786,8 +772,8 @@ proc rawMessage*(msg: TMsgKind, arg: string) =
proc writeSurroundingSrc(info: TLineInfo) = proc writeSurroundingSrc(info: TLineInfo) =
const indent = " " const indent = " "
msgWriteln(indent & info.sourceLine.ropeToStr) msgWriteln(indent & $info.sourceLine)
msgWriteln(indent & repeatChar(info.col, ' ') & '^') msgWriteln(indent & spaces(info.col) & '^')
proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string = proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
let frmt = case msg let frmt = case msg
@ -795,7 +781,7 @@ proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
of hintMin..hintMax: PosHintFormat of hintMin..hintMax: PosHintFormat
else: PosErrorFormat else: PosErrorFormat
result = frmt % [toMsgFilename(info), coordToStr(info.line), result = frmt % [toMsgFilename(info), coordToStr(info.line),
coordToStr(info.col), getMessageStr(msg, arg)] coordToStr(info.col+1), getMessageStr(msg, arg)]
proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
eh: TErrorHandling) = eh: TErrorHandling) =
@ -818,9 +804,12 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
ignoreMsg = optHints notin gOptions or msg notin gNotes ignoreMsg = optHints notin gOptions or msg notin gNotes
frmt = PosHintFormat frmt = PosHintFormat
inc(gHintCounter) 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), let s = frmt % [toMsgFilename(info), coordToStr(info.line),
coordToStr(info.col), getMessageStr(msg, arg)] coordToStr(info.col+1), getMessageStr(msg, arg)]
if not ignoreMsg: if not ignoreMsg and not ignoreMsgBecauseOfIdeTools(msg):
msgWriteln(s) msgWriteln(s)
if optPrintSurroundingSrc and msg in errMin..errMax: if optPrintSurroundingSrc and msg in errMin..errMax:
info.writeSurroundingSrc info.writeSurroundingSrc
@ -841,6 +830,9 @@ proc localError*(info: TLineInfo, msg: TMsgKind, arg = "") =
proc localError*(info: TLineInfo, arg: string) = proc localError*(info: TLineInfo, arg: string) =
liMessage(info, errGenerated, arg, doNothing) liMessage(info, errGenerated, arg, doNothing)
proc localError*(info: TLineInfo, format: string, params: openarray[string]) =
localError(info, format % params)
proc message*(info: TLineInfo, msg: TMsgKind, arg = "") = proc message*(info: TLineInfo, msg: TMsgKind, arg = "") =
liMessage(info, msg, arg, doNothing) liMessage(info, msg, arg, doNothing)
@ -862,9 +854,9 @@ template internalAssert*(e: bool): stmt =
if not e: internalError($instantiationInfo()) if not e: internalError($instantiationInfo())
proc addSourceLine*(fileIdx: int32, line: string) = proc addSourceLine*(fileIdx: int32, line: string) =
fileInfos[fileIdx].lines.add line.toRope fileInfos[fileIdx].lines.add line.rope
proc sourceLine*(i: TLineInfo): PRope = proc sourceLine*(i: TLineInfo): Rope =
if i.fileIndex < 0: return nil if i.fileIndex < 0: return nil
if not optPreserveOrigSource and fileInfos[i.fileIndex].lines.len == 0: if not optPreserveOrigSource and fileInfos[i.fileIndex].lines.len == 0:
@ -879,16 +871,14 @@ proc sourceLine*(i: TLineInfo): PRope =
result = fileInfos[i.fileIndex].lines[i.line-1] result = fileInfos[i.fileIndex].lines[i.line-1]
proc quotedFilename*(i: TLineInfo): PRope = proc quotedFilename*(i: TLineInfo): Rope =
internalAssert i.fileIndex >= 0 internalAssert i.fileIndex >= 0
result = fileInfos[i.fileIndex].quotedName result = fileInfos[i.fileIndex].quotedName
ropes.errorHandler = proc (err: TRopesError, msg: string, useWarning: bool) = ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) =
case err case err
of rInvalidFormatStr: of rInvalidFormatStr:
internalError("ropes: invalid format string: " & msg) internalError("ropes: invalid format string: " & msg)
of rTokenTooLong:
internalError("ropes: token too long: " & msg)
of rCannotOpenFile: of rCannotOpenFile:
rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg) rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg)

View file

@ -9,13 +9,14 @@
when defined(gcc) and defined(windows): when defined(gcc) and defined(windows):
when defined(x86): when defined(x86):
{.link: "icons/nimrod.res".} {.link: "icons/nim.res".}
else: else:
{.link: "icons/nimrod_icon.o".} {.link: "icons/nim_icon.o".}
import import
commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes, commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes,
extccomp, strutils, os, osproc, platform, main, parseopt, service extccomp, strutils, os, osproc, platform, main, parseopt, service,
nodejs
when hasTinyCBackend: when hasTinyCBackend:
import tccgen import tccgen
@ -60,6 +61,8 @@ proc handleCmdLine() =
if gCmd == cmdRun: if gCmd == cmdRun:
tccgen.run(commands.arguments) tccgen.run(commands.arguments)
if optRun in gGlobalOptions: if optRun in gGlobalOptions:
if gProjectName == "-":
gProjectFull = "stdinfile"
if gCmd == cmdCompileToJS: if gCmd == cmdCompileToJS:
var ex: string var ex: string
if options.outFile.len > 0: if options.outFile.len > 0:
@ -67,7 +70,7 @@ proc handleCmdLine() =
else: else:
ex = quoteShell( ex = quoteShell(
completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir)) completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir))
execExternalProgram("node " & ex & ' ' & commands.arguments) execExternalProgram(findNodeJs() & " " & ex & ' ' & commands.arguments)
else: else:
var binPath: string var binPath: string
if options.outFile.len > 0: if options.outFile.len > 0:
@ -89,4 +92,4 @@ condsyms.initDefines()
when not defined(selftest): when not defined(selftest):
handleCmdLine() handleCmdLine()
quit(int8(msgs.gErrorCounter > 0)) msgQuit(int8(msgs.gErrorCounter > 0))

View file

@ -1,7 +1,5 @@
# Special configuration file for the Nim project # Special configuration file for the Nim project
# gc:markAndSweep
hint[XDeclaredButNotUsed]:off hint[XDeclaredButNotUsed]:off
path:"llvm" path:"llvm"
path:"$projectPath/.." path:"$projectPath/.."
@ -20,3 +18,4 @@ define:useStdoutAsStdmsg
cs:partial cs:partial
#define:useNodeIds #define:useNodeIds
symbol:nimfix symbol:nimfix
#gc:markAndSweep

View file

@ -33,7 +33,7 @@ proc `<.`(a, b: string): bool =
while true: while true:
let ii = parseInt(a, verA, i) let ii = parseInt(a, verA, i)
let jj = parseInt(b, verB, j) let jj = parseInt(b, verB, j)
# if A has no number left, but B has, B is prefered: 0.8 vs 0.8.3 # if A has no number left, but B has, B is preferred: 0.8 vs 0.8.3
if ii <= 0 or jj <= 0: return jj > 0 if ii <= 0 or jj <= 0: return jj > 0
if verA < verB: return true if verA < verB: return true
elif verA > verB: return false elif verA > verB: return false

View file

@ -11,10 +11,10 @@
import import
llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer, llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer,
options, idents, wordrecg options, idents, wordrecg, strtabs
# ---------------- configuration file parser ----------------------------- # ---------------- configuration file parser -----------------------------
# we use Nim's scanner here to safe space and work # 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 # simple filter
@ -82,17 +82,17 @@ proc doElif(L: var TLexer, tok: var TToken) =
proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) = proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) =
var nestedIfs = 0 var nestedIfs = 0
while true: while true:
if (tok.ident != nil) and (tok.ident.s == "@"): if tok.ident != nil and tok.ident.s == "@":
ppGetTok(L, tok) ppGetTok(L, tok)
case whichKeyword(tok.ident) case whichKeyword(tok.ident)
of wIf: of wIf:
inc(nestedIfs) inc(nestedIfs)
of wElse: of wElse:
if (dest == jdElseEndif) and (nestedIfs == 0): if dest == jdElseEndif and nestedIfs == 0:
doElse(L, tok) doElse(L, tok)
break break
of wElif: of wElif:
if (dest == jdElseEndif) and (nestedIfs == 0): if dest == jdElseEndif and nestedIfs == 0:
doElif(L, tok) doElif(L, tok)
break break
of wEnd: of wEnd:
@ -121,7 +121,8 @@ proc parseDirective(L: var TLexer, tok: var TToken) =
of wEnd: doEnd(L, tok) of wEnd: doEnd(L, tok)
of wWrite: of wWrite:
ppGetTok(L, tok) ppGetTok(L, tok)
msgs.msgWriteln(tokToStr(tok)) msgs.msgWriteln(strtabs.`%`(tokToStr(tok), options.gConfigVars,
{useEnvironment, useKey}))
ppGetTok(L, tok) ppGetTok(L, tok)
else: else:
case tok.ident.s.normalize case tok.ident.s.normalize
@ -157,7 +158,7 @@ proc checkSymbol(L: TLexer, tok: TToken) =
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: if tok.ident.id == getIdent("-").id or tok.ident.id == getIdent("--").id:
confTok(L, tok) # skip unnecessary prefix confTok(L, tok) # skip unnecessary prefix
var info = getLineInfo(L, tok) # safe for later in case of an error var info = getLineInfo(L, tok) # save for later in case of an error
checkSymbol(L, tok) checkSymbol(L, tok)
var s = tokToStr(tok) var s = tokToStr(tok)
confTok(L, tok) # skip symbol confTok(L, tok) # skip symbol
@ -178,9 +179,10 @@ proc parseAssignment(L: var TLexer, tok: var TToken) =
if tok.tokType == tkBracketRi: confTok(L, tok) if tok.tokType == tkBracketRi: confTok(L, tok)
else: lexMessage(L, errTokenExpected, "']'") else: lexMessage(L, errTokenExpected, "']'")
add(val, ']') add(val, ']')
if tok.tokType in {tkColon, tkEquals}: let percent = tok.ident.id == getIdent("%=").id
if tok.tokType in {tkColon, tkEquals} or percent:
if len(val) > 0: add(val, ':') if len(val) > 0: add(val, ':')
confTok(L, tok) # skip ':' or '=' confTok(L, tok) # skip ':' or '=' or '%'
checkSymbol(L, tok) checkSymbol(L, tok)
add(val, tokToStr(tok)) add(val, tokToStr(tok))
confTok(L, tok) # skip symbol confTok(L, tok) # skip symbol
@ -189,7 +191,11 @@ proc parseAssignment(L: var TLexer, tok: var TToken) =
checkSymbol(L, tok) checkSymbol(L, tok)
add(val, tokToStr(tok)) add(val, tokToStr(tok))
confTok(L, tok) confTok(L, tok)
processSwitch(s, val, passPP, info) if percent:
processSwitch(s, strtabs.`%`(val, options.gConfigVars,
{useEnvironment, useEmpty}), passPP, info)
else:
processSwitch(s, val, passPP, info)
proc readConfigFile(filename: string) = proc readConfigFile(filename: string) =
var var
@ -246,6 +252,11 @@ proc loadConfigs*(cfg: string) =
if gProjectName.len != 0: if gProjectName.len != 0:
# new project wide config file: # new project wide config file:
let projectConfig = changeFileExt(gProjectFull, "nim.cfg") var projectConfig = changeFileExt(gProjectFull, "nimcfg")
if fileExists(projectConfig): readConfigFile(projectConfig) if not fileExists(projectConfig):
else: readConfigFile(changeFileExt(gProjectFull, "nimrod.cfg")) projectConfig = changeFileExt(gProjectFull, "nim.cfg")
if not fileExists(projectConfig):
projectConfig = changeFileExt(gProjectFull, "nimrod.cfg")
if fileExists(projectConfig):
rawMessage(warnDeprecated, projectConfig)
readConfigFile(projectConfig)

View file

@ -24,7 +24,7 @@ proc execute*(program: string) =
when hasFFI: defineSymbol("nimffi") when hasFFI: defineSymbol("nimffi")
registerPass(verbosePass) registerPass(verbosePass)
registerPass(semPass) registerPass(semPass)
registerPass(vmPass) registerPass(evalPass)
appendStr(searchPaths, options.libpath) appendStr(searchPaths, options.libpath)
compileSystemModule() compileSystemModule()

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.

View file

@ -2,10 +2,10 @@
# gc:markAndSweep # gc:markAndSweep
hint[XDeclaredButNotUsed]:off hint[XDeclaredButNotUsed]:off
path:"$projectPath/../.." path:"$projectPath/.."
path:"$lib/packages/docutils" path:"$lib/packages/docutils"
path:"$nim/compiler" path:"../../compiler"
define:useStdoutAsStdmsg define:useStdoutAsStdmsg
symbol:nimfix symbol:nimfix

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.

View file

@ -166,4 +166,4 @@ proc getCurrentLine(L: TBaseLexer, marker: bool = true): string =
inc(i) inc(i)
result.add("\n") result.add("\n")
if marker: if marker:
result.add(repeatChar(getColNumber(L, L.bufpos)) & '^' & "\n") result.add(spaces(getColNumber(L, L.bufpos)) & '^' & "\n")

View file

@ -0,0 +1,198 @@
#
#
# The Nim Compiler
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Nimsuggest is a tool that helps to give editors IDE like capabilities.
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()

View file

@ -0,0 +1,17 @@
# 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

6
compiler/nodejs.nim Normal file
View file

@ -0,0 +1,6 @@
import os
proc findNodeJs*(): string =
result = findExe("nodejs")
if result == "":
result = findExe("node")

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.

View file

@ -56,17 +56,14 @@ type # please make sure we have under 32 options
optNoMain, # do not generate a "main" proc optNoMain, # do not generate a "main" proc
optThreads, # support for multi-threading optThreads, # support for multi-threading
optStdout, # output to stdout optStdout, # output to stdout
optSuggest, # ideTools: 'suggest'
optContext, # ideTools: 'context'
optDef, # ideTools: 'def'
optUsages, # ideTools: 'usages'
optThreadAnalysis, # thread analysis pass optThreadAnalysis, # thread analysis pass
optTaintMode, # taint mode turned on optTaintMode, # taint mode turned on
optTlsEmulation, # thread var emulation turned on optTlsEmulation, # thread var emulation turned on
optGenIndex # generate index file for documentation; optGenIndex # generate index file for documentation;
optEmbedOrigSrc # embed the original source in the generated code optEmbedOrigSrc # embed the original source in the generated code
# also: generate header file # also: generate header file
optIdeDebug # idetools: debug mode
optIdeTerse # idetools: use terse descriptions
TGlobalOptions* = set[TGlobalOption] TGlobalOptions* = set[TGlobalOption]
TCommands* = enum # Nim's commands TCommands* = enum # Nim's commands
# **keep binary compatible** # **keep binary compatible**
@ -86,6 +83,12 @@ type # please make sure we have under 32 options
TGCMode* = enum # the selected GC TGCMode* = enum # the selected GC
gcNone, gcBoehm, gcMarkAndSweep, gcRefc, gcV2, gcGenerational gcNone, gcBoehm, gcMarkAndSweep, gcRefc, gcV2, gcGenerational
TIdeCmd* = enum
ideNone, ideSug, ideCon, ideDef, ideUse
var
gIdeCmd*: TIdeCmd
const const
ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck, ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck,
optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck} optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck}
@ -111,18 +114,12 @@ var
gLastCmdTime*: float # when caas is enabled, we measure each command gLastCmdTime*: float # when caas is enabled, we measure each command
gListFullPaths*: bool gListFullPaths*: bool
isServing*: bool = false isServing*: bool = false
gDirtyBufferIdx* = 0'i32 # indicates the fileIdx of the dirty version of
# the tracked source X, saved by the CAAS client.
gDirtyOriginalIdx* = 0'i32 # the original source file of the dirtified buffer.
gNoNimblePath* = false gNoNimblePath* = false
gExperimentalMode*: bool gExperimentalMode*: bool
proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools} proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools}
proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc
template isWorkingWithDirtyBuffer*: expr =
gDirtyBufferIdx != 0
template compilationCachePresent*: expr = template compilationCachePresent*: expr =
{optCaasEnabled, optSymbolFiles} * gGlobalOptions != {} {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {}
@ -286,7 +283,7 @@ when noTimeMachine:
var p = startProcess("/usr/bin/tmutil", args = ["addexclusion", dir]) var p = startProcess("/usr/bin/tmutil", args = ["addexclusion", dir])
discard p.waitForExit discard p.waitForExit
p.close p.close
except E_Base, EOS: except Exception:
discard discard
proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string = proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string =

View file

@ -131,11 +131,10 @@ proc semNodeKindConstraints*(p: PNode): PNode =
result.strVal.add(ppEof) result.strVal.add(ppEof)
type type
TSideEffectAnalysis = enum TSideEffectAnalysis* = enum
seUnknown, seSideEffect, seNoSideEffect seUnknown, seSideEffect, seNoSideEffect
proc checkForSideEffects(n: PNode): TSideEffectAnalysis = proc checkForSideEffects*(n: PNode): TSideEffectAnalysis =
# XXX is 'raise' a side effect?
case n.kind case n.kind
of nkCallKinds: of nkCallKinds:
# only calls can produce side effects: # only calls can produce side effects:
@ -162,6 +161,8 @@ proc checkForSideEffects(n: PNode): TSideEffectAnalysis =
# an atom cannot produce a side effect: # an atom cannot produce a side effect:
result = seNoSideEffect result = seNoSideEffect
else: else:
# assume no side effect:
result = seNoSideEffect
for i in 0 .. <n.len: for i in 0 .. <n.len:
let ret = checkForSideEffects(n.sons[i]) let ret = checkForSideEffects(n.sons[i])
if ret == seSideEffect: return ret if ret == seSideEffect: return ret
@ -174,7 +175,8 @@ type
arLValue, # is an l-value arLValue, # is an l-value
arLocalLValue, # is an l-value, but local var; must not escape arLocalLValue, # is an l-value, but local var; must not escape
# its stack frame! # its stack frame!
arDiscriminant # is a discriminant 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): TAssignableResult =
## 'owner' can be nil! ## 'owner' can be nil!
@ -188,6 +190,11 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
result = arLocalLValue result = arLocalLValue
else: else:
result = arLValue result = arLValue
elif n.sym.kind == skParam and n.sym.typ.kind == tyVar:
result = arLValue
elif n.sym.kind == skType:
let t = n.sym.typ.skipTypes({tyTypeDesc})
if t.kind == tyVar: result = arStrange
of nkDotExpr: of nkDotExpr:
if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in
{tyVar, tyPtr, tyRef}: {tyVar, tyPtr, tyRef}:
@ -211,7 +218,7 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct): elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct):
# types that are equal modulo distinction preserve l-value: # types that are equal modulo distinction preserve l-value:
result = isAssignable(owner, n.sons[1]) result = isAssignable(owner, n.sons[1])
of nkHiddenDeref, nkDerefExpr: of nkHiddenDeref, nkDerefExpr, nkHiddenAddr:
result = arLValue result = arLValue
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
result = isAssignable(owner, n.sons[0]) result = isAssignable(owner, n.sons[0])
@ -221,6 +228,9 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
else: else:
discard discard
proc isLValue*(n: PNode): bool =
isAssignable(nil, n) in {arLValue, arLocalLValue, arStrange}
proc matchNodeKinds*(p, n: PNode): bool = proc matchNodeKinds*(p, n: PNode): bool =
# matches the parameter constraint 'p' against the concrete AST 'n'. # matches the parameter constraint 'p' against the concrete AST 'n'.
# Efficiency matters here. # Efficiency matters here.

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -91,7 +91,7 @@ proc closeParser(p: var TParser) =
proc parMessage(p: TParser, msg: TMsgKind, arg = "") = proc parMessage(p: TParser, msg: TMsgKind, arg = "") =
## Produce and emit the parser message `arg` to output. ## Produce and emit the parser message `arg` to output.
lexMessage(p.lex, msg, arg) lexMessageTok(p.lex, msg, p.tok, arg)
proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) = proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) =
## Produce and emit a parser message to output about the token `tok` ## Produce and emit a parser message to output about the token `tok`
@ -111,7 +111,12 @@ proc rawSkipComment(p: var TParser, node: PNode) =
if p.tok.tokType == tkComment: if p.tok.tokType == tkComment:
if node != nil: if node != nil:
if node.comment == nil: node.comment = "" if node.comment == nil: node.comment = ""
add(node.comment, p.tok.literal) if p.tok.literal == "[]":
node.flags.incl nfIsCursor
#echo "parser: "
#debug node
else:
add(node.comment, p.tok.literal)
else: else:
parMessage(p, errInternal, "skipComment") parMessage(p, errInternal, "skipComment")
getTok(p) getTok(p)
@ -149,7 +154,7 @@ proc eat(p: var TParser, tokType: TTokType) =
if p.tok.tokType == tokType: if p.tok.tokType == tokType:
getTok(p) getTok(p)
else: else:
lexMessage(p.lex, errTokenExpected, TokTypeToStr[tokType]) lexMessageTok(p.lex, errTokenExpected, p.tok, TokTypeToStr[tokType])
proc parLineInfo(p: TParser): TLineInfo = proc parLineInfo(p: TParser): TLineInfo =
## Retrieve the line information associated with the parser's current state. ## Retrieve the line information associated with the parser's current state.
@ -193,8 +198,8 @@ proc isSigilLike(tok: TToken): bool {.inline.} =
proc isRightAssociative(tok: TToken): bool {.inline.} = proc isRightAssociative(tok: TToken): bool {.inline.} =
## Determines whether the token is right assocative. ## Determines whether the token is right assocative.
result = tok.tokType == tkOpr and (tok.ident.s[0] == '^' or result = tok.tokType == tkOpr and tok.ident.s[0] == '^'
(let L = tok.ident.s.len; L > 1 and tok.ident.s[L-1] == '>')) # or (let L = tok.ident.s.len; L > 1 and tok.ident.s[L-1] == '>'))
proc getPrecedence(tok: TToken, strongSpaces: bool): int = proc getPrecedence(tok: TToken, strongSpaces: bool): int =
## Calculates the precedence of the given token. ## Calculates the precedence of the given token.
@ -207,27 +212,29 @@ proc getPrecedence(tok: TToken, strongSpaces: bool): int =
let relevantChar = tok.ident.s[0] let relevantChar = tok.ident.s[0]
# arrow like? # arrow like?
if L > 1 and tok.ident.s[L-1] == '>': return considerStrongSpaces(1) if L > 1 and tok.ident.s[L-1] == '>' and
tok.ident.s[L-2] in {'-', '~', '='}: return considerStrongSpaces(1)
template considerAsgn(value: expr) = template considerAsgn(value: expr) =
result = if tok.ident.s[L-1] == '=': 1 else: considerStrongSpaces(value) result = if tok.ident.s[L-1] == '=': 1 else: value
case relevantChar case relevantChar
of '$', '^': considerAsgn(10) of '$', '^': considerAsgn(10)
of '*', '%', '/', '\\': considerAsgn(9) of '*', '%', '/', '\\': considerAsgn(9)
of '~': result = considerStrongSpaces(8) of '~': result = 8
of '+', '-', '|': considerAsgn(8) of '+', '-', '|': considerAsgn(8)
of '&': considerAsgn(7) of '&': considerAsgn(7)
of '=', '<', '>', '!': result = considerStrongSpaces(5) of '=', '<', '>', '!': result = 5
of '.': considerAsgn(6) of '.': considerAsgn(6)
of '?': result = considerStrongSpaces(2) of '?': result = 2
else: considerAsgn(2) else: considerAsgn(2)
of tkDiv, tkMod, tkShl, tkShr: result = 9 of tkDiv, tkMod, tkShl, tkShr: result = 9
of tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs: result = 5 of tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs: result = 5
of tkDotDot: result = considerStrongSpaces(6) of tkDotDot: result = 6
of tkAnd: result = 4 of tkAnd: result = 4
of tkOr, tkXor, tkPtr, tkRef: result = 3 of tkOr, tkXor, tkPtr, tkRef: result = 3
else: result = -10 else: return -10
result = considerStrongSpaces(result)
proc isOperator(tok: TToken): bool = proc isOperator(tok: TToken): bool =
## Determines if the given token is an operator type token. ## Determines if the given token is an operator type token.
@ -236,9 +243,15 @@ proc isOperator(tok: TToken): bool =
proc isUnary(p: TParser): bool = proc isUnary(p: TParser): bool =
## Check if the current parser token is a unary operator ## Check if the current parser token is a unary operator
p.strongSpaces and p.tok.tokType in {tkOpr, tkDotDot} and if p.tok.tokType in {tkOpr, tkDotDot} and
p.tok.strongSpaceB == 0 and p.tok.strongSpaceB == 0 and
p.tok.strongSpaceA > 0 p.tok.strongSpaceA > 0:
# XXX change this after 0.10.4 is out
if p.strongSpaces:
result = true
else:
parMessage(p, warnDeprecated,
"will be parsed as unary operator; inconsistent spacing")
proc checkBinary(p: TParser) {.inline.} = proc checkBinary(p: TParser) {.inline.} =
## Check if the current parser token is a binary operator. ## Check if the current parser token is a binary operator.
@ -261,7 +274,7 @@ proc checkBinary(p: TParser) {.inline.} =
#| operator = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9 #| operator = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9
#| | 'or' | 'xor' | 'and' #| | 'or' | 'xor' | 'and'
#| | 'is' | 'isnot' | 'in' | 'notin' | 'of' #| | 'is' | 'isnot' | 'in' | 'notin' | 'of'
#| | 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'addr' | 'static' | '..' #| | 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'static' | '..'
#| #|
#| prefixOperator = operator #| prefixOperator = operator
#| #|
@ -286,9 +299,9 @@ proc colcom(p: var TParser, n: PNode) =
proc parseSymbol(p: var TParser, allowNil = false): PNode = proc parseSymbol(p: var TParser, allowNil = false): PNode =
#| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
#| | IDENT #| | IDENT | 'addr' | 'type'
case p.tok.tokType case p.tok.tokType
of tkSymbol: of tkSymbol, tkAddr, tkType:
result = newIdentNodeP(p.tok.ident, p) result = newIdentNodeP(p.tok.ident, p)
getTok(p) getTok(p)
of tkAccent: of tkAccent:
@ -319,7 +332,10 @@ proc parseSymbol(p: var TParser, allowNil = false): PNode =
getTok(p) getTok(p)
else: else:
parMessage(p, errIdentifierExpected, p.tok) parMessage(p, errIdentifierExpected, p.tok)
getTok(p) # BUGFIX: We must consume a token here to prevent endless loops! # BUGFIX: We must consume a token here to prevent endless loops!
# But: this really sucks for idetools and keywords, so we don't do it
# if it is a keyword:
if not isKeyword(p.tok.tokType): getTok(p)
result = ast.emptyNode result = ast.emptyNode
proc indexExpr(p: var TParser): PNode = proc indexExpr(p: var TParser): PNode =
@ -373,29 +389,18 @@ proc exprList(p: var TParser, endTok: TTokType, result: PNode) =
if p.tok.tokType != tkComma: break if p.tok.tokType != tkComma: break
getTok(p) getTok(p)
optInd(p, a) optInd(p, a)
eat(p, endTok)
proc dotExpr(p: var TParser, a: PNode): PNode = proc dotExpr(p: var TParser, a: PNode): PNode =
#| dotExpr = expr '.' optInd ('type' | 'addr' | symbol) #| dotExpr = expr '.' optInd symbol
var info = p.parLineInfo var info = p.parLineInfo
getTok(p) getTok(p)
optInd(p, a) result = newNodeI(nkDotExpr, info)
case p.tok.tokType optInd(p, result)
of tkType: addSon(result, a)
result = newNodeP(nkTypeOfExpr, p) addSon(result, parseSymbol(p))
getTok(p)
addSon(result, a)
of tkAddr:
result = newNodeP(nkAddr, p)
getTok(p)
addSon(result, a)
else:
result = newNodeI(nkDotExpr, info)
addSon(result, a)
addSon(result, parseSymbol(p))
proc qualifiedIdent(p: var TParser): PNode = proc qualifiedIdent(p: var TParser): PNode =
#| qualifiedIdent = symbol ('.' optInd ('type' | 'addr' | symbol))? #| qualifiedIdent = symbol ('.' optInd symbol)?
result = parseSymbol(p) result = parseSymbol(p)
if p.tok.tokType == tkDot: result = dotExpr(p, result) if p.tok.tokType == tkDot: result = dotExpr(p, result)
@ -562,7 +567,7 @@ proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode =
#| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')' #| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')'
#| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']' #| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']'
case p.tok.tokType case p.tok.tokType
of tkSymbol: of tkSymbol, tkType, tkAddr:
result = newIdentNodeP(p.tok.ident, p) result = newIdentNodeP(p.tok.ident, p)
getTok(p) getTok(p)
result = parseGStrLit(p, result) result = parseGStrLit(p, result)
@ -668,11 +673,11 @@ proc namedParams(p: var TParser, callee: PNode,
proc parseMacroColon(p: var TParser, x: PNode): PNode proc parseMacroColon(p: var TParser, x: PNode): PNode
proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
#| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks? #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
#| | doBlocks #| | doBlocks
#| | '.' optInd ('type' | 'addr' | symbol) generalizedLit? #| | '.' optInd symbol generalizedLit?
#| | '[' optInd indexExprList optPar ']' #| | '[' optInd indexExprList optPar ']'
#| | '{' optInd indexExprList optPar '}' #| | '{' optInd indexExprList optPar '}'
#| | &( '`'|IDENT|literal|'cast') expr # command syntax #| | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax
result = r result = r
while p.tok.indent < 0 or while p.tok.indent < 0 or
(p.tok.tokType == tkDot and p.tok.indent >= baseIndent): (p.tok.tokType == tkDot and p.tok.indent >= baseIndent):
@ -698,21 +703,22 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
of tkCurlyLe: of tkCurlyLe:
if p.strongSpaces and p.tok.strongSpaceA > 0: break if p.strongSpaces and p.tok.strongSpaceA > 0: break
result = namedParams(p, result, nkCurlyExpr, tkCurlyRi) result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast: of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType:
if p.inPragma == 0: if p.inPragma == 0:
# actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
# solution, but pragmas.nim can't handle that # solution, but pragmas.nim can't handle that
let a = result let a = result
result = newNodeP(nkCommand, p) result = newNodeP(nkCommand, p)
addSon(result, a) addSon(result, a)
addSon result, parseExpr(p) when true:
when false: addSon result, parseExpr(p)
else:
while p.tok.tokType != tkEof: while p.tok.tokType != tkEof:
let a = parseExpr(p) let x = parseExpr(p)
addSon(result, a) addSon(result, x)
if p.tok.tokType != tkComma: break if p.tok.tokType != tkComma: break
getTok(p) getTok(p)
optInd(p, a) optInd(p, x)
if p.tok.tokType == tkDo: if p.tok.tokType == tkDo:
parseDoBlocks(p, result) parseDoBlocks(p, result)
else: else:
@ -737,7 +743,7 @@ proc parseOperators(p: var TParser, headNode: PNode,
var a = newNodeP(nkInfix, p) var a = newNodeP(nkInfix, p)
var opNode = newIdentNodeP(p.tok.ident, p) # skip operator: var opNode = newIdentNodeP(p.tok.ident, p) # skip operator:
getTok(p) getTok(p)
optInd(p, opNode) optInd(p, a)
# read sub-expression with higher priority: # read sub-expression with higher priority:
var b = simpleExprAux(p, opPrec + leftAssoc, modeB) var b = simpleExprAux(p, opPrec + leftAssoc, modeB)
addSon(a, opNode) addSon(a, opNode)
@ -857,6 +863,7 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode =
#| [' optInd (identColonEquals (comma/semicolon)?)* optPar ']' #| [' optInd (identColonEquals (comma/semicolon)?)* optPar ']'
#| extTupleDecl = 'tuple' #| extTupleDecl = 'tuple'
#| COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)? #| COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)?
#| tupleClass = 'tuple'
result = newNodeP(nkTupleTy, p) result = newNodeP(nkTupleTy, p)
getTok(p) getTok(p)
if p.tok.tokType == tkBracketLe: if p.tok.tokType == tkBracketLe:
@ -886,6 +893,8 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode =
parMessage(p, errIdentifierExpected, p.tok) parMessage(p, errIdentifierExpected, p.tok)
break break
if not sameInd(p): break if not sameInd(p): break
else:
result = newNodeP(nkTupleClassTy, p)
proc parseParamList(p: var TParser, retColon = true): PNode = proc parseParamList(p: var TParser, retColon = true): PNode =
#| paramList = '(' declColonEquals ^* (comma/semicolon) ')' #| paramList = '(' declColonEquals ^* (comma/semicolon) ')'
@ -935,8 +944,7 @@ proc parseDoBlock(p: var TParser): PNode =
getTok(p) getTok(p)
let params = parseParamList(p, retColon=false) let params = parseParamList(p, retColon=false)
let pragmas = optPragmas(p) let pragmas = optPragmas(p)
eat(p, tkColon) colcom(p, result)
skipComment(p, result)
result = newProcNode(nkDo, info, parseStmt(p), result = newProcNode(nkDo, info, parseStmt(p),
params = params, params = params,
pragmas = pragmas) pragmas = pragmas)
@ -1020,11 +1028,10 @@ proc parseObject(p: var TParser): PNode
proc parseTypeClass(p: var TParser): PNode proc parseTypeClass(p: var TParser): PNode
proc primary(p: var TParser, mode: TPrimaryMode): PNode = proc primary(p: var TParser, mode: TPrimaryMode): PNode =
#| typeKeyw = 'var' | 'ref' | 'ptr' | 'shared' | 'type' | 'tuple' #| typeKeyw = 'var' | 'ref' | 'ptr' | 'shared' | 'tuple'
#| | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum' #| | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum'
#| primary = typeKeyw typeDescK #| primary = typeKeyw typeDescK
#| / prefixOperator* identOrLiteral primarySuffix* #| / prefixOperator* identOrLiteral primarySuffix*
#| / 'addr' primary
#| / 'static' primary #| / 'static' primary
#| / 'bind' primary #| / 'bind' primary
if isOperator(p.tok): if isOperator(p.tok):
@ -1044,11 +1051,6 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
return return
case p.tok.tokType: case p.tok.tokType:
of tkVar: result = parseTypeDescKAux(p, nkVarTy, mode)
of tkRef: result = parseTypeDescKAux(p, nkRefTy, mode)
of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, mode)
of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, mode)
of tkType: result = parseTypeDescKAux(p, nkTypeOfExpr, mode)
of tkTuple: result = parseTuple(p, mode == pmTypeDef) of tkTuple: result = parseTuple(p, mode == pmTypeDef)
of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}) of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef})
of tkIterator: of tkIterator:
@ -1077,15 +1079,15 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
else: else:
result = newNodeP(nkObjectTy, p) result = newNodeP(nkObjectTy, p)
getTok(p) getTok(p)
of tkGeneric: of tkGeneric, tkConcept:
if mode == pmTypeDef: if mode == pmTypeDef:
let wasGeneric = p.tok.tokType == tkGeneric
result = parseTypeClass(p) result = parseTypeClass(p)
# hack so that it's remembered and can be marked as deprecated in
# sem'check:
if wasGeneric: result.flags.incl nfBase2
else: else:
parMessage(p, errInvalidToken, p.tok) parMessage(p, errInvalidToken, p.tok)
of tkAddr:
result = newNodeP(nkAddr, p)
getTokNoInd(p)
addSon(result, primary(p, pmNormal))
of tkStatic: of tkStatic:
let info = parLineInfo(p) let info = parLineInfo(p)
getTokNoInd(p) getTokNoInd(p)
@ -1099,6 +1101,10 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
getTok(p) getTok(p)
optInd(p, result) optInd(p, result)
addSon(result, primary(p, pmNormal)) addSon(result, primary(p, pmNormal))
of tkVar: result = parseTypeDescKAux(p, nkVarTy, mode)
of tkRef: result = parseTypeDescKAux(p, nkRefTy, mode)
of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, mode)
of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, mode)
else: else:
let baseInd = p.lex.currLineIndent let baseInd = p.lex.currLineIndent
result = identOrLiteral(p, mode) result = identOrLiteral(p, mode)
@ -1111,7 +1117,7 @@ proc parseTypeDesc(p: var TParser): PNode =
proc parseTypeDefAux(p: var TParser): PNode = proc parseTypeDefAux(p: var TParser): PNode =
#| typeDefAux = simpleExpr #| typeDefAux = simpleExpr
#| | 'generic' typeClass #| | 'concept' typeClass
result = simpleExpr(p, pmTypeDef) result = simpleExpr(p, pmTypeDef)
proc makeCall(n: PNode): PNode = proc makeCall(n: PNode): PNode =
@ -1132,9 +1138,11 @@ proc parseMacroColon(p: var TParser, x: PNode): PNode =
result = makeCall(result) result = makeCall(result)
getTok(p) getTok(p)
skipComment(p, result) skipComment(p, result)
let stmtList = newNodeP(nkStmtList, p)
if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}: if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}:
let body = parseStmt(p) let body = parseStmt(p)
addSon(result, makeStmtList(body)) stmtList.add body
#addSon(result, makeStmtList(body))
while sameInd(p): while sameInd(p):
var b: PNode var b: PNode
case p.tok.tokType case p.tok.tokType
@ -1146,19 +1154,22 @@ proc parseMacroColon(p: var TParser, x: PNode): PNode =
getTok(p) getTok(p)
optInd(p, b) optInd(p, b)
addSon(b, parseExpr(p)) addSon(b, parseExpr(p))
eat(p, tkColon)
of tkExcept: of tkExcept:
b = newNodeP(nkExceptBranch, p) b = newNodeP(nkExceptBranch, p)
exprList(p, tkColon, b) exprList(p, tkColon, b)
skipComment(p, b)
of tkElse: of tkElse:
b = newNodeP(nkElse, p) b = newNodeP(nkElse, p)
getTok(p) getTok(p)
eat(p, tkColon)
else: break else: break
eat(p, tkColon)
addSon(b, parseStmt(p)) addSon(b, parseStmt(p))
addSon(result, b) addSon(stmtList, b)
if b.kind == nkElse: break if b.kind == nkElse: break
if stmtList.len == 1 and stmtList[0].kind == nkStmtList:
# to keep backwards compatibility (see tests/vm/tstringnil)
result.add stmtList[0]
else:
result.add stmtList
proc parseExprStmt(p: var TParser): PNode = proc parseExprStmt(p: var TParser): PNode =
#| exprStmt = simpleExpr #| exprStmt = simpleExpr
@ -1167,9 +1178,7 @@ proc parseExprStmt(p: var TParser): PNode =
#| doBlocks #| doBlocks
#| / macroColon #| / macroColon
#| ))? #| ))?
inc p.inPragma
var a = simpleExpr(p) var a = simpleExpr(p)
dec p.inPragma
if p.tok.tokType == tkEquals: if p.tok.tokType == tkEquals:
getTok(p) getTok(p)
optInd(p, result) optInd(p, result)
@ -1178,8 +1187,20 @@ proc parseExprStmt(p: var TParser): PNode =
addSon(result, a) addSon(result, a)
addSon(result, b) addSon(result, b)
else: else:
if p.tok.indent < 0 and isExprStart(p): # simpleExpr parsed 'p a' from 'p a, b'?
result = newNode(nkCommand, a.info, @[a]) if p.tok.indent < 0 and p.tok.tokType == tkComma and a.kind == nkCommand:
result = a
while true:
getTok(p)
optInd(p, result)
var e = parseExpr(p)
addSon(result, e)
if p.tok.tokType != tkComma: break
elif p.tok.indent < 0 and isExprStart(p):
if a.kind == nkCommand:
result = a
else:
result = newNode(nkCommand, a.info, @[a])
while true: while true:
var e = parseExpr(p) var e = parseExpr(p)
addSon(result, e) addSon(result, e)
@ -1293,8 +1314,7 @@ proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
var branch = newNodeP(nkElifBranch, p) var branch = newNodeP(nkElifBranch, p)
optInd(p, branch) optInd(p, branch)
addSon(branch, parseExpr(p)) addSon(branch, parseExpr(p))
eat(p, tkColon) colcom(p, branch)
skipComment(p, branch)
addSon(branch, parseStmt(p)) addSon(branch, parseStmt(p))
skipComment(p, branch) skipComment(p, branch)
addSon(result, branch) addSon(result, branch)
@ -1302,8 +1322,7 @@ proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
if p.tok.tokType == tkElse and sameOrNoInd(p): if p.tok.tokType == tkElse and sameOrNoInd(p):
var branch = newNodeP(nkElse, p) var branch = newNodeP(nkElse, p)
eat(p, tkElse) eat(p, tkElse)
eat(p, tkColon) colcom(p, branch)
skipComment(p, branch)
addSon(branch, parseStmt(p)) addSon(branch, parseStmt(p))
addSon(result, branch) addSon(result, branch)
@ -1351,13 +1370,11 @@ proc parseCase(p: var TParser): PNode =
getTok(p) getTok(p)
optInd(p, b) optInd(p, b)
addSon(b, parseExpr(p)) addSon(b, parseExpr(p))
eat(p, tkColon)
of tkElse: of tkElse:
b = newNodeP(nkElse, p) b = newNodeP(nkElse, p)
getTok(p) getTok(p)
eat(p, tkColon)
else: break else: break
skipComment(p, b) colcom(p, b)
addSon(b, parseStmt(p)) addSon(b, parseStmt(p))
addSon(result, b) addSon(result, b)
if b.kind == nkElse: break if b.kind == nkElse: break
@ -1374,8 +1391,7 @@ proc parseTry(p: var TParser; isExpr: bool): PNode =
#| (optInd 'finally' colcom stmt)? #| (optInd 'finally' colcom stmt)?
result = newNodeP(nkTryStmt, p) result = newNodeP(nkTryStmt, p)
getTok(p) getTok(p)
eat(p, tkColon) colcom(p, result)
skipComment(p, result)
addSon(result, parseStmt(p)) addSon(result, parseStmt(p))
var b: PNode = nil var b: PNode = nil
while sameOrNoInd(p) or isExpr: while sameOrNoInd(p) or isExpr:
@ -1385,10 +1401,9 @@ proc parseTry(p: var TParser; isExpr: bool): PNode =
exprList(p, tkColon, b) exprList(p, tkColon, b)
of tkFinally: of tkFinally:
b = newNodeP(nkFinally, p) b = newNodeP(nkFinally, p)
getTokNoInd(p) getTok(p)
eat(p, tkColon)
else: break else: break
skipComment(p, b) colcom(p, b)
addSon(b, parseStmt(p)) addSon(b, parseStmt(p))
addSon(result, b) addSon(result, b)
if b.kind == nkFinally: break if b.kind == nkFinally: break
@ -1397,7 +1412,7 @@ proc parseTry(p: var TParser; isExpr: bool): PNode =
proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode = proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode =
#| exceptBlock = 'except' colcom stmt #| exceptBlock = 'except' colcom stmt
result = newNodeP(kind, p) result = newNodeP(kind, p)
getTokNoInd(p) getTok(p)
colcom(p, result) colcom(p, result)
addSon(result, parseStmt(p)) addSon(result, parseStmt(p))
@ -1430,7 +1445,7 @@ proc parseStaticOrDefer(p: var TParser; k: TNodeKind): PNode =
#| staticStmt = 'static' colcom stmt #| staticStmt = 'static' colcom stmt
#| deferStmt = 'defer' colcom stmt #| deferStmt = 'defer' colcom stmt
result = newNodeP(k, p) result = newNodeP(k, p)
getTokNoInd(p) getTok(p)
colcom(p, result) colcom(p, result)
addSon(result, parseStmt(p)) addSon(result, parseStmt(p))
@ -1589,6 +1604,7 @@ proc parseEnum(p: var TParser): PNode =
optInd(p, result) optInd(p, result)
while true: while true:
var a = parseSymbol(p) var a = parseSymbol(p)
if a.kind == nkEmpty: return
if p.tok.indent >= 0 and p.tok.indent <= p.currInd: if p.tok.indent >= 0 and p.tok.indent <= p.currInd:
add(result, a) add(result, a)
break break
@ -1610,7 +1626,7 @@ proc parseEnum(p: var TParser): PNode =
p.tok.tokType == tkEof: p.tok.tokType == tkEof:
break break
if result.len <= 1: if result.len <= 1:
lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok)) lexMessageTok(p.lex, errIdentifierExpected, p.tok, prettyTok(p.tok))
proc parseObjectPart(p: var TParser): PNode proc parseObjectPart(p: var TParser): PNode
proc parseObjectWhen(p: var TParser): PNode = proc parseObjectWhen(p: var TParser): PNode =
@ -1668,9 +1684,8 @@ proc parseObjectCase(p: var TParser): PNode =
of tkElse: of tkElse:
b = newNodeP(nkElse, p) b = newNodeP(nkElse, p)
getTok(p) getTok(p)
eat(p, tkColon)
else: break else: break
skipComment(p, b) colcom(p, b)
var fields = parseObjectPart(p) var fields = parseObjectPart(p)
if fields.kind == nkEmpty: if fields.kind == nkEmpty:
parMessage(p, errIdentifierExpected, p.tok) parMessage(p, errIdentifierExpected, p.tok)
@ -1683,14 +1698,14 @@ proc parseObjectCase(p: var TParser): PNode =
proc parseObjectPart(p: var TParser): PNode = proc parseObjectPart(p: var TParser): PNode =
#| objectPart = IND{>} objectPart^+IND{=} DED #| objectPart = IND{>} objectPart^+IND{=} DED
#| / objectWhen / objectCase / 'nil' / declColonEquals #| / objectWhen / objectCase / 'nil' / 'discard' / declColonEquals
if realInd(p): if realInd(p):
result = newNodeP(nkRecList, p) result = newNodeP(nkRecList, p)
withInd(p): withInd(p):
rawSkipComment(p, result) rawSkipComment(p, result)
while sameInd(p): while sameInd(p):
case p.tok.tokType case p.tok.tokType
of tkCase, tkWhen, tkSymbol, tkAccent, tkNil: of tkCase, tkWhen, tkSymbol, tkAccent, tkNil, tkDiscard:
addSon(result, parseObjectPart(p)) addSon(result, parseObjectPart(p))
else: else:
parMessage(p, errIdentifierExpected, p.tok) parMessage(p, errIdentifierExpected, p.tok)
@ -1704,7 +1719,7 @@ proc parseObjectPart(p: var TParser): PNode =
of tkSymbol, tkAccent: of tkSymbol, tkAccent:
result = parseIdentColonEquals(p, {withPragma}) result = parseIdentColonEquals(p, {withPragma})
skipComment(p, result) skipComment(p, result)
of tkNil: of tkNil, tkDiscard:
result = newNodeP(nkNilLit, p) result = newNodeP(nkNilLit, p)
getTok(p) getTok(p)
else: else:
@ -1735,8 +1750,8 @@ proc parseObject(p: var TParser): PNode =
proc parseTypeClassParam(p: var TParser): PNode = proc parseTypeClassParam(p: var TParser): PNode =
if p.tok.tokType == tkVar: if p.tok.tokType == tkVar:
result = newNodeP(nkVarTy, p)
getTok(p) getTok(p)
result = newNode(nkVarTy)
result.addSon(p.parseSymbol) result.addSon(p.parseSymbol)
else: else:
result = p.parseSymbol result = p.parseSymbol
@ -1747,7 +1762,7 @@ proc parseTypeClass(p: var TParser): PNode =
#| &IND{>} stmt #| &IND{>} stmt
result = newNodeP(nkTypeClassTy, p) result = newNodeP(nkTypeClassTy, p)
getTok(p) getTok(p)
var args = newNode(nkArgList) var args = newNodeP(nkArgList, p)
addSon(result, args) addSon(result, args)
addSon(args, p.parseTypeClassParam) addSon(args, p.parseTypeClassParam)
while p.tok.tokType == tkComma: while p.tok.tokType == tkComma:
@ -1866,7 +1881,7 @@ proc simpleStmt(p: var TParser): PNode =
proc complexOrSimpleStmt(p: var TParser): PNode = proc complexOrSimpleStmt(p: var TParser): PNode =
#| complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt #| complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt
#| | tryStmt | finallyStmt | exceptStmt | forStmt #| | tryStmt | forStmt
#| | blockStmt | staticStmt | deferStmt | asmStmt #| | blockStmt | staticStmt | deferStmt | asmStmt
#| | 'proc' routine #| | 'proc' routine
#| | 'method' routine #| | 'method' routine
@ -2013,8 +2028,8 @@ proc parseString*(s: string; filename: string = ""; line: int = 0;
var parser: TParser var parser: TParser
# XXX for now the builtin 'parseStmt/Expr' functions do not know about strong # XXX for now the builtin 'parseStmt/Expr' functions do not know about strong
# spaces... # spaces...
openParser(parser, filename, stream, false)
parser.lex.errorHandler = errorHandler parser.lex.errorHandler = errorHandler
openParser(parser, filename, stream, false)
result = parser.parseAll result = parser.parseAll
closeParser(parser) closeParser(parser)

View file

@ -169,8 +169,12 @@ proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) =
if rd == nil: if rd == nil:
openPasses(a, module) openPasses(a, module)
if stream == nil: if stream == nil:
let filename = fileIdx.toFullPath let filename = fileIdx.toFullPathConsiderDirty
s = llStreamOpen(filename, fmRead) if module.name.s == "-":
module.name.s = "stdinfile"
s = llStreamOpen(stdin)
else:
s = llStreamOpen(filename, fmRead)
if s == nil: if s == nil:
rawMessage(errCannotOpenFile, filename) rawMessage(errCannotOpenFile, filename)
return return

View file

@ -275,7 +275,7 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
if arg != rs and aliases.isPartOf(rs, arg) == arYes: if arg != rs and aliases.isPartOf(rs, arg) == arYes:
ok = true ok = true
break break
# constraint not fullfilled: # constraint not fulfilled:
if not ok: return nil if not ok: return nil
of aqNoAlias: of aqNoAlias:
# it MUST not alias with any other param: # it MUST not alias with any other param:
@ -284,7 +284,7 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
if arg != rs and aliases.isPartOf(rs, arg) != arNo: if arg != rs and aliases.isPartOf(rs, arg) != arNo:
ok = false ok = false
break break
# constraint not fullfilled: # constraint not fulfilled:
if not ok: return nil if not ok: return nil
markUsed(n.info, s) markUsed(n.info, s)

View file

@ -21,8 +21,8 @@ type
# conditionals to condsyms (end of module). # conditionals to condsyms (end of module).
osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris, osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris,
osIrix, osNetbsd, osFreebsd, osOpenbsd, osAix, osPalmos, osQnx, osAmiga, osIrix, osNetbsd, osFreebsd, osOpenbsd, osAix, osPalmos, osQnx, osAmiga,
osAtari, osNetware, osMacos, osMacosx, osHaiku, osJS, osNimrodVM, osAtari, osNetware, osMacos, osMacosx, osHaiku, osVxworks,
osStandalone osJS, osNimrodVM, osStandalone
type type
TInfoOSProp* = enum TInfoOSProp* = enum
@ -136,6 +136,10 @@ const
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}), props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}),
(name: "VxWorks", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ";", dirSep: "\\",
scriptExt: ".sh", curDir: ".", exeExt: ".vxe", extSep: ".",
props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}),
(name: "JS", parDir: "..", (name: "JS", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", objExt: ".o", newLine: "\x0A",

43
compiler/plugins.nim Normal file
View file

@ -0,0 +1,43 @@
#
#
# The Nim Compiler
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Plugin support for the Nim compiler. Right now there are no plugins and they
## need to be build with the compiler, no DLL support.
import ast, semdata, idents
type
Transformation* = proc (c: PContext; n: PNode): PNode {.nimcall.}
Plugin = ref object
fn, module, package: PIdent
t: Transformation
next: Plugin
proc pluginMatches(p: Plugin; s: PSym): bool =
if s.name.id != p.fn.id: return false
let module = s.owner
if module == nil or module.kind != skModule or
module.name.id != p.module.id: return false
let package = module.owner
if package == nil or package.kind != skPackage or
package.name.id != p.package.id: return false
return true
var head: Plugin
proc getPlugin*(fn: PSym): Transformation =
var it = head
while it != nil:
if pluginMatches(it, fn): return it.t
it = it.next
proc registerPlugin*(package, module, fn: string; t: Transformation) =
let oldHead = head
head = Plugin(fn: getIdent(fn), module: getIdent(module),
package: getIdent(package), t: t, next: oldHead)

View file

@ -0,0 +1,13 @@
#
#
# The Nim Compiler
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Include file that imports all plugins that are active.
import
locals.locals

View file

@ -0,0 +1,42 @@
#
#
# The Nim Compiler
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## The builtin 'system.locals' implemented as a plugin.
import plugins, ast, astalgo, magicsys, lookups, semdata, lowerings
proc semLocals(c: PContext, n: PNode): PNode =
var counter = 0
var tupleType = newTypeS(tyTuple, c)
result = newNodeIT(nkPar, n.info, tupleType)
tupleType.n = newNodeI(nkRecList, n.info)
# for now we skip openarrays ...
for scope in walkScopes(c.currentScope):
if scope == c.topLevelScope: break
for it in items(scope.symbols):
# XXX parameters' owners are wrong for generics; this caused some pain
# for closures too; we should finally fix it.
#if it.owner != c.p.owner: return result
if it.kind in skLocalVars and
it.typ.skipTypes({tyGenericInst, tyVar}).kind notin
{tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyExpr, tyStmt, tyEmpty}:
var field = newSym(skField, it.name, getCurrOwner(), n.info)
field.typ = it.typ.skipTypes({tyGenericInst, tyVar})
field.position = counter
inc(counter)
addSon(tupleType.n, newSymNode(field))
addSonSkipIntLit(tupleType, field.typ)
var a = newSymNode(it, result.info)
if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a)
result.add(a)
registerPlugin("stdlib", "system", "locals", semLocals)

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -25,7 +25,7 @@ const
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC, wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl, wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl,
wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe, wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe,
wOverride} wOverride, wConstructor}
converterPragmas* = procPragmas converterPragmas* = procPragmas
methodPragmas* = procPragmas methodPragmas* = procPragmas
templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty, templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty,
@ -60,7 +60,7 @@ const
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern, wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern,
wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal, wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal,
wGensym, wInject, wCodegenDecl, wGuard} wGensym, wInject, wCodegenDecl, wGuard, wGoto}
constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl, constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject} wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject}
letPragmas* = varPragmas letPragmas* = varPragmas
@ -89,7 +89,7 @@ proc pragmaAsm*(c: PContext, n: PNode): char =
invalidPragma(it) invalidPragma(it)
proc setExternName(s: PSym, extname: string) = proc setExternName(s: PSym, extname: string) =
s.loc.r = toRope(extname % s.name.s) s.loc.r = rope(extname % s.name.s)
if gCmd == cmdPretty and '$' notin extname: if gCmd == cmdPretty and '$' notin extname:
# note that '{.importc.}' is transformed into '{.importc: "$1".}' # note that '{.importc.}' is transformed into '{.importc: "$1".}'
s.loc.flags.incl(lfFullExternalName) s.loc.flags.incl(lfFullExternalName)
@ -105,7 +105,7 @@ proc validateExternCName(s: PSym, info: TLineInfo) =
## Valid identifiers are those alphanumeric including the underscore not ## Valid identifiers are those alphanumeric including the underscore not
## starting with a number. If the check fails, a generic error will be ## starting with a number. If the check fails, a generic error will be
## displayed to the user. ## displayed to the user.
let target = ropeToStr(s.loc.r) let target = $s.loc.r
if target.len < 1 or target[0] notin IdentStartChars or if target.len < 1 or target[0] notin IdentStartChars or
not target.allCharsInSet(IdentChars): not target.allCharsInSet(IdentChars):
localError(info, errGenerated, "invalid exported symbol") localError(info, errGenerated, "invalid exported symbol")
@ -411,12 +411,6 @@ proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
proc pragmaBreakpoint(c: PContext, n: PNode) = proc pragmaBreakpoint(c: PContext, n: PNode) =
discard getOptionalStr(c, n, "") discard getOptionalStr(c, n, "")
proc pragmaCheckpoint(c: PContext, n: PNode) =
# checkpoints can be used to debug the compiler; they are not documented
var info = n.info
inc(info.line) # next line is affected!
msgs.addCheckpoint(info)
proc pragmaWatchpoint(c: PContext, n: PNode) = proc pragmaWatchpoint(c: PContext, n: PNode) =
if n.kind == nkExprColonExpr: if n.kind == nkExprColonExpr:
n.sons[1] = c.semExpr(c, n.sons[1]) n.sons[1] = c.semExpr(c, n.sons[1])
@ -453,7 +447,9 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
addSon(result, newStrNode(nkStrLit, $marker)) addSon(result, newStrNode(nkStrLit, $marker))
if c < 0: break if c < 0: break
a = c + 1 a = c + 1
else: illFormedAst(n) else:
illFormedAstLocal(n)
result = newNode(nkAsmStmt, n.info)
proc pragmaEmit(c: PContext, n: PNode) = proc pragmaEmit(c: PContext, n: PNode) =
discard getStrLitNode(c, n) discard getStrLitNode(c, n)
@ -669,8 +665,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
incl(sym.flags, sfGlobal) incl(sym.flags, sfGlobal)
incl(sym.flags, sfPure) incl(sym.flags, sfPure)
of wMerge: of wMerge:
# only supported for backwards compat, doesn't do anything anymore
noVal(it) noVal(it)
incl(sym.flags, sfMerge) of wConstructor:
noVal(it)
incl(sym.flags, sfConstructor)
of wHeader: of wHeader:
var lib = getLib(c, libHeader, getStrLitNode(c, it)) var lib = getLib(c, libHeader, getStrLitNode(c, it))
addToLib(lib, sym) addToLib(lib, sym)
@ -678,7 +677,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
incl(sym.loc.flags, lfHeader) incl(sym.loc.flags, lfHeader)
incl(sym.loc.flags, lfNoDecl) incl(sym.loc.flags, lfNoDecl)
# implies nodecl, because otherwise header would not make sense # implies nodecl, because otherwise header would not make sense
if sym.loc.r == nil: sym.loc.r = toRope(sym.name.s) if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
of wDestructor: of wDestructor:
sym.flags.incl sfOverriden sym.flags.incl sfOverriden
if sym.name.s.normalize != "destroy": if sym.name.s.normalize != "destroy":
@ -739,11 +738,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
incl(sym.flags, sfProcvar) incl(sym.flags, sfProcvar)
if sym.typ != nil: incl(sym.typ.flags, tfThread) if sym.typ != nil: incl(sym.typ.flags, tfThread)
of wGcSafe: of wGcSafe:
if optThreadAnalysis in gGlobalOptions: noVal(it)
noVal(it) if sym.kind != skType: incl(sym.flags, sfThread)
if sym.kind != skType: incl(sym.flags, sfThread) if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)
if sym.typ != nil: incl(sym.typ.flags, tfGcSafe) else: invalidPragma(it)
else: invalidPragma(it)
of wPacked: of wPacked:
noVal(it) noVal(it)
if sym.typ == nil: invalidPragma(it) if sym.typ == nil: invalidPragma(it)
@ -845,6 +843,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
invalidPragma(it) invalidPragma(it)
else: else:
sym.guard = pragmaGuard(c, it, sym.kind) sym.guard = pragmaGuard(c, it, sym.kind)
of wGoto:
if sym == nil or sym.kind notin {skVar, skLet}:
invalidPragma(it)
else:
sym.flags.incl sfGoto
of wInjectStmt: of wInjectStmt:
if it.kind != nkExprColonExpr: if it.kind != nkExprColonExpr:
localError(it.info, errExprExpected) localError(it.info, errExprExpected)
@ -867,9 +870,11 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
while it != nil: while it != nil:
let o = it.otherPragmas let o = it.otherPragmas
if not o.isNil: if not o.isNil:
pushInfoContext(n.info)
for i in countup(0, sonsLen(o) - 1): for i in countup(0, sonsLen(o) - 1):
if singlePragma(c, sym, o, i, validPragmas): if singlePragma(c, sym, o, i, validPragmas):
internalError(n.info, "implicitPragmas") internalError(n.info, "implicitPragmas")
popInfoContext()
it = it.next.POptionEntry it = it.next.POptionEntry
if lfExportLib in sym.loc.flags and sfExportc notin sym.flags: if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
@ -879,7 +884,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
sfImportc in sym.flags and lib != nil: sfImportc in sym.flags and lib != nil:
incl(sym.loc.flags, lfDynamicLib) incl(sym.loc.flags, lfDynamicLib)
addToLib(lib, sym) addToLib(lib, sym)
if sym.loc.r == nil: sym.loc.r = toRope(sym.name.s) if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
proc hasPragma*(n: PNode, pragma: TSpecialWord): bool = proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
if n == nil or n.sons == nil: if n == nil or n.sons == nil:

View file

@ -41,17 +41,20 @@ proc renderModule*(n: PNode, filename: string, renderFlags: TRenderFlags = {})
proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string
proc initTokRender*(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {}) proc initTokRender*(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {})
proc getNextTok*(r: var TSrcGen, kind: var TTokType, literal: var string) proc getNextTok*(r: var TSrcGen, kind: var TTokType, literal: var string)
proc `$`*(n: PNode): string = n.renderTree
# implementation # implementation
# We render the source code in a two phases: The first # We render the source code in a two phases: The first
# determines how long the subtree will likely be, the second # determines how long the subtree will likely be, the second
# phase appends to a buffer that will be the output. # phase appends to a buffer that will be the output.
proc isKeyword*(s: string): bool = proc isKeyword*(i: PIdent): bool =
var i = getIdent(s)
if (i.id >= ord(tokKeywordLow) - ord(tkSymbol)) and if (i.id >= ord(tokKeywordLow) - ord(tkSymbol)) and
(i.id <= ord(tokKeywordHigh) - ord(tkSymbol)): (i.id <= ord(tokKeywordHigh) - ord(tkSymbol)):
result = true result = true
proc isKeyword*(s: string): bool = isKeyword(getIdent(s))
proc renderDefinitionName*(s: PSym, noQuotes = false): string = proc renderDefinitionName*(s: PSym, noQuotes = false): string =
## Returns the definition name of the symbol. ## Returns the definition name of the symbol.
## ##
@ -59,7 +62,7 @@ proc renderDefinitionName*(s: PSym, noQuotes = false): string =
## happen if the name happens to be a keyword or the first character is not ## happen if the name happens to be a keyword or the first character is not
## part of the SymStartChars set. ## part of the SymStartChars set.
let x = s.name.s let x = s.name.s
if noQuotes or (x[0] in SymStartChars and not renderer.isKeyword(x)): if noQuotes or (x[0] in SymStartChars and not renderer.isKeyword(s.name)):
result = x result = x
else: else:
result = '`' & x & '`' result = '`' & x & '`'
@ -91,7 +94,7 @@ proc addTok(g: var TSrcGen, kind: TTokType, s: string) =
proc addPendingNL(g: var TSrcGen) = proc addPendingNL(g: var TSrcGen) =
if g.pendingNL >= 0: if g.pendingNL >= 0:
addTok(g, tkSpaces, "\n" & repeatChar(g.pendingNL)) addTok(g, tkSpaces, "\n" & spaces(g.pendingNL))
g.lineLen = g.pendingNL g.lineLen = g.pendingNL
g.pendingNL = - 1 g.pendingNL = - 1
@ -189,7 +192,7 @@ proc putComment(g: var TSrcGen, s: string) =
if not isCode and (g.lineLen + (j - i) > MaxLineLen): if not isCode and (g.lineLen + (j - i) > MaxLineLen):
put(g, tkComment, com) put(g, tkComment, com)
optNL(g, ind) optNL(g, ind)
com = '#' & repeatChar(comIndent) com = '#' & spaces(comIndent)
while s[i] > ' ': while s[i] > ' ':
add(com, s[i]) add(com, s[i])
inc(i) inc(i)
@ -279,7 +282,7 @@ proc gcom(g: var TSrcGen, n: PNode) =
(g.lineLen < LineCommentColumn): (g.lineLen < LineCommentColumn):
var ml = maxLineLength(n.comment) var ml = maxLineLength(n.comment)
if ml + LineCommentColumn <= MaxLineLen: if ml + LineCommentColumn <= MaxLineLen:
put(g, tkSpaces, repeatChar(LineCommentColumn - g.lineLen)) put(g, tkSpaces, spaces(LineCommentColumn - g.lineLen))
putComment(g, n.comment) #assert(g.comStack[high(g.comStack)] = n); putComment(g, n.comment) #assert(g.comStack[high(g.comStack)] = n);
proc gcoms(g: var TSrcGen) = proc gcoms(g: var TSrcGen) =
@ -382,7 +385,7 @@ proc lsub(n: PNode): int =
result = lsub(n.sons[0]) + lcomma(n, 1) + 2 result = lsub(n.sons[0]) + lcomma(n, 1) + 2
of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv: result = lsub(n[1]) of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv: result = lsub(n[1])
of nkCast: result = lsub(n.sons[0]) + lsub(n.sons[1]) + len("cast[]()") of nkCast: result = lsub(n.sons[0]) + lsub(n.sons[1]) + len("cast[]()")
of nkAddr: result = lsub(n.sons[0]) + len("addr()") of nkAddr: result = (if n.len>0: lsub(n.sons[0]) + len("addr()") else: 4)
of nkStaticExpr: result = lsub(n.sons[0]) + len("static_") of nkStaticExpr: result = lsub(n.sons[0]) + len("static_")
of nkHiddenAddr, nkHiddenDeref: result = lsub(n.sons[0]) of nkHiddenAddr, nkHiddenDeref: result = lsub(n.sons[0])
of nkCommand: result = lsub(n.sons[0]) + lcomma(n, 1) + 1 of nkCommand: result = lsub(n.sons[0]) + lcomma(n, 1) + 1
@ -394,6 +397,7 @@ proc lsub(n: PNode): int =
of nkClosedSymChoice, nkOpenSymChoice: of nkClosedSymChoice, nkOpenSymChoice:
result = lsons(n) + len("()") + sonsLen(n) - 1 result = lsons(n) + len("()") + sonsLen(n) - 1
of nkTupleTy: result = lcomma(n) + len("tuple[]") of nkTupleTy: result = lcomma(n) + len("tuple[]")
of nkTupleClassTy: result = len("tuple")
of nkDotExpr: result = lsons(n) + 1 of nkDotExpr: result = lsons(n) + 1
of nkBind: result = lsons(n) + len("bind_") of nkBind: result = lsons(n) + len("bind_")
of nkBindStmt: result = lcomma(n) + len("bind_") of nkBindStmt: result = lcomma(n) + len("bind_")
@ -429,7 +433,7 @@ proc lsub(n: PNode): int =
len("if_:_") len("if_:_")
of nkElifExpr: result = lsons(n) + len("_elif_:_") of nkElifExpr: result = lsons(n) + len("_elif_:_")
of nkElseExpr: result = lsub(n.sons[0]) + len("_else:_") # type descriptions of nkElseExpr: result = lsub(n.sons[0]) + len("_else:_") # type descriptions
of nkTypeOfExpr: result = lsub(n.sons[0]) + len("type_") of nkTypeOfExpr: result = (if n.len > 0: lsub(n.sons[0]) else: 0)+len("type_")
of nkRefTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ref") of nkRefTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ref")
of nkPtrTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ptr") of nkPtrTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ptr")
of nkVarTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("var") of nkVarTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("var")
@ -499,6 +503,7 @@ proc gsub(g: var TSrcGen, n: PNode) =
proc hasCom(n: PNode): bool = proc hasCom(n: PNode): bool =
result = false result = false
if n.isNil: return false
if n.comment != nil: return true if n.comment != nil: return true
case n.kind case n.kind
of nkEmpty..nkNilLit: discard of nkEmpty..nkNilLit: discard
@ -721,7 +726,7 @@ proc gproc(g: var TSrcGen, n: PNode) =
proc gTypeClassTy(g: var TSrcGen, n: PNode) = proc gTypeClassTy(g: var TSrcGen, n: PNode) =
var c: TContext var c: TContext
initContext(c) initContext(c)
putWithSpace(g, tkGeneric, "generic") putWithSpace(g, tkConcept, "concept")
gsons(g, n[0], c) # arglist gsons(g, n[0], c) # arglist
gsub(g, n[1]) # pragmas gsub(g, n[1]) # pragmas
gsub(g, n[2]) # of gsub(g, n[2]) # of
@ -842,9 +847,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
put(g, tkParRi, ")") put(g, tkParRi, ")")
of nkAddr: of nkAddr:
put(g, tkAddr, "addr") put(g, tkAddr, "addr")
put(g, tkParLe, "(") if n.len > 0:
gsub(g, n.sons[0]) put(g, tkParLe, "(")
put(g, tkParRi, ")") gsub(g, n.sons[0])
put(g, tkParRi, ")")
of nkStaticExpr: of nkStaticExpr:
put(g, tkStatic, "static") put(g, tkStatic, "static")
put(g, tkSpaces, Space) put(g, tkSpaces, Space)
@ -1265,9 +1271,12 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
putWithSpace(g, tkColon, ":") putWithSpace(g, tkColon, ":")
gcoms(g) gcoms(g)
gstmts(g, n.sons[0], c) gstmts(g, n.sons[0], c)
of nkFinally: of nkFinally, nkDefer:
optNL(g) optNL(g)
put(g, tkFinally, "finally") if n.kind == nkFinally:
put(g, tkFinally, "finally")
else:
put(g, tkDefer, "defer")
putWithSpace(g, tkColon, ":") putWithSpace(g, tkColon, ":")
gcoms(g) gcoms(g)
gstmts(g, n.sons[0], c) gstmts(g, n.sons[0], c)
@ -1291,10 +1300,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
gsub(g, n.sons[0]) gsub(g, n.sons[0])
of nkTupleTy: of nkTupleTy:
put(g, tkTuple, "tuple") put(g, tkTuple, "tuple")
if sonsLen(n) > 0: put(g, tkBracketLe, "[")
put(g, tkBracketLe, "[") gcomma(g, n)
gcomma(g, n) put(g, tkBracketRi, "]")
put(g, tkBracketRi, "]") of nkTupleClassTy:
put(g, tkTuple, "tuple")
of nkMetaNode_Obsolete: of nkMetaNode_Obsolete:
put(g, tkParLe, "(META|") put(g, tkParLe, "(META|")
gsub(g, n.sons[0]) gsub(g, n.sons[0])

View file

@ -277,7 +277,7 @@ proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) =
loc.t = nil loc.t = nil
if r.s[r.pos] == '!': if r.s[r.pos] == '!':
inc(r.pos) inc(r.pos)
loc.r = toRope(decodeStr(r.s, r.pos)) loc.r = rope(decodeStr(r.s, r.pos))
else: else:
loc.r = nil loc.r = nil
if r.s[r.pos] == '>': inc(r.pos) if r.s[r.pos] == '>': inc(r.pos)
@ -344,7 +344,7 @@ proc decodeLib(r: PRodReader, info: TLineInfo): PLib =
result.kind = TLibKind(decodeVInt(r.s, r.pos)) result.kind = TLibKind(decodeVInt(r.s, r.pos))
if r.s[r.pos] != '|': internalError("decodeLib: 1") if r.s[r.pos] != '|': internalError("decodeLib: 1")
inc(r.pos) inc(r.pos)
result.name = toRope(decodeStr(r.s, r.pos)) result.name = rope(decodeStr(r.s, r.pos))
if r.s[r.pos] != '|': internalError("decodeLib: 2") if r.s[r.pos] != '|': internalError("decodeLib: 2")
inc(r.pos) inc(r.pos)
result.path = decodeNode(r, info) result.path = decodeNode(r, info)
@ -666,7 +666,7 @@ proc newRodReader(modfilename: string, crc: TCrc32,
r.readerIndex = readerIndex r.readerIndex = readerIndex
r.filename = modfilename r.filename = modfilename
initIdTable(r.syms) initIdTable(r.syms)
# we terminate the file explicitely with ``\0``, so the cast to `cstring` # we terminate the file explicitly with ``\0``, so the cast to `cstring`
# is safe: # is safe:
r.s = cast[cstring](r.memfile.mem) r.s = cast[cstring](r.memfile.mem)
if startsWith(r.s, "NIM:"): if startsWith(r.s, "NIM:"):

View file

@ -186,7 +186,7 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
pushType(w, loc.t) pushType(w, loc.t)
if loc.r != nil: if loc.r != nil:
add(result, '!') add(result, '!')
encodeStr(ropeToStr(loc.r), result) encodeStr($loc.r, result)
if oldLen + 1 == result.len: if oldLen + 1 == result.len:
# no data was necessary, so remove the '<' again: # no data was necessary, so remove the '<' again:
setLen(result, oldLen) setLen(result, oldLen)
@ -200,7 +200,7 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
return return
# we need no surrounding [] here because the type is in a line of its own # we need no surrounding [] here because the type is in a line of its own
if t.kind == tyForward: internalError("encodeType: tyForward") if t.kind == tyForward: internalError("encodeType: tyForward")
# for the new rodfile viewer we use a preceeding [ so that the data section # for the new rodfile viewer we use a preceding [ so that the data section
# can easily be disambiguated: # can easily be disambiguated:
add(result, '[') add(result, '[')
encodeVInt(ord(t.kind), result) encodeVInt(ord(t.kind), result)
@ -241,7 +241,7 @@ proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
add(result, '|') add(result, '|')
encodeVInt(ord(lib.kind), result) encodeVInt(ord(lib.kind), result)
add(result, '|') add(result, '|')
encodeStr(ropeToStr(lib.name), result) encodeStr($lib.name, result)
add(result, '|') add(result, '|')
encodeNode(w, info, lib.path, result) encodeNode(w, info, lib.path, result)

View file

@ -52,81 +52,63 @@
# Note that the left and right pointers are not needed for leaves. # Note that the left and right pointers are not needed for leaves.
# Leaves have relatively high memory overhead (~30 bytes on a 32 # Leaves have relatively high memory overhead (~30 bytes on a 32
# bit machines) and we produce many of them. This is why we cache and # bit machines) and we produce many of them. This is why we cache and
# share leaves accross different rope trees. # share leaves across different rope trees.
# To cache them they are inserted in a `cache` array. # To cache them they are inserted in a `cache` array.
import import
strutils, platform, hashes, crc, options platform, hashes
type type
TFormatStr* = string # later we may change it to CString for better FormatStr* = string # later we may change it to CString for better
# performance of the code generator (assignments # performance of the code generator (assignments
# copy the format strings # copy the format strings
# though it is not necessary) # though it is not necessary)
PRope* = ref TRope Rope* = ref RopeObj
TRope*{.acyclic.} = object of RootObj # the empty rope is represented RopeObj*{.acyclic.} = object of RootObj # the empty rope is represented
# by nil to safe space # by nil to safe space
left*, right*: PRope left*, right*: Rope
length*: int length*: int
data*: string # != nil if a leaf data*: string # != nil if a leaf
TRopeSeq* = seq[PRope] RopeSeq* = seq[Rope]
TRopesError* = enum RopesError* = enum
rCannotOpenFile rCannotOpenFile
rInvalidFormatStr rInvalidFormatStr
rTokenTooLong
proc con*(a, b: PRope): PRope
proc con*(a: PRope, b: string): PRope
proc con*(a: string, b: PRope): PRope
proc con*(a: varargs[PRope]): PRope
proc app*(a: var PRope, b: PRope)
proc app*(a: var PRope, b: string)
proc prepend*(a: var PRope, b: PRope)
proc toRope*(s: string): PRope
proc toRope*(i: BiggestInt): PRope
proc ropeLen*(a: PRope): int
proc writeRopeIfNotEqual*(r: PRope, filename: string): bool
proc ropeToStr*(p: PRope): string
proc ropef*(frmt: TFormatStr, args: varargs[PRope]): PRope
proc appf*(c: var PRope, frmt: TFormatStr, args: varargs[PRope])
proc ropeEqualsFile*(r: PRope, f: string): bool
# returns true if the rope r is the same as the contents of file f
proc ropeInvariant*(r: PRope): bool
# exported for debugging
# implementation # implementation
var errorHandler*: proc(err: TRopesError, msg: string, useWarning = false) var errorHandler*: proc(err: RopesError, msg: string, useWarning = false)
# avoid dependency on msgs.nim # avoid dependency on msgs.nim
proc ropeLen(a: PRope): int = proc len*(a: Rope): int =
## the rope's length
if a == nil: result = 0 if a == nil: result = 0
else: result = a.length else: result = a.length
proc newRope*(data: string = nil): PRope = proc newRope(data: string = nil): Rope =
new(result) new(result)
if data != nil: if data != nil:
result.length = len(data) result.length = len(data)
result.data = data result.data = data
proc newMutableRope*(capacity = 30): PRope = proc newMutableRope*(capacity = 30): Rope =
## creates a new rope that supports direct modifications of the rope's ## creates a new rope that supports direct modifications of the rope's
## 'data' and 'length' fields. ## 'data' and 'length' fields.
new(result) new(result)
result.data = newStringOfCap(capacity) result.data = newStringOfCap(capacity)
proc freezeMutableRope*(r: PRope) {.inline.} = proc freezeMutableRope*(r: Rope) {.inline.} =
r.length = r.data.len r.length = r.data.len
var var
cache: array[0..2048*2 -1, PRope] cache: array[0..2048*2 - 1, Rope]
proc resetRopeCache* = proc resetRopeCache* =
for i in low(cache)..high(cache): for i in low(cache)..high(cache):
cache[i] = nil cache[i] = nil
proc ropeInvariant(r: PRope): bool = proc ropeInvariant(r: Rope): bool =
if r == nil: if r == nil:
result = true result = true
else: else:
@ -143,7 +125,7 @@ var gCacheTries* = 0
var gCacheMisses* = 0 var gCacheMisses* = 0
var gCacheIntTries* = 0 var gCacheIntTries* = 0
proc insertInCache(s: string): PRope = proc insertInCache(s: string): Rope =
inc gCacheTries inc gCacheTries
var h = hash(s) and high(cache) var h = hash(s) and high(cache)
result = cache[h] result = cache[h]
@ -152,82 +134,77 @@ proc insertInCache(s: string): PRope =
result = newRope(s) result = newRope(s)
cache[h] = result cache[h] = result
proc toRope(s: string): PRope = proc rope*(s: string): Rope =
## Converts a string to a rope.
if s.len == 0: if s.len == 0:
result = nil result = nil
else: else:
result = insertInCache(s) result = insertInCache(s)
assert(ropeInvariant(result)) assert(ropeInvariant(result))
proc ropeSeqInsert(rs: var TRopeSeq, r: PRope, at: Natural) = proc rope*(i: BiggestInt): Rope =
var length = len(rs) ## Converts an int to a rope.
if at > length: inc gCacheIntTries
setLen(rs, at + 1) result = rope($i)
else:
setLen(rs, length + 1) # move old rope elements:
for i in countdown(length, at + 1):
rs[i] = rs[i - 1] # this is correct, I used pen and paper to validate it
rs[at] = r
proc newRecRopeToStr(result: var string, resultLen: var int, r: PRope) = proc rope*(f: BiggestFloat): Rope =
var stack = @[r] ## Converts a float to a rope.
while len(stack) > 0: result = rope($f)
var it = pop(stack)
while it.data == nil:
add(stack, it.right)
it = it.left
assert(it.data != nil)
copyMem(addr(result[resultLen]), addr(it.data[0]), it.length)
inc(resultLen, it.length)
assert(resultLen <= len(result))
proc ropeToStr(p: PRope): string = proc `&`*(a, b: Rope): Rope =
if p == nil: if a == nil:
result = "" result = b
else: elif b == nil:
result = newString(p.length) result = a
var resultLen = 0
newRecRopeToStr(result, resultLen, p)
proc con(a, b: PRope): PRope =
if a == nil: result = b
elif b == nil: result = a
else: else:
result = newRope() result = newRope()
result.length = a.length + b.length result.length = a.length + b.length
result.left = a result.left = a
result.right = b result.right = b
proc con(a: PRope, b: string): PRope = result = con(a, toRope(b)) proc `&`*(a: Rope, b: string): Rope =
proc con(a: string, b: PRope): PRope = result = con(toRope(a), b) ## the concatenation operator for ropes.
result = a & rope(b)
proc con(a: varargs[PRope]): PRope = proc `&`*(a: string, b: Rope): Rope =
for i in countup(0, high(a)): result = con(result, a[i]) ## the concatenation operator for ropes.
result = rope(a) & b
proc ropeConcat*(a: varargs[PRope]): PRope = proc `&`*(a: openArray[Rope]): Rope =
# not overloaded version of concat to speed-up `rfmt` a little bit ## the concatenation operator for an openarray of ropes.
for i in countup(0, high(a)): result = con(result, a[i]) for i in countup(0, high(a)): result = result & a[i]
proc toRope(i: BiggestInt): PRope = proc add*(a: var Rope, b: Rope) =
inc gCacheIntTries ## adds `b` to the rope `a`.
result = toRope($i) a = a & b
proc app(a: var PRope, b: PRope) = a = con(a, b) proc add*(a: var Rope, b: string) =
proc app(a: var PRope, b: string) = a = con(a, b) ## adds `b` to the rope `a`.
proc prepend(a: var PRope, b: PRope) = a = con(b, a) a = a & b
proc writeRope*(f: File, c: PRope) = iterator leaves*(r: Rope): string =
var stack = @[c] ## iterates over any leaf string in the rope `r`.
while len(stack) > 0: if r != nil:
var it = pop(stack) var stack = @[r]
while it.data == nil: while stack.len > 0:
add(stack, it.right) var it = stack.pop
it = it.left while isNil(it.data):
assert(it != nil) stack.add(it.right)
assert(it.data != nil) it = it.left
write(f, it.data) assert(it != nil)
assert(it.data != nil)
yield it.data
proc writeRope*(head: PRope, filename: string, useWarning = false) = iterator items*(r: Rope): char =
## iterates over any character in the rope `r`.
for s in leaves(r):
for c in items(s): yield c
proc writeRope*(f: File, r: Rope) =
## writes a rope to a file.
for s in leaves(r): write(f, s)
proc writeRope*(head: Rope, filename: string, useWarning = false) =
var f: File var f: File
if open(f, filename, fmWrite): if open(f, filename, fmWrite):
if head != nil: writeRope(f, head) if head != nil: writeRope(f, head)
@ -235,42 +212,69 @@ proc writeRope*(head: PRope, filename: string, useWarning = false) =
else: else:
errorHandler(rCannotOpenFile, filename, useWarning) errorHandler(rCannotOpenFile, filename, useWarning)
proc `$`*(r: Rope): string =
## converts a rope back to a string.
result = newString(r.len)
setLen(result, 0)
for s in leaves(r): add(result, s)
proc ropeConcat*(a: varargs[Rope]): Rope =
# not overloaded version of concat to speed-up `rfmt` a little bit
for i in countup(0, high(a)): result = result & a[i]
proc prepend*(a: var Rope, b: Rope) = a = b & a
proc prepend*(a: var Rope, b: string) = a = b & a
var var
rnl* = tnl.newRope rnl* = tnl.newRope
softRnl* = tnl.newRope softRnl* = tnl.newRope
proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope = proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
var i = 0 var i = 0
var length = len(frmt) var length = len(frmt)
result = nil result = nil
var num = 0 var num = 0
while i <= length - 1: while i < length:
if frmt[i] == '$': if frmt[i] == '$':
inc(i) # skip '$' inc(i) # skip '$'
case frmt[i] case frmt[i]
of '$': of '$':
app(result, "$") add(result, "$")
inc(i) inc(i)
of '#': of '#':
inc(i) inc(i)
app(result, args[num]) add(result, args[num])
inc(num) inc(num)
of '0'..'9': of '0'..'9':
var j = 0 var j = 0
while true: while true:
j = (j * 10) + ord(frmt[i]) - ord('0') j = j * 10 + ord(frmt[i]) - ord('0')
inc(i) inc(i)
if (i > length + 0 - 1) or not (frmt[i] in {'0'..'9'}): break if frmt[i] notin {'0'..'9'}: break
num = j num = j
if j > high(args) + 1: if j > high(args) + 1:
errorHandler(rInvalidFormatStr, $(j)) errorHandler(rInvalidFormatStr, $(j))
else: else:
app(result, args[j - 1]) add(result, args[j-1])
of '{':
inc(i)
var j = 0
while frmt[i] in {'0'..'9'}:
j = j * 10 + ord(frmt[i]) - ord('0')
inc(i)
num = j
if frmt[i] == '}': inc(i)
else: errorHandler(rInvalidFormatStr, $(frmt[i]))
if j > high(args) + 1:
errorHandler(rInvalidFormatStr, $(j))
else:
add(result, args[j-1])
of 'n': of 'n':
app(result, softRnl) add(result, softRnl)
inc i inc(i)
of 'N': of 'N':
app(result, rnl) add(result, rnl)
inc(i) inc(i)
else: else:
errorHandler(rInvalidFormatStr, $(frmt[i])) errorHandler(rInvalidFormatStr, $(frmt[i]))
@ -279,83 +283,67 @@ proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope =
if frmt[i] != '$': inc(i) if frmt[i] != '$': inc(i)
else: break else: break
if i - 1 >= start: if i - 1 >= start:
app(result, substr(frmt, start, i - 1)) add(result, substr(frmt, start, i - 1))
assert(ropeInvariant(result)) assert(ropeInvariant(result))
proc addf*(c: var Rope, frmt: FormatStr, args: openArray[Rope]) =
## shortcut for ``add(c, frmt % args)``.
add(c, frmt % args)
when true: when true:
template `~`*(r: string): PRope = r.ropef template `~`*(r: string): Rope = r % []
else: else:
{.push stack_trace: off, line_trace: off.} {.push stack_trace: off, line_trace: off.}
proc `~`*(r: static[string]): PRope = proc `~`*(r: static[string]): Rope =
# this is the new optimized "to rope" operator # this is the new optimized "to rope" operator
# the mnemonic is that `~` looks a bit like a rope :) # the mnemonic is that `~` looks a bit like a rope :)
var r {.global.} = r.ropef var r {.global.} = r % []
return r return r
{.pop.} {.pop.}
proc appf(c: var PRope, frmt: TFormatStr, args: varargs[PRope]) =
app(c, ropef(frmt, args))
const const
bufSize = 1024 # 1 KB is reasonable bufSize = 1024 # 1 KB is reasonable
proc auxRopeEqualsFile(r: PRope, bin: var File, buf: pointer): bool = proc equalsFile*(r: Rope, f: File): bool =
if r.data != nil: ## returns true if the contents of the file `f` equal `r`.
if r.length > bufSize: var
errorHandler(rTokenTooLong, r.data) buf: array[bufSize, char]
return bpos = buf.len
var readBytes = readBuffer(bin, buf, r.length) blen = buf.len
result = readBytes == r.length and
equalMem(buf, addr(r.data[0]), r.length) # BUGFIX
else:
result = auxRopeEqualsFile(r.left, bin, buf)
if result: result = auxRopeEqualsFile(r.right, bin, buf)
proc ropeEqualsFile(r: PRope, f: string): bool = for s in leaves(r):
var bin: File var spos = 0
result = open(bin, f) let slen = s.len
if not result: while spos < slen:
return # not equal if file does not exist if bpos == blen:
var buf = alloc(bufSize) # Read more data
result = auxRopeEqualsFile(r, bin, buf) bpos = 0
blen = readBuffer(f, addr(buf[0]), buf.len)
if blen == 0: # no more data in file
result = false
return
let n = min(blen - bpos, slen - spos)
# TODO There's gotta be a better way of comparing here...
if not equalMem(addr(buf[bpos]), cast[pointer](cast[int](cstring(s))+spos), n):
result = false
return
spos += n
bpos += n
result = readBuffer(f, addr(buf[0]), 1) == 0 # check that we've read all
proc equalsFile*(r: Rope, filename: string): bool =
## returns true if the contents of the file `f` equal `r`. If `f` does not
## exist, false is returned.
var f: File
result = open(f, filename)
if result: if result:
result = readBuffer(bin, buf, bufSize) == 0 # really at the end of file? result = equalsFile(r, f)
dealloc(buf) close(f)
close(bin)
proc crcFromRopeAux(r: PRope, startVal: TCrc32): TCrc32 = proc writeRopeIfNotEqual*(r: Rope, filename: string): bool =
if r.data != nil:
result = startVal
for i in countup(0, len(r.data) - 1):
result = updateCrc32(r.data[i], result)
else:
result = crcFromRopeAux(r.left, startVal)
result = crcFromRopeAux(r.right, result)
proc newCrcFromRopeAux(r: PRope, startVal: TCrc32): TCrc32 =
# XXX profiling shows this is actually expensive
var stack: TRopeSeq = @[r]
result = startVal
while len(stack) > 0:
var it = pop(stack)
while it.data == nil:
add(stack, it.right)
it = it.left
assert(it.data != nil)
var i = 0
var L = len(it.data)
while i < L:
result = updateCrc32(it.data[i], result)
inc(i)
proc crcFromRope(r: PRope): TCrc32 =
result = newCrcFromRopeAux(r, InitCrc32)
proc writeRopeIfNotEqual(r: PRope, filename: string): bool =
# returns true if overwritten # returns true if overwritten
var c: TCrc32 if not equalsFile(r, filename):
c = crcFromFile(filename)
if c != crcFromRope(r):
writeRope(r, filename) writeRope(r, filename)
result = true result = true
else: else:

View file

@ -16,7 +16,7 @@ import
procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch, procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting, intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity, evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity,
semparallel, lowerings semparallel, lowerings, plugins, plugins.active
when defined(nimfix): when defined(nimfix):
import nimfix.prettybase import nimfix.prettybase
@ -47,6 +47,24 @@ proc finishMethod(c: PContext, s: PSym)
proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode
template semIdeForTemplateOrGenericCheck(n, requiresCheck) =
# we check quickly if the node is where the cursor is
when defined(nimsuggest):
if n.info.fileIndex == gTrackPos.fileIndex and n.info.line == gTrackPos.line:
requiresCheck = true
template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
requiresCheck: bool) =
# use only for idetools support; this is pretty slow so generics and
# templates perform some quick check whether the cursor is actually in
# the generic or template.
when defined(nimsuggest):
assert gCmd == cmdIdeTools
if requiresCheck:
if optIdeDebug in gGlobalOptions:
echo "passing to safeSemExpr: ", renderTree(n)
discard safeSemExpr(c, n)
proc typeMismatch(n: PNode, formal, actual: PType) = proc typeMismatch(n: PNode, formal, actual: PType) =
if formal.kind != tyError and actual.kind != tyError: if formal.kind != tyError and actual.kind != tyError:
localError(n.info, errGenerated, msgKindToString(errTypeMismatch) & localError(n.info, errGenerated, msgKindToString(errTypeMismatch) &
@ -71,21 +89,16 @@ proc fitNode(c: PContext, formal: PType, arg: PNode): PNode =
let x = result.skipConv let x = result.skipConv
if x.kind == nkPar and formal.kind != tyExpr: if x.kind == nkPar and formal.kind != tyExpr:
changeType(x, formal, check=true) changeType(x, formal, check=true)
else:
result = skipHiddenSubConv(result)
#result.typ = takeType(formal, arg.typ)
#echo arg.info, " picked ", result.typ.typeToString
proc inferWithMetatype(c: PContext, formal: PType, proc inferWithMetatype(c: PContext, formal: PType,
arg: PNode, coerceDistincts = false): PNode arg: PNode, coerceDistincts = false): PNode
var commonTypeBegin = PType(kind: tyExpr) var commonTypeBegin = PType(kind: tyExpr)
proc isEmptyContainer(t: PType): bool =
case t.kind
of tyExpr, tyNil: result = true
of tyArray, tyArrayConstr: result = t.sons[1].kind == tyEmpty
of tySet, tySequence, tyOpenArray, tyVarargs:
result = t.sons[0].kind == tyEmpty
of tyGenericInst: result = isEmptyContainer(t.lastSon)
else: result = false
proc commonType*(x, y: PType): PType = proc commonType*(x, y: PType): PType =
# new type relation that is used for array constructors, # new type relation that is used for array constructors,
# if expressions, etc.: # if expressions, etc.:
@ -112,9 +125,11 @@ proc commonType*(x, y: PType): PType =
elif a.kind == tyTuple and b.kind == tyTuple and a.len == b.len: elif a.kind == tyTuple and b.kind == tyTuple and a.len == b.len:
var nt: PType var nt: PType
for i in 0.. <a.len: for i in 0.. <a.len:
if isEmptyContainer(a.sons[i]) and not isEmptyContainer(b.sons[i]): let aEmpty = isEmptyContainer(a.sons[i])
let bEmpty = isEmptyContainer(b.sons[i])
if aEmpty != bEmpty:
if nt.isNil: nt = copyType(a, a.owner, false) if nt.isNil: nt = copyType(a, a.owner, false)
nt.sons[i] = b.sons[i] nt.sons[i] = if aEmpty: b.sons[i] else: a.sons[i]
if not nt.isNil: result = nt if not nt.isNil: result = nt
#elif b.sons[idx].kind == tyEmpty: return x #elif b.sons[idx].kind == tyEmpty: return x
elif a.kind == tyRange and b.kind == tyRange: elif a.kind == tyRange and b.kind == tyRange:
@ -175,9 +190,15 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
allowed: TSymFlags): PSym allowed: TSymFlags): PSym
proc semStmtScope(c: PContext, n: PNode): PNode proc semStmtScope(c: PContext, n: PNode): PNode
proc typeAllowedCheck(info: TLineInfo; typ: PType; kind: TSymKind) =
let t = typeAllowed(typ, kind)
if t != nil:
if t == typ: localError(info, "invalid type: '" & typeToString(typ) & "'")
else: localError(info, "invalid type: '" & typeToString(t) &
"' in this context: '" & typeToString(typ) & "'")
proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} = proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} =
if not typeAllowed(typ, skConst): typeAllowedCheck(typ.n.info, typ, skConst)
localError(typ.n.info, errXisNoType, typeToString(typ))
proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode
@ -270,6 +291,7 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
return n return n
result = getConstExpr(c.module, e) result = getConstExpr(c.module, e)
if result == nil: if result == nil:
#if e.kind == nkEmpty: globalError(n.info, errConstExprExpected)
result = evalConstExpr(c.module, e) result = evalConstExpr(c.module, e)
if result == nil or result.kind == nkEmpty: if result == nil or result.kind == nkEmpty:
if e.info != n.info: if e.info != n.info:
@ -353,13 +375,7 @@ proc semConstBoolExpr(c: PContext, n: PNode): PNode =
localError(n.info, errConstExprExpected) localError(n.info, errConstExprExpected)
result = nn result = nn
type proc semGenericStmt(c: PContext, n: PNode): PNode
TSemGenericFlag = enum
withinBind, withinTypeDesc, withinMixin
TSemGenericFlags = set[TSemGenericFlag]
proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags,
ctx: var IntSet): PNode
include semtypes, semtempl, semgnrc, semstmts, semexprs include semtypes, semtempl, semgnrc, semstmts, semexprs
@ -386,7 +402,7 @@ proc myOpen(module: PSym): PPassContext =
c.semInferredLambda = semInferredLambda c.semInferredLambda = semInferredLambda
c.semGenerateInstance = generateInstance c.semGenerateInstance = generateInstance
c.semTypeNode = semTypeNode c.semTypeNode = semTypeNode
c.instDeepCopy = sigmatch.instDeepCopy c.instTypeBoundOp = sigmatch.instTypeBoundOp
pushProcCon(c, module) pushProcCon(c, module)
pushOwner(c.module) pushOwner(c.module)

View file

@ -1,13 +1,14 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## This module implements lifting for assignments and ``deepCopy``. ## This module implements lifting for assignments. Later versions of this code
## will be able to also lift ``=deepCopy`` and ``=destroy``.
# included from sem.nim # included from sem.nim
@ -15,98 +16,75 @@ type
TLiftCtx = object TLiftCtx = object
c: PContext c: PContext
info: TLineInfo # for construction info: TLineInfo # for construction
result: PNode
kind: TTypeAttachedOp kind: TTypeAttachedOp
fn: PSym
asgnForType: PType
recurse: bool
type proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode)
TFieldInstCtx = object # either 'tup[i]' or 'field' is valid proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym
tupleType: PType # if != nil we're traversing a tuple
tupleIndex: int
field: PSym
replaceByFieldName: bool
proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode = proc at(a, i: PNode, elemType: PType): PNode =
result = newNodeI(nkBracketExpr, a.info, 2)
result.sons[0] = a
result.sons[1] = i
result.typ = elemType
proc liftBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
for i in 0 .. <t.len:
let lit = lowerings.newIntLit(i)
liftBodyAux(c, t.sons[i], body, x.at(lit, t.sons[i]), y.at(lit, t.sons[i]))
proc dotField(x: PNode, f: PSym): PNode =
result = newNodeI(nkDotExpr, x.info, 2)
result.sons[0] = x
result.sons[1] = newSymNode(f, x.info)
result.typ = f.typ
proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
case n.kind case n.kind
of nkEmpty..pred(nkIdent), succ(nkIdent)..nkNilLit: result = n
of nkIdent:
result = n
var L = sonsLen(forLoop)
if c.replaceByFieldName:
if n.ident.id == forLoop[0].ident.id:
let fieldName = if c.tupleType.isNil: c.field.name.s
elif c.tupleType.n.isNil: "Field" & $c.tupleIndex
else: c.tupleType.n.sons[c.tupleIndex].sym.name.s
result = newStrNode(nkStrLit, fieldName)
return
# other fields:
for i in ord(c.replaceByFieldName)..L-3:
if n.ident.id == forLoop[i].ident.id:
var call = forLoop.sons[L-2]
var tupl = call.sons[i+1-ord(c.replaceByFieldName)]
if c.field.isNil:
result = newNodeI(nkBracketExpr, n.info)
result.add(tupl)
result.add(newIntNode(nkIntLit, c.tupleIndex))
else:
result = newNodeI(nkDotExpr, n.info)
result.add(tupl)
result.add(newSymNode(c.field, n.info))
break
else:
if n.kind == nkContinueStmt:
localError(n.info, errGenerated,
"'continue' not supported in a 'fields' loop")
result = copyNode(n)
newSons(result, sonsLen(n))
for i in countup(0, sonsLen(n)-1):
result.sons[i] = instFieldLoopBody(c, n.sons[i], forLoop)
proc liftBodyObj(c: TLiftCtx; typ, x, y: PNode) =
case typ.kind
of nkSym: of nkSym:
var fc: TFieldInstCtx # either 'tup[i]' or 'field' is valid let f = n.sym
fc.field = typ.sym liftBodyAux(c, f.typ, body, x.dotField(f), y.dotField(f))
fc.replaceByFieldName = c.m == mFieldPairs
openScope(c.c)
inc c.c.inUnrolledContext
let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
father.add(semStmt(c.c, body))
dec c.c.inUnrolledContext
closeScope(c.c)
of nkNilLit: discard of nkNilLit: discard
of nkRecCase: of nkRecCase:
let L = forLoop.len # copy the selector:
let call = forLoop.sons[L-2] liftBodyObj(c, n[0], body, x, y)
if call.len > 2:
localError(forLoop.info, errGenerated,
"parallel 'fields' iterator does not work for 'case' objects")
return
# iterate over the selector:
asgnForObjectFields(c, typ[0], forLoop, father)
# we need to generate a case statement: # we need to generate a case statement:
var caseStmt = newNodeI(nkCaseStmt, c.info) var caseStmt = newNodeI(nkCaseStmt, c.info)
# XXX generate 'if' that checks same branches
# generate selector: # generate selector:
var access = newNodeI(nkDotExpr, forLoop.info, 2) var access = dotField(x, n[0].sym)
access.sons[0] = call.sons[1] caseStmt.add(access)
access.sons[1] = newSymNode(typ.sons[0].sym, forLoop.info)
caseStmt.add(semExprWithType(c.c, access))
# copy the branches over, but replace the fields with the for loop body: # copy the branches over, but replace the fields with the for loop body:
for i in 1 .. <typ.len: for i in 1 .. <n.len:
var branch = copyTree(typ[i]) var branch = copyTree(n[i])
let L = branch.len let L = branch.len
branch.sons[L-1] = newNodeI(nkStmtList, forLoop.info) branch.sons[L-1] = newNodeI(nkStmtList, c.info)
semForObjectFields(c, typ[i].lastSon, forLoop, branch[L-1])
caseStmt.add(branch)
father.add(caseStmt)
of nkRecList:
for t in items(typ): liftBodyObj(c, t, x, y)
else:
illFormedAst(typ)
proc newAsgnCall(op: PSym; x, y: PNode): PNode = liftBodyObj(c, n[i].lastSon, branch.sons[L-1], x, y)
caseStmt.add(branch)
body.add(caseStmt)
localError(c.info, "cannot lift assignment operator to 'case' object")
of nkRecList:
for t in items(n): liftBodyObj(c, t, body, x, y)
else:
illFormedAstLocal(n)
proc genAddr(c: PContext; x: PNode): PNode =
if x.kind == nkHiddenDeref:
checkSonsLen(x, 1)
result = x.sons[0]
else:
result = newNodeIT(nkHiddenAddr, x.info, makeVarType(c, x.typ))
addSon(result, x)
proc newAsgnCall(c: PContext; op: PSym; x, y: PNode): PNode =
if sfError in op.flags:
localError(x.info, errWrongSymbolX, op.name.s)
result = newNodeI(nkCall, x.info) result = newNodeI(nkCall, x.info)
result.add(newSymNode(op)) result.add newSymNode(op)
result.add x result.add genAddr(c, x)
result.add y result.add y
proc newAsgnStmt(le, ri: PNode): PNode = proc newAsgnStmt(le, ri: PNode): PNode =
@ -122,76 +100,185 @@ proc newDestructorCall(op: PSym; x: PNode): PNode =
proc newDeepCopyCall(op: PSym; x, y: PNode): PNode = proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
result = newAsgnStmt(x, newDestructorCall(op, y)) result = newAsgnStmt(x, newDestructorCall(op, y))
proc considerOverloadedOp(c: TLiftCtx; t: PType; x, y: PNode): bool = proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
let op = t.attachedOps[c.kind] case c.kind
if op != nil: of attachedDestructor:
markUsed(c.info, op) let op = t.destructor
styleCheckUse(c.info, op) if op != nil:
case c.kind markUsed(c.info, op)
of attachedDestructor: styleCheckUse(c.info, op)
c.result.add newDestructorCall(op, x) body.add newDestructorCall(op, x)
of attachedAsgn: result = true
c.result.add newAsgnCall(op, x, y) of attachedAsgn:
of attachedDeepCopy: if tfHasAsgn in t.flags:
c.result.add newDeepCopyCall(op, x, y) var op: PSym
result = true if sameType(t, c.asgnForType):
# generate recursive call:
if c.recurse:
op = c.fn
else:
c.recurse = true
return false
else:
op = t.assignment
if op == nil:
op = liftBody(c.c, t, c.info)
markUsed(c.info, op)
styleCheckUse(c.info, op)
body.add newAsgnCall(c.c, op, x, y)
result = true
of attachedDeepCopy:
let op = t.deepCopy
if op != nil:
markUsed(c.info, op)
styleCheckUse(c.info, op)
body.add newDeepCopyCall(op, x, y)
result = true
proc defaultOp(c: TLiftCtx; t: PType; x, y: PNode) = proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
if c.kind != attachedDestructor: if c.kind != attachedDestructor:
c.result.add newAsgnStmt(x, y) body.add newAsgnStmt(x, y)
proc liftBodyAux(c: TLiftCtx; t: PType; x, y: PNode) = proc addVar(father, v, value: PNode) =
const hasAttachedOp: array[TTypeAttachedOp, TTypeIter] = [ var vpart = newNodeI(nkIdentDefs, v.info, 3)
(proc (t: PType, closure: PObject): bool = vpart.sons[0] = v
t.attachedOp[attachedDestructor] != nil), vpart.sons[1] = ast.emptyNode
(proc (t: PType, closure: PObject): bool = vpart.sons[2] = value
t.attachedOp[attachedAsgn] != nil), addSon(father, vpart)
(proc (t: PType, closure: PObject): bool =
t.attachedOp[attachedDeepCopy] != nil)] proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
var temp = newSym(skTemp, getIdent(lowerings.genPrefix), c.fn, c.info)
temp.typ = getSysType(tyInt)
incl(temp.flags, sfFromGeneric)
var v = newNodeI(nkVarSection, c.info)
result = newSymNode(temp)
v.addVar(result, lowerings.newIntLit(first))
body.add v
proc genBuiltin(magic: TMagic; name: string; i: PNode): PNode =
result = newNodeI(nkCall, i.info)
result.add createMagic(name, magic).newSymNode
result.add i
proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
result = newNodeI(nkWhileStmt, c.info, 2)
let cmp = genBuiltin(mLeI, "<=", i)
cmp.add genHigh(dest)
cmp.typ = getSysType(tyBool)
result.sons[0] = cmp
result.sons[1] = newNodeI(nkStmtList, c.info)
proc addIncStmt(body, i: PNode) =
let incCall = genBuiltin(mInc, "inc", i)
incCall.add lowerings.newIntLit(1)
body.add incCall
proc newSeqCall(c: PContext; x, y: PNode): PNode =
# don't call genAddr(c, x) here:
result = genBuiltin(mNewSeq, "newSeq", x)
let lenCall = genBuiltin(mLengthSeq, "len", y)
lenCall.typ = getSysType(tyInt)
result.add lenCall
proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case t.kind case t.kind
of tyNone, tyEmpty: discard of tyNone, tyEmpty: discard
of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString: of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString,
defaultOp(c, t, x, y) tyPtr, tyString, tyRef:
of tyPtr, tyString: defaultOp(c, t, body, x, y)
if not considerOverloadedOp(c, t, x, y):
defaultOp(c, t, x, y)
of tyArrayConstr, tyArray, tySequence: of tyArrayConstr, tyArray, tySequence:
if iterOverType(lastSon(t), hasAttachedOp[c.kind], nil): if tfHasAsgn in t.flags:
# generate loop and call the attached Op: if t.kind == tySequence:
# XXX add 'nil' handling here
body.add newSeqCall(c.c, x, y)
let i = declareCounter(c, body, firstOrd(t))
let whileLoop = genWhileLoop(c, i, x)
let elemType = t.lastSon
liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
y.at(i, elemType))
addIncStmt(whileLoop.sons[1], i)
body.add whileLoop
else: else:
defaultOp(c, t, x, y) defaultOp(c, t, body, x, y)
of tyObject: of tyObject, tyDistinct:
liftBodyObj(c, t.n, x, y) if not considerOverloadedOp(c, t, body, x, y):
if t.sons[0] != nil: liftBodyAux(c, t.sons[0], body, x, y)
if t.kind == tyObject: liftBodyObj(c, t.n, body, x, y)
of tyTuple: of tyTuple:
liftBodyTup(c, t, x, y) liftBodyTup(c, t, body, x, y)
of tyRef:
# we MUST not check for acyclic here as a DAG might still share nodes:
of tyProc: of tyProc:
if t.callConv != ccClosure or c.kind != attachedDeepCopy: if t.callConv != ccClosure or c.kind != attachedDeepCopy:
defaultOp(c, t, x, y) defaultOp(c, t, body, x, y)
else: else:
# a big problem is that we don't know the enviroment's type here, so we # a big problem is that we don't know the enviroment's type here, so we
# have to go through some indirection; we delegate this to the codegen: # have to go through some indirection; we delegate this to the codegen:
call = newNodeI(nkCall, n.info, 2) let call = newNodeI(nkCall, c.info, 2)
call.typ = t call.typ = t
call.sons[0] = newSymNode(createMagic("deepCopy", mDeepCopy)) call.sons[0] = newSymNode(createMagic("deepCopy", mDeepCopy))
call.sons[1] = y call.sons[1] = y
c.result.add newAsgnStmt(x, call) body.add newAsgnStmt(x, call)
of tyVarargs, tyOpenArray: of tyVarargs, tyOpenArray:
localError(c.info, errGenerated, "cannot copy openArray") localError(c.info, errGenerated, "cannot copy openArray")
of tyFromExpr, tyIter, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, of tyFromExpr, tyIter, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything, tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
tyMutable, tyGenericParam, tyGenericBody, tyNil, tyExpr, tyStmt, tyMutable, tyGenericParam, tyGenericBody, tyNil, tyExpr, tyStmt,
tyTypeDesc, tyGenericInvokation, tyBigNum, tyConst, tyForward: tyTypeDesc, tyGenericInvocation, tyBigNum, tyConst, tyForward:
internalError(c.info, "assignment requested for type: " & typeToString(t)) internalError(c.info, "assignment requested for type: " & typeToString(t))
of tyDistinct, tyOrdinal, tyRange, of tyOrdinal, tyRange,
tyGenericInst, tyFieldAccessor, tyStatic, tyVar: tyGenericInst, tyFieldAccessor, tyStatic, tyVar:
liftBodyAux(c, lastSon(t)) liftBodyAux(c, lastSon(t), body, x, y)
proc liftBody(c: PContext; typ: PType; info: TLineInfo): PNode = proc newProcType(info: TLineInfo; owner: PSym): PType =
result = newType(tyProc, owner)
result.n = newNodeI(nkFormalParams, info)
rawAddSon(result, nil) # return type
# result.n[0] used to be `nkType`, but now it's `nkEffectList` because
# the effects are now stored in there too ... this is a bit hacky, but as
# usual we desperately try to save memory:
addSon(result.n, newNodeI(nkEffectList, info))
proc addParam(procType: PType; param: PSym) =
param.position = procType.len-1
addSon(procType.n, newSymNode(param))
rawAddSon(procType, param.typ)
proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
var a: TLiftCtx var a: TLiftCtx
a.info = info a.info = info
a.result = newNodeI(nkStmtList, info) let body = newNodeI(nkStmtList, info)
liftBodyAux(a, typ) result = newSym(skProc, getIdent":lifted=", typ.owner, info)
a.fn = result
a.asgnForType = typ
let dest = newSym(skParam, getIdent"dest", result, info)
let src = newSym(skParam, getIdent"src", result, info)
dest.typ = makeVarType(c, typ)
src.typ = typ
result.typ = newProcType(info, typ.owner)
result.typ.addParam dest
result.typ.addParam src
liftBodyAux(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
var n = newNodeI(nkProcDef, info, bodyPos+1)
for i in 0 .. < n.len: n.sons[i] = emptyNode
n.sons[namePos] = newSymNode(result)
n.sons[paramsPos] = result.typ.n
n.sons[bodyPos] = body
result.ast = n
# register late as recursion is handled differently
typ.assignment = result
#echo "Produced this ", n
proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
let t = typ.skipTypes({tyGenericInst, tyVar})
result = t.assignment
if result.isNil:
result = liftBody(c, t, info)
proc overloadedAsgn(c: PContext; dest, src: PNode): PNode =
let a = getAsgnOrLiftBody(c, dest.typ, dest.info)
result = newAsgnCall(c, a, dest, src)

View file

@ -41,38 +41,55 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
best, alt: var TCandidate, best, alt: var TCandidate,
errors: var CandidateErrors) = errors: var CandidateErrors) =
var o: TOverloadIter var o: TOverloadIter
var sym = initOverloadIter(o, c, headSymbol) # thanks to the lazy semchecking for operands, we need to iterate over the
var symScope = o.lastOverloadScope # symbol table *before* any call to 'initCandidate' which might invoke
# semExpr which might modify the symbol table in cases like
# 'init(a, 1, (var b = new(Type2); b))'.
var symx = initOverloadIter(o, c, headSymbol)
let symScope = o.lastOverloadScope
var syms: seq[tuple[a: PSym, b: int]] = @[]
while symx != nil:
if symx.kind in filter: syms.add((symx, o.lastOverloadScope))
symx = nextOverloadIter(o, c, headSymbol)
if syms.len == 0: return
var z: TCandidate var z: TCandidate
initCandidate(c, best, syms[0][0], initialBinding, symScope)
if sym == nil: return initCandidate(c, alt, syms[0][0], initialBinding, symScope)
initCandidate(c, best, sym, initialBinding, symScope)
initCandidate(c, alt, sym, initialBinding, symScope)
best.state = csNoMatch best.state = csNoMatch
while sym != nil: for i in 0 .. <syms.len:
if sym.kind in filter: let sym = syms[i][0]
determineType(c, sym) determineType(c, sym)
initCandidate(c, z, sym, initialBinding, o.lastOverloadScope) initCandidate(c, z, sym, initialBinding, syms[i][1])
z.calleeSym = sym z.calleeSym = sym
matches(c, n, orig, z)
if errors != nil: #if sym.name.s == "*" and (n.info ?? "temp5.nim") and n.info.line == 140:
errors.safeAdd(sym) # gDebug = true
if z.errors != nil: matches(c, n, orig, z)
for err in z.errors: if errors != nil:
errors.add(err) errors.safeAdd(sym)
if z.state == csMatch: if z.errors != nil:
# little hack so that iterators are preferred over everything else: for err in z.errors:
if sym.kind in skIterators: inc(z.exactMatches, 200) errors.add(err)
case best.state if z.state == csMatch:
of csEmpty, csNoMatch: best = z # little hack so that iterators are preferred over everything else:
of csMatch: if sym.kind in skIterators: inc(z.exactMatches, 200)
var cmp = cmpCandidates(best, z) case best.state
if cmp < 0: best = z # x is better than the best so far of csEmpty, csNoMatch: best = z
elif cmp == 0: alt = z # x is as good as the best so far of csMatch:
else: discard var cmp = cmpCandidates(best, z)
sym = nextOverloadIter(o, c, headSymbol) if cmp < 0: best = z # x is better than the best so far
elif cmp == 0: alt = z # x is as good as the best so far
else: discard
#if sym.name.s == "cmp" and (n.info ?? "rstgen.nim") and n.info.line == 516:
# echo "Matches ", n.info, " ", typeToString(sym.typ)
# debug sym
# writeMatches(z)
# for i in 1 .. <len(z.call):
# z.call[i].typ.debug
# quit 1
proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
# Gives a detailed error message; this is separated from semOverloadedCall, # Gives a detailed error message; this is separated from semOverloadedCall,
@ -81,8 +98,9 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
if c.inCompilesContext > 0: if c.inCompilesContext > 0:
# fail fast: # fail fast:
globalError(n.info, errTypeMismatch, "") globalError(n.info, errTypeMismatch, "")
if errors.len == 0: if errors.isNil or errors.len == 0:
localError(n.info, errExprXCannotBeCalled, n[0].renderTree) localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
return
# to avoid confusing errors like: # to avoid confusing errors like:
# got (SslPtr, SocketHandle) # got (SslPtr, SocketHandle)
@ -123,7 +141,8 @@ proc gatherUsedSyms(c: PContext, usedSyms: var seq[PNode]) =
for s in scope.usingSyms: usedSyms.safeAdd(s) for s in scope.usingSyms: usedSyms.safeAdd(s)
proc resolveOverloads(c: PContext, n, orig: PNode, proc resolveOverloads(c: PContext, n, orig: PNode,
filter: TSymKinds): TCandidate = filter: TSymKinds;
errors: var CandidateErrors): TCandidate =
var initialBinding: PNode var initialBinding: PNode
var alt: TCandidate var alt: TCandidate
var f = n.sons[0] var f = n.sons[0]
@ -134,7 +153,6 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
else: else:
initialBinding = nil initialBinding = nil
var errors: CandidateErrors
var usedSyms: seq[PNode] var usedSyms: seq[PNode]
template pickBest(headSymbol: expr) = template pickBest(headSymbol: expr) =
@ -183,7 +201,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
elif nfDotSetter in n.flags: elif nfDotSetter in n.flags:
internalAssert f.kind == nkIdent and n.sonsLen == 3 internalAssert f.kind == nkIdent and n.sonsLen == 3
let calleeName = newStrNode(nkStrLit, f.ident.s[0.. -2]).withInfo(n.info) let calleeName = newStrNode(nkStrLit,
f.ident.s[0..f.ident.s.len-2]).withInfo(n.info)
let callOp = newIdentNode(getIdent".=", n.info) let callOp = newIdentNode(getIdent".=", n.info)
n.sons[0..1] = [callOp, n[1], calleeName] n.sons[0..1] = [callOp, n[1], calleeName]
orig.sons[0..1] = [callOp, orig[1], calleeName] orig.sons[0..1] = [callOp, orig[1], calleeName]
@ -204,7 +223,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
errors = @[] errors = @[]
pickBest(f) pickBest(f)
notFoundError(c, n, errors) #notFoundError(c, n, errors)
return return
@ -260,7 +279,7 @@ proc inferWithMetatype(c: PContext, formal: PType,
instGenericConvertersArg(c, result, m) instGenericConvertersArg(c, result, m)
if result != nil: if result != nil:
# This almost exactly replicates the steps taken by the compiler during # This almost exactly replicates the steps taken by the compiler during
# param matching. It performs an embarassing ammount of back-and-forth # param matching. It performs an embarrassing amount of back-and-forth
# type jugling, but it's the price to pay for consistency and correctness # type jugling, but it's the price to pay for consistency and correctness
result.typ = generateTypeInstance(c, m.bindings, arg.info, result.typ = generateTypeInstance(c, m.bindings, arg.info,
formal.skipTypes({tyCompositeTypeClass})) formal.skipTypes({tyCompositeTypeClass}))
@ -277,25 +296,45 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
styleCheckUse(n.sons[0].info, finalCallee) styleCheckUse(n.sons[0].info, finalCallee)
if finalCallee.ast == nil: if finalCallee.ast == nil:
internalError(n.info, "calleeSym.ast is nil") # XXX: remove this check! internalError(n.info, "calleeSym.ast is nil") # XXX: remove this check!
if x.hasFauxMatch:
result = x.call
result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
if containsGenericType(result.typ) or x.fauxMatch == tyUnknown:
result.typ = newTypeS(x.fauxMatch, c)
return
if finalCallee.ast.sons[genericParamsPos].kind != nkEmpty: if finalCallee.ast.sons[genericParamsPos].kind != nkEmpty:
# a generic proc! finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
if not x.proxyMatch:
finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
else:
result = x.call
result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
result.typ = finalCallee.typ.sons[0]
if containsGenericType(result.typ): result.typ = errorType(c)
return
result = x.call result = x.call
instGenericConvertersSons(c, result, x) instGenericConvertersSons(c, result, x)
result.sons[0] = newSymNode(finalCallee, result.sons[0].info) result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
result.typ = finalCallee.typ.sons[0] result.typ = finalCallee.typ.sons[0]
proc canDeref(n: PNode): bool {.inline.} =
result = n.len >= 2 and (let t = n[1].typ;
t != nil and t.skipTypes({tyGenericInst}).kind in {tyPtr, tyRef})
proc tryDeref(n: PNode): PNode =
result = newNodeI(nkHiddenDeref, n.info)
result.typ = n.typ.skipTypes(abstractInst).sons[0]
result.addSon(n)
proc semOverloadedCall(c: PContext, n, nOrig: PNode, proc semOverloadedCall(c: PContext, n, nOrig: PNode,
filter: TSymKinds): PNode = filter: TSymKinds): PNode =
var r = resolveOverloads(c, n, nOrig, filter) var errors: CandidateErrors
var r = resolveOverloads(c, n, nOrig, filter, errors)
if r.state == csMatch: result = semResolvedCall(c, n, r) if r.state == csMatch: result = semResolvedCall(c, n, r)
elif experimentalMode(c) and canDeref(n):
# try to deref the first argument and then try overloading resolution again:
n.sons[1] = n.sons[1].tryDeref
var r = resolveOverloads(c, n, nOrig, filter, errors)
if r.state == csMatch: result = semResolvedCall(c, n, r)
else:
# get rid of the deref again for a better error message:
n.sons[1] = n.sons[1].sons[0]
notFoundError(c, n, errors)
else:
notFoundError(c, n, errors)
# else: result = errorNode(c, n) # else: result = errorNode(c, n)
proc explicitGenericInstError(n: PNode): PNode = proc explicitGenericInstError(n: PNode): PNode =

View file

@ -26,7 +26,7 @@ type
POptionEntry* = ref TOptionEntry POptionEntry* = ref TOptionEntry
PProcCon* = ref TProcCon PProcCon* = ref TProcCon
TProcCon*{.final.} = object # procedure context; also used for top-level TProcCon* = object # procedure context; also used for top-level
# statements # statements
owner*: PSym # the symbol this context belongs to owner*: PSym # the symbol this context belongs to
resultSym*: PSym # the result symbol (if we are in a proc) resultSym*: PSym # the result symbol (if we are in a proc)
@ -35,16 +35,24 @@ type
inTryStmt*: int # whether we are in a try statement; works also inTryStmt*: int # whether we are in a try statement; works also
# in standalone ``except`` and ``finally`` # in standalone ``except`` and ``finally``
next*: PProcCon # used for stacking procedure contexts next*: PProcCon # used for stacking procedure contexts
wasForwarded*: bool # whether the current proc has a separate header
bracketExpr*: PNode # current bracket expression (for ^ support)
TInstantiationPair* = object TInstantiationPair* = object
genericSym*: PSym genericSym*: PSym
inst*: PInstantiation inst*: PInstantiation
TExprFlag* = enum TExprFlag* = enum
efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType, efLValue, efWantIterator, efInTypeof,
efWantStmt, efAllowStmt, efDetermineType,
efAllowDestructor, efWantValue, efOperand, efNoSemCheck efAllowDestructor, efWantValue, efOperand, efNoSemCheck
TExprFlags* = set[TExprFlag] TExprFlags* = set[TExprFlag]
TTypeAttachedOp* = enum
attachedAsgn,
attachedDeepCopy,
attachedDestructor
PContext* = ref TContext PContext* = ref TContext
TContext* = object of TPassContext # a context represents a module TContext* = object of TPassContext # a context represents a module
module*: PSym # the module sym belonging to the context module*: PSym # the module sym belonging to the context
@ -91,8 +99,8 @@ type
lastGenericIdx*: int # used for the generics stack lastGenericIdx*: int # used for the generics stack
hloLoopDetector*: int # used to prevent endless loops in the HLO hloLoopDetector*: int # used to prevent endless loops in the HLO
inParallelStmt*: int inParallelStmt*: int
instDeepCopy*: proc (c: PContext; dc: PSym; t: PType; instTypeBoundOp*: proc (c: PContext; dc: PSym; t: PType; info: TLineInfo;
info: TLineInfo): PSym {.nimcall.} op: TTypeAttachedOp): PSym {.nimcall.}
proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
@ -110,7 +118,6 @@ proc newOptionEntry*(): POptionEntry
proc newLib*(kind: TLibKind): PLib proc newLib*(kind: TLibKind): PLib
proc addToLib*(lib: PLib, sym: PSym) proc addToLib*(lib: PLib, sym: PSym)
proc makePtrType*(c: PContext, baseType: PType): PType proc makePtrType*(c: PContext, baseType: PType): PType
proc makeVarType*(c: PContext, baseType: PType): PType
proc newTypeS*(kind: TTypeKind, c: PContext): PType proc newTypeS*(kind: TTypeKind, c: PContext): PType
proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext) proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext)
@ -205,9 +212,12 @@ proc makePtrType(c: PContext, baseType: PType): PType =
result = newTypeS(tyPtr, c) result = newTypeS(tyPtr, c)
addSonSkipIntLit(result, baseType.assertNotNil) addSonSkipIntLit(result, baseType.assertNotNil)
proc makeVarType(c: PContext, baseType: PType): PType = proc makeVarType*(c: PContext, baseType: PType): PType =
result = newTypeS(tyVar, c) if baseType.kind == tyVar:
addSonSkipIntLit(result, baseType.assertNotNil) result = baseType
else:
result = newTypeS(tyVar, c)
addSonSkipIntLit(result, baseType.assertNotNil)
proc makeTypeDesc*(c: PContext, typ: PType): PType = proc makeTypeDesc*(c: PContext, typ: PType): PType =
result = newTypeS(tyTypeDesc, c) result = newTypeS(tyTypeDesc, c)
@ -220,6 +230,7 @@ proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
proc makeTypeFromExpr*(c: PContext, n: PNode): PType = proc makeTypeFromExpr*(c: PContext, n: PNode): PType =
result = newTypeS(tyFromExpr, c) result = newTypeS(tyFromExpr, c)
assert n != nil
result.n = n result.n = n
proc newTypeWithSons*(c: PContext, kind: TTypeKind, proc newTypeWithSons*(c: PContext, kind: TTypeKind,
@ -238,6 +249,7 @@ proc makeAndType*(c: PContext, t1, t2: PType): PType =
propagateToOwner(result, t1) propagateToOwner(result, t1)
propagateToOwner(result, t2) propagateToOwner(result, t2)
result.flags.incl((t1.flags + t2.flags) * {tfHasStatic}) result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
result.flags.incl tfHasMeta
proc makeOrType*(c: PContext, t1, t2: PType): PType = proc makeOrType*(c: PContext, t1, t2: PType): PType =
result = newTypeS(tyOr, c) result = newTypeS(tyOr, c)
@ -245,12 +257,14 @@ proc makeOrType*(c: PContext, t1, t2: PType): PType =
propagateToOwner(result, t1) propagateToOwner(result, t1)
propagateToOwner(result, t2) propagateToOwner(result, t2)
result.flags.incl((t1.flags + t2.flags) * {tfHasStatic}) result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
result.flags.incl tfHasMeta
proc makeNotType*(c: PContext, t1: PType): PType = proc makeNotType*(c: PContext, t1: PType): PType =
result = newTypeS(tyNot, c) result = newTypeS(tyNot, c)
result.sons = @[t1] result.sons = @[t1]
propagateToOwner(result, t1) propagateToOwner(result, t1)
result.flags.incl(t1.flags * {tfHasStatic}) result.flags.incl(t1.flags * {tfHasStatic})
result.flags.incl tfHasMeta
proc nMinusOne*(n: PNode): PNode = proc nMinusOne*(n: PNode): PNode =
result = newNode(nkCall, n.info, @[ result = newNode(nkCall, n.info, @[
@ -268,7 +282,7 @@ proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType =
template rangeHasStaticIf*(t: PType): bool = template rangeHasStaticIf*(t: PType): bool =
# this accepts the ranges's node # this accepts the ranges's node
t.n[1].kind == nkStaticExpr t.n != nil and t.n.len > 1 and t.n[1].kind == nkStaticExpr
template getStaticTypeFromRange*(t: PType): PType = template getStaticTypeFromRange*(t: PType): PType =
t.n[1][0][1].typ t.n[1][0][1].typ
@ -306,6 +320,9 @@ proc markIndirect*(c: PContext, s: PSym) {.inline.} =
proc illFormedAst*(n: PNode) = proc illFormedAst*(n: PNode) =
globalError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments})) globalError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
proc illFormedAstLocal*(n: PNode) =
localError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
proc checkSonsLen*(n: PNode, length: int) = proc checkSonsLen*(n: PNode, length: int) =
if sonsLen(n) != length: illFormedAst(n) if sonsLen(n) != length: illFormedAst(n)

View file

@ -30,7 +30,7 @@ proc instantiateDestructor(c: PContext, typ: PType): PType
proc doDestructorStuff(c: PContext, s: PSym, n: PNode) = proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
var t = s.typ.sons[1].skipTypes({tyVar}) var t = s.typ.sons[1].skipTypes({tyVar})
if t.kind == tyGenericInvokation: if t.kind == tyGenericInvocation:
for i in 1 .. <t.sonsLen: for i in 1 .. <t.sonsLen:
if t.sons[i].kind != tyGenericParam: if t.sons[i].kind != tyGenericParam:
localError(n.info, errDestructorNotGenericEnough) localError(n.info, errDestructorNotGenericEnough)
@ -71,11 +71,12 @@ proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode =
result.addSon(newNode(nkDotExpr, n.info, @[holder, n.sons[0]])) result.addSon(newNode(nkDotExpr, n.info, @[holder, n.sons[0]]))
for i in countup(1, n.len - 1): for i in countup(1, n.len - 1):
# of A, B: # of A, B:
var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2]) let ni = n[i]
var caseBranch = newNode(ni.kind, ni.info, ni.sons[0..ni.len-2])
let stmt = destroyFieldOrFields(c, n[i].lastSon, holder) let stmt = destroyFieldOrFields(c, ni.lastSon, holder)
if stmt == nil: if stmt == nil:
caseBranch.addSon(newNode(nkStmtList, n[i].info, @[])) caseBranch.addSon(newNode(nkStmtList, ni.info, @[]))
else: else:
caseBranch.addSon(stmt) caseBranch.addSon(stmt)
nonTrivialFields += stmt.len nonTrivialFields += stmt.len
@ -209,7 +210,7 @@ proc insertDestructors(c: PContext,
if j < totalVars - 1: if j < totalVars - 1:
var remainingVars = newNodeI(varSection.kind, info) var remainingVars = newNodeI(varSection.kind, info)
remainingVars.sons = varSection.sons[(j+1)..(-1)] remainingVars.sons = varSection.sons[(j+1)..varSection.len-1]
let (outer, inner) = insertDestructors(c, remainingVars) let (outer, inner) = insertDestructors(c, remainingVars)
if outer != nil: if outer != nil:
tryStmt.addSon(outer) tryStmt.addSon(outer)

View file

@ -24,14 +24,14 @@ proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
# same as 'semExprWithType' but doesn't check for proc vars # same as 'semExprWithType' but doesn't check for proc vars
result = semExpr(c, n, flags + {efOperand}) result = semExpr(c, n, flags + {efOperand})
if result.kind == nkEmpty: if result.kind == nkEmpty and result.typ.isNil:
# do not produce another redundant error message: # do not produce another redundant error message:
#raiseRecoverableError("") #raiseRecoverableError("")
result = errorNode(c, n) result = errorNode(c, n)
if result.typ != nil: if result.typ != nil:
# XXX tyGenericInst here? # XXX tyGenericInst here?
if result.typ.kind == tyVar: result = newDeref(result) if result.typ.kind == tyVar: result = newDeref(result)
elif efWantStmt in flags: elif {efWantStmt, efAllowStmt} * flags != {}:
result.typ = newTypeS(tyEmpty, c) result.typ = newTypeS(tyEmpty, c)
else: else:
localError(n.info, errExprXHasNoType, localError(n.info, errExprXHasNoType,
@ -103,15 +103,31 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
of skMacro: result = semMacroExpr(c, n, n, s, flags) of skMacro: result = semMacroExpr(c, n, n, s, flags)
of skTemplate: result = semTemplateExpr(c, n, s, flags) of skTemplate: result = semTemplateExpr(c, n, s, flags)
of skVar, skLet, skResult, skParam, skForVar: of skParam:
markUsed(n.info, s)
styleCheckUse(n.info, s)
if s.typ.kind == tyStatic and s.typ.n != nil:
# XXX see the hack in sigmatch.nim ...
return s.typ.n
elif sfGenSym in s.flags:
if c.p.wasForwarded:
# gensym'ed parameters that nevertheless have been forward declared
# need a special fixup:
let realParam = c.p.owner.typ.n[s.position+1]
internalAssert realParam.kind == nkSym and realParam.sym.kind == skParam
return newSymNode(c.p.owner.typ.n[s.position+1].sym, n.info)
elif c.p.owner.kind == skMacro:
# gensym'ed macro parameters need a similar hack (see bug #1944):
var u = searchInScopes(c, s.name)
internalAssert u != nil and u.kind == skParam and u.owner == s.owner
return newSymNode(u, n.info)
result = newSymNode(s, n.info)
of skVar, skLet, skResult, skForVar:
markUsed(n.info, s) markUsed(n.info, s)
styleCheckUse(n.info, s) styleCheckUse(n.info, s)
# if a proc accesses a global variable, it is not side effect free: # if a proc accesses a global variable, it is not side effect free:
if sfGlobal in s.flags: if sfGlobal in s.flags:
incl(c.p.owner.flags, sfSideEffect) incl(c.p.owner.flags, sfSideEffect)
elif s.kind == skParam and s.typ.kind == tyStatic and s.typ.n != nil:
# XXX see the hack in sigmatch.nim ...
return s.typ.n
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
# We cannot check for access to outer vars for example because it's still # We cannot check for access to outer vars for example because it's still
# not sure the symbol really ends up being used: # not sure the symbol really ends up being used:
@ -201,7 +217,7 @@ proc isCastable(dst, src: PType): bool =
result = false result = false
elif srcSize < 0: elif srcSize < 0:
result = false result = false
elif not typeAllowed(dst, skParam): elif typeAllowed(dst, skParam) != nil:
result = false result = false
else: else:
result = (dstSize >= srcSize) or result = (dstSize >= srcSize) or
@ -219,7 +235,7 @@ proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) =
# gnrc. params, then it won't be necessary to open a new scope here # gnrc. params, then it won't be necessary to open a new scope here
openScope(c) openScope(c)
var lifted = liftParamType(c, skType, newNodeI(nkArgList, info), var lifted = liftParamType(c, skType, newNodeI(nkArgList, info),
t, ":anon", info) t, ":anon", info)
closeScope(c) closeScope(c)
if lifted != nil: t = lifted if lifted != nil: t = lifted
@ -229,7 +245,7 @@ proc semConv(c: PContext, n: PNode): PNode =
return n return n
result = newNodeI(nkConv, n.info) result = newNodeI(nkConv, n.info)
var targetType = semTypeNode(c, n.sons[0], nil) var targetType = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc})
maybeLiftType(targetType, c, n[0].info) maybeLiftType(targetType, c, n[0].info)
result.addSon copyTree(n.sons[0]) result.addSon copyTree(n.sons[0])
var op = semExprWithType(c, n.sons[1]) var op = semExprWithType(c, n.sons[1])
@ -293,7 +309,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
var typ = skipTypes(n.sons[1].typ, abstractVarRange + var typ = skipTypes(n.sons[1].typ, abstractVarRange +
{tyTypeDesc, tyFieldAccessor}) {tyTypeDesc, tyFieldAccessor})
case typ.kind case typ.kind
of tySequence, tyString, tyOpenArray, tyVarargs: of tySequence, tyString, tyCString, tyOpenArray, tyVarargs:
n.typ = getSysType(tyInt) n.typ = getSysType(tyInt)
of tyArrayConstr, tyArray: of tyArrayConstr, tyArray:
n.typ = typ.sons[0] # indextype n.typ = typ.sons[0] # indextype
@ -373,7 +389,7 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
maybeLiftType(t2, c, n.info) maybeLiftType(t2, c, n.info)
var m: TCandidate var m: TCandidate
initCandidate(c, m, t2) initCandidate(c, m, t2)
let match = typeRel(m, t2, t1) != isNone let match = typeRel(m, t2, t1) >= isSubtype # isNone
result = newIntNode(nkIntLit, ord(match)) result = newIntNode(nkIntLit, ord(match))
result.typ = n.typ result.typ = n.typ
@ -431,14 +447,32 @@ proc changeType(n: PNode, newType: PType, check: bool) =
of nkPar: of nkPar:
let tup = newType.skipTypes({tyGenericInst}) let tup = newType.skipTypes({tyGenericInst})
if tup.kind != tyTuple: if tup.kind != tyTuple:
if tup.kind == tyObject: return
internalError(n.info, "changeType: no tuple type for constructor") internalError(n.info, "changeType: no tuple type for constructor")
elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr:
# named tuple?
for i in countup(0, sonsLen(n) - 1):
var m = n.sons[i].sons[0]
if m.kind != nkSym:
internalError(m.info, "changeType(): invalid tuple constr")
return
if tup.n != nil:
var f = getSymFromList(tup.n, m.sym.name)
if f == nil:
internalError(m.info, "changeType(): invalid identifier")
return
changeType(n.sons[i].sons[1], f.typ, check)
else:
changeType(n.sons[i].sons[1], tup.sons[i], check)
else: else:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var m = n.sons[i] changeType(n.sons[i], tup.sons[i], check)
if m.kind == nkExprColonExpr: when false:
m = m.sons[1] var m = n.sons[i]
n.sons[i] = m var a = newNodeIT(nkExprColonExpr, m.info, newType.sons[i])
changeType(m, tup.sons[i], check) addSon(a, newSymNode(newType.n.sons[i].sym))
addSon(a, m)
changeType(m, tup.sons[i], check)
of nkCharLit..nkUInt64Lit: of nkCharLit..nkUInt64Lit:
if check: if check:
let value = n.intVal let value = n.intVal
@ -501,44 +535,45 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
result.sons[i] = fitNode(c, typ, result.sons[i]) result.sons[i] = fitNode(c, typ, result.sons[i])
result.typ.sons[0] = makeRangeType(c, 0, sonsLen(result) - 1, n.info) result.typ.sons[0] = makeRangeType(c, 0, sonsLen(result) - 1, n.info)
proc fixAbstractType(c: PContext, n: PNode) = template fixAbstractType(c: PContext, n: PNode) =
# XXX finally rewrite that crap! when false:
for i in countup(1, sonsLen(n) - 1): # XXX finally rewrite that crap!
var it = n.sons[i] for i in countup(1, sonsLen(n) - 1):
case it.kind var it = n.sons[i]
of nkHiddenStdConv, nkHiddenSubConv: case it.kind
if it.sons[1].kind == nkBracket: of nkHiddenStdConv, nkHiddenSubConv:
it.sons[1].typ = arrayConstrType(c, it.sons[1]) if it.sons[1].kind == nkBracket:
#it.sons[1] = semArrayConstr(c, it.sons[1]) it.sons[1].typ = arrayConstrType(c, it.sons[1])
if skipTypes(it.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: #it.sons[1] = semArrayConstr(c, it.sons[1])
#if n.sons[0].kind == nkSym and IdentEq(n.sons[0].sym.name, "[]="): if skipTypes(it.typ, abstractVar).kind in {tyOpenArray, tyVarargs}:
# debug(n) #if n.sons[0].kind == nkSym and IdentEq(n.sons[0].sym.name, "[]="):
# debug(n)
var s = skipTypes(it.sons[1].typ, abstractVar) var s = skipTypes(it.sons[1].typ, abstractVar)
if s.kind == tyArrayConstr and s.sons[1].kind == tyEmpty: if s.kind == tyArrayConstr and s.sons[1].kind == tyEmpty:
s = copyType(s, getCurrOwner(), false) s = copyType(s, getCurrOwner(), false)
skipTypes(s, abstractVar).sons[1] = elemType( skipTypes(s, abstractVar).sons[1] = elemType(
skipTypes(it.typ, abstractVar)) skipTypes(it.typ, abstractVar))
it.sons[1].typ = s it.sons[1].typ = s
elif s.kind == tySequence and s.sons[0].kind == tyEmpty: elif s.kind == tySequence and s.sons[0].kind == tyEmpty:
s = copyType(s, getCurrOwner(), false) s = copyType(s, getCurrOwner(), false)
skipTypes(s, abstractVar).sons[0] = elemType( skipTypes(s, abstractVar).sons[0] = elemType(
skipTypes(it.typ, abstractVar)) skipTypes(it.typ, abstractVar))
it.sons[1].typ = s it.sons[1].typ = s
elif skipTypes(it.sons[1].typ, abstractVar).kind in elif skipTypes(it.sons[1].typ, abstractVar).kind in
{tyNil, tyArrayConstr, tyTuple, tySet}: {tyNil, tyArrayConstr, tyTuple, tySet}:
var s = skipTypes(it.typ, abstractVar) var s = skipTypes(it.typ, abstractVar)
if s.kind != tyExpr: if s.kind != tyExpr:
changeType(it.sons[1], s, check=true) changeType(it.sons[1], s, check=true)
n.sons[i] = it.sons[1] n.sons[i] = it.sons[1]
of nkBracket: of nkBracket:
# an implicitly constructed array (passed to an open array): # an implicitly constructed array (passed to an open array):
n.sons[i] = semArrayConstr(c, it, {}) n.sons[i] = semArrayConstr(c, it, {})
else: else:
discard discard
#if (it.typ == nil): #if (it.typ == nil):
# InternalError(it.info, "fixAbstractType: " & renderTree(it)) # InternalError(it.info, "fixAbstractType: " & renderTree(it))
proc skipObjConv(n: PNode): PNode = proc skipObjConv(n: PNode): PNode =
case n.kind case n.kind
@ -554,7 +589,8 @@ proc isAssignable(c: PContext, n: PNode): TAssignableResult =
result = parampatterns.isAssignable(c.p.owner, n) result = parampatterns.isAssignable(c.p.owner, n)
proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
if n.kind == nkHiddenDeref: if n.kind == nkHiddenDeref and not (gCmd == cmdCompileToCpp or
sfCompileToCpp in c.module.flags):
checkSonsLen(n, 1) checkSonsLen(n, 1)
result = n.sons[0] result = n.sons[0]
else: else:
@ -652,7 +688,9 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
# implicit statics. # implicit statics.
if n.len > 1: if n.len > 1:
for i in 1 .. <n.len: for i in 1 .. <n.len:
if n[i].typ.kind != tyStatic or tfUnresolved notin n[i].typ.flags: # see bug #2113, it's possible that n[i].typ for errornous code:
if n[i].typ.isNil or n[i].typ.kind != tyStatic or
tfUnresolved notin n[i].typ.flags:
break maybeLabelAsStatic break maybeLabelAsStatic
n.typ = newTypeWithSons(c, tyStatic, @[n.typ]) n.typ = newTypeWithSons(c, tyStatic, @[n.typ])
n.typ.flags.incl tfUnresolved n.typ.flags.incl tfUnresolved
@ -667,7 +705,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
if callee.kind notin {skProc, skConverter} or callee.isGenericRoutine: if callee.kind notin {skProc, skConverter} or callee.isGenericRoutine:
return return
if n.typ != nil and not typeAllowed(n.typ, skConst): return if n.typ != nil and typeAllowed(n.typ, skConst) != nil: return
var call = newNodeIT(nkCall, n.info, n.typ) var call = newNodeIT(nkCall, n.info, n.typ)
call.add(n.sons[0]) call.add(n.sons[0])
@ -720,11 +758,26 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
else: else:
if callee.kind in skIterators and callee.id == c.p.owner.id: if callee.kind in skIterators and callee.id == c.p.owner.id:
localError(n.info, errRecursiveDependencyX, callee.name.s) localError(n.info, errRecursiveDependencyX, callee.name.s)
# error correction, prevents endless for loop elimination in transf.
# See bug #2051:
result.sons[0] = newSymNode(errorSym(c, n))
if sfNoSideEffect notin callee.flags: if sfNoSideEffect notin callee.flags:
if {sfImportc, sfSideEffect} * callee.flags != {}: if {sfImportc, sfSideEffect} * callee.flags != {}:
incl(c.p.owner.flags, sfSideEffect) incl(c.p.owner.flags, sfSideEffect)
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode
proc resolveIndirectCall(c: PContext; n, nOrig: PNode;
t: PType): TCandidate =
initCandidate(c, result, t)
matches(c, n, nOrig, result)
if result.state != csMatch:
# try to deref the first argument:
if experimentalMode(c) and canDeref(n):
n.sons[1] = n.sons[1].tryDeref
initCandidate(c, result, t)
matches(c, n, nOrig, result)
proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = nil result = nil
checkMinSonsLen(n, 1) checkMinSonsLen(n, 1)
@ -748,9 +801,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
t = skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}) t = skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc})
if t != nil and t.kind == tyProc: if t != nil and t.kind == tyProc:
# This is a proc variable, apply normal overload resolution # This is a proc variable, apply normal overload resolution
var m: TCandidate let m = resolveIndirectCall(c, n, nOrig, t)
initCandidate(c, m, t)
matches(c, n, nOrig, m)
if m.state != csMatch: if m.state != csMatch:
if c.inCompilesContext > 0: if c.inCompilesContext > 0:
# speed up error generation: # speed up error generation:
@ -780,7 +831,6 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
if tfNoSideEffect notin t.flags: incl(c.p.owner.flags, sfSideEffect) if tfNoSideEffect notin t.flags: incl(c.p.owner.flags, sfSideEffect)
elif t != nil and t.kind == tyTypeDesc: elif t != nil and t.kind == tyTypeDesc:
if n.len == 1: return semObjConstr(c, n, flags) if n.len == 1: return semObjConstr(c, n, flags)
let destType = t.skipTypes({tyTypeDesc, tyGenericInst})
return semConv(c, n) return semConv(c, n)
else: else:
result = overloadedCallOpr(c, n) result = overloadedCallOpr(c, n)
@ -851,6 +901,11 @@ proc isTypeExpr(n: PNode): bool =
of nkSym: result = n.sym.kind == skType of nkSym: result = n.sym.kind == skType
else: result = false else: result = false
proc createSetType(c: PContext; baseType: PType): PType =
assert baseType != nil
result = newTypeS(tySet, c)
rawAddSon(result, baseType)
proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent, proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
check: var PNode): PSym = check: var PNode): PSym =
# transform in a node that contains the runtime check for the # transform in a node that contains the runtime check for the
@ -866,7 +921,8 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
if (r.sons[0].kind != nkSym): illFormedAst(r) if (r.sons[0].kind != nkSym): illFormedAst(r)
result = lookupInRecordAndBuildCheck(c, n, r.sons[0], field, check) result = lookupInRecordAndBuildCheck(c, n, r.sons[0], field, check)
if result != nil: return if result != nil: return
var s = newNodeI(nkCurly, r.info) let setType = createSetType(c, r.sons[0].typ)
var s = newNodeIT(nkCurly, r.info, setType)
for i in countup(1, sonsLen(r) - 1): for i in countup(1, sonsLen(r) - 1):
var it = r.sons[i] var it = r.sons[i]
case it.kind case it.kind
@ -878,13 +934,14 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
if check == nil: if check == nil:
check = newNodeI(nkCheckedFieldExpr, n.info) check = newNodeI(nkCheckedFieldExpr, n.info)
addSon(check, ast.emptyNode) # make space for access node addSon(check, ast.emptyNode) # make space for access node
s = newNodeI(nkCurly, n.info) s = newNodeIT(nkCurly, n.info, setType)
for j in countup(0, sonsLen(it) - 2): addSon(s, copyTree(it.sons[j])) for j in countup(0, sonsLen(it) - 2): addSon(s, copyTree(it.sons[j]))
var inExpr = newNodeI(nkCall, n.info) var inExpr = newNodeIT(nkCall, n.info, getSysType(tyBool))
addSon(inExpr, newIdentNode(getIdent("in"), n.info)) addSon(inExpr, newSymNode(ast.opContains, n.info))
addSon(inExpr, s)
addSon(inExpr, copyTree(r.sons[0])) addSon(inExpr, copyTree(r.sons[0]))
addSon(inExpr, s) #writeln(output, renderTree(inExpr)); addSon(check, inExpr)
addSon(check, semExpr(c, inExpr)) #addSon(check, semExpr(c, inExpr))
return return
of nkElse: of nkElse:
result = lookupInRecordAndBuildCheck(c, n, lastSon(it), field, check) result = lookupInRecordAndBuildCheck(c, n, lastSon(it), field, check)
@ -892,14 +949,14 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
if check == nil: if check == nil:
check = newNodeI(nkCheckedFieldExpr, n.info) check = newNodeI(nkCheckedFieldExpr, n.info)
addSon(check, ast.emptyNode) # make space for access node addSon(check, ast.emptyNode) # make space for access node
var inExpr = newNodeI(nkCall, n.info) var inExpr = newNodeIT(nkCall, n.info, getSysType(tyBool))
addSon(inExpr, newIdentNode(getIdent("in"), n.info)) addSon(inExpr, newSymNode(ast.opContains, n.info))
addSon(inExpr, copyTree(r.sons[0]))
addSon(inExpr, s) addSon(inExpr, s)
var notExpr = newNodeI(nkCall, n.info) addSon(inExpr, copyTree(r.sons[0]))
addSon(notExpr, newIdentNode(getIdent("not"), n.info)) var notExpr = newNodeIT(nkCall, n.info, getSysType(tyBool))
addSon(notExpr, newSymNode(ast.opNot, n.info))
addSon(notExpr, inExpr) addSon(notExpr, inExpr)
addSon(check, semExpr(c, notExpr)) addSon(check, notExpr)
return return
else: illFormedAst(it) else: illFormedAst(it)
of nkSym: of nkSym:
@ -915,9 +972,10 @@ proc makeDeref(n: PNode): PNode =
t = skipTypes(t.sons[0], {tyGenericInst}) t = skipTypes(t.sons[0], {tyGenericInst})
while t.kind in {tyPtr, tyRef}: while t.kind in {tyPtr, tyRef}:
var a = result var a = result
result = newNodeIT(nkHiddenDeref, n.info, t.sons[0]) let baseTyp = t.lastSon
result = newNodeIT(nkHiddenDeref, n.info, baseTyp)
addSon(result, a) addSon(result, a)
t = skipTypes(t.lastSon, {tyGenericInst}) t = skipTypes(baseTyp, {tyGenericInst})
const const
tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass} tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass}
@ -926,8 +984,8 @@ const
proc readTypeParameter(c: PContext, typ: PType, proc readTypeParameter(c: PContext, typ: PType,
paramName: PIdent, info: TLineInfo): PNode = paramName: PIdent, info: TLineInfo): PNode =
let ty = if typ.kind == tyGenericInst: typ.skipGenericAlias let ty = if typ.kind == tyGenericInst: typ.skipGenericAlias
else: (internalAssert(typ.kind == tyCompositeTypeClass); typ.sons[1]) else: (internalAssert(typ.kind == tyCompositeTypeClass);
#debug ty typ.sons[1].skipGenericAlias)
let tbody = ty.sons[0] let tbody = ty.sons[0]
for s in countup(0, tbody.len-2): for s in countup(0, tbody.len-2):
let tParam = tbody.sons[s] let tParam = tbody.sons[s]
@ -985,7 +1043,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
of tyTypeParamsHolders: of tyTypeParamsHolders:
return readTypeParameter(c, ty, i, n.info) return readTypeParameter(c, ty, i, n.info)
of tyObject, tyTuple: of tyObject, tyTuple:
if ty.n.kind == nkRecList: if ty.n != nil and ty.n.kind == nkRecList:
for field in ty.n: for field in ty.n:
if field.sym.name == i: if field.sym.name == i:
n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.sym.typ]) n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.sym.typ])
@ -1053,7 +1111,7 @@ proc dotTransformation(c: PContext, n: PNode): PNode =
proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
# this is difficult, because the '.' is used in many different contexts # this is difficult, because the '.' is used in many different contexts
# in Nimrod. We first allow types in the semantic checking. # in Nim. We first allow types in the semantic checking.
result = builtinFieldAccess(c, n, flags) result = builtinFieldAccess(c, n, flags)
if result == nil: if result == nil:
result = dotTransformation(c, n) result = dotTransformation(c, n)
@ -1077,19 +1135,20 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
## returns nil if not a built-in subscript operator; also called for the ## returns nil if not a built-in subscript operator; also called for the
## checking of assignments ## checking of assignments
if sonsLen(n) == 1: if sonsLen(n) == 1:
var x = semDeref(c, n) let x = semDeref(c, n)
if x == nil: return nil if x == nil: return nil
result = newNodeIT(nkDerefExpr, x.info, x.typ) result = newNodeIT(nkDerefExpr, x.info, x.typ)
result.add(x[0]) result.add(x[0])
return return
checkMinSonsLen(n, 2) checkMinSonsLen(n, 2)
n.sons[0] = semExprWithType(c, n.sons[0]) n.sons[0] = semExprWithType(c, n.sons[0])
var arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef}) let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef})
case arr.kind case arr.kind
of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, tySequence, tyString, of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, tySequence, tyString,
tyCString: tyCString:
if n.len != 2: return nil if n.len != 2: return nil
n.sons[0] = makeDeref(n.sons[0]) n.sons[0] = makeDeref(n.sons[0])
c.p.bracketExpr = n.sons[0]
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
n.sons[i] = semExprWithType(c, n.sons[i], n.sons[i] = semExprWithType(c, n.sons[i],
flags*{efInTypeof, efDetermineType}) flags*{efInTypeof, efDetermineType})
@ -1110,6 +1169,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
of tyTuple: of tyTuple:
checkSonsLen(n, 2) checkSonsLen(n, 2)
n.sons[0] = makeDeref(n.sons[0]) n.sons[0] = makeDeref(n.sons[0])
c.p.bracketExpr = n.sons[0]
# [] operator for tuples requires constant expression: # [] operator for tuples requires constant expression:
n.sons[1] = semConstExpr(c, n.sons[1]) n.sons[1] = semConstExpr(c, n.sons[1])
if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal}).kind in if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal}).kind in
@ -1120,13 +1180,16 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
else: else:
localError(n.info, errIndexTypesDoNotMatch) localError(n.info, errIndexTypesDoNotMatch)
result = n result = n
else: discard else:
c.p.bracketExpr = n.sons[0]
proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
let oldBracketExpr = c.p.bracketExpr
result = semSubscript(c, n, flags) result = semSubscript(c, n, flags)
if result == nil: if result == nil:
# overloaded [] operator: # overloaded [] operator:
result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]")) result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]"))
c.p.bracketExpr = oldBracketExpr
proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
var id = considerQuotedIdent(a[1]) var id = considerQuotedIdent(a[1])
@ -1167,6 +1230,7 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
if x.typ.kind == tyVar and x.kind == nkSym and x.sym.kind == skResult: if x.typ.kind == tyVar and x.kind == nkSym and x.sym.kind == skResult:
n.sons[0] = x # 'result[]' --> 'result' n.sons[0] = x # 'result[]' --> 'result'
n.sons[1] = takeImplicitAddr(c, ri) n.sons[1] = takeImplicitAddr(c, ri)
x.typ.flags.incl tfVarIsPtr
template resultTypeIsInferrable(typ: PType): expr = template resultTypeIsInferrable(typ: PType): expr =
typ.isMetaType and typ.kind != tyTypeDesc typ.isMetaType and typ.kind != tyTypeDesc
@ -1192,11 +1256,15 @@ proc semAsgn(c: PContext, n: PNode): PNode =
of nkBracketExpr: of nkBracketExpr:
# a[i] = x # a[i] = x
# --> `[]=`(a, i, x) # --> `[]=`(a, i, x)
let oldBracketExpr = c.p.bracketExpr
a = semSubscript(c, a, {efLValue}) a = semSubscript(c, a, {efLValue})
if a == nil: if a == nil:
result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=") result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=")
add(result, n[1]) add(result, n[1])
return semExprNoType(c, result) result = semExprNoType(c, result)
c.p.bracketExpr = oldBracketExpr
return result
c.p.bracketExpr = oldBracketExpr
of nkCurlyExpr: of nkCurlyExpr:
# a{i} = x --> `{}=`(a, i, x) # a{i} = x --> `{}=`(a, i, x)
result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=") result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=")
@ -1232,6 +1300,9 @@ proc semAsgn(c: PContext, n: PNode): PNode =
typeMismatch(n, lhs.typ, rhs.typ) typeMismatch(n, lhs.typ, rhs.typ)
n.sons[1] = fitNode(c, le, rhs) n.sons[1] = fitNode(c, le, rhs)
if tfHasAsgn in lhs.typ.flags and not lhsIsResult:
return overloadedAsgn(c, lhs, n.sons[1])
fixAbstractType(c, n) fixAbstractType(c, n)
asgnToResultVar(c, n, n.sons[0], n.sons[1]) asgnToResultVar(c, n, n.sons[0], n.sons[1])
result = n result = n
@ -1285,7 +1356,12 @@ proc semProcBody(c: PContext, n: PNode): PNode =
if c.p.owner.kind notin {skMacro, skTemplate} and if c.p.owner.kind notin {skMacro, skTemplate} and
c.p.resultSym != nil and c.p.resultSym.typ.isMetaType: c.p.resultSym != nil and c.p.resultSym.typ.isMetaType:
localError(c.p.resultSym.info, errCannotInferReturnType) if isEmptyType(result.typ):
# we inferred a 'void' return type:
c.p.resultSym.typ = nil
c.p.owner.typ.sons[0] = nil
else:
localError(c.p.resultSym.info, errCannotInferReturnType)
closeScope(c) closeScope(c)
@ -1318,17 +1394,18 @@ proc semYield(c: PContext, n: PNode): PNode =
elif n.sons[0].kind != nkEmpty: elif n.sons[0].kind != nkEmpty:
n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility: n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility:
var iterType = c.p.owner.typ var iterType = c.p.owner.typ
var restype = iterType.sons[0] let restype = iterType.sons[0]
if restype != nil: if restype != nil:
let adjustedRes = if c.p.owner.kind == skIterator: restype.base let adjustedRes = if restype.kind == tyIter: restype.base
else: restype else: restype
n.sons[0] = fitNode(c, adjustedRes, n.sons[0]) if adjustedRes.kind != tyExpr:
n.sons[0] = fitNode(c, adjustedRes, n.sons[0])
if n.sons[0].typ == nil: internalError(n.info, "semYield") if n.sons[0].typ == nil: internalError(n.info, "semYield")
if resultTypeIsInferrable(adjustedRes): if resultTypeIsInferrable(adjustedRes):
let inferred = n.sons[0].typ let inferred = n.sons[0].typ
if c.p.owner.kind == skIterator: if restype.kind == tyIter:
iterType.sons[0].sons[0] = inferred restype.sons[0] = inferred
else: else:
iterType.sons[0] = inferred iterType.sons[0] = inferred
@ -1378,10 +1455,6 @@ proc semDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PNode =
localError(n.info, "obsolete usage of 'defined', use 'declared' instead") localError(n.info, "obsolete usage of 'defined', use 'declared' instead")
elif condsyms.isDefined(n.sons[1].ident): elif condsyms.isDefined(n.sons[1].ident):
result.intVal = 1 result.intVal = 1
elif not condsyms.isDeclared(n.sons[1].ident):
message(n.info, warnUser,
"undeclared conditional symbol; use --symbol to declare it: " &
n[1].ident.s)
elif lookUpForDefined(c, n.sons[1], onlyCurrentScope) != nil: elif lookUpForDefined(c, n.sons[1], onlyCurrentScope) != nil:
result.intVal = 1 result.intVal = 1
result.info = n.info result.info = n.info
@ -1453,7 +1526,9 @@ proc semExpandToAst(c: PContext, n: PNode): PNode =
# Preserve the magic symbol in order to be handled in evals.nim # Preserve the magic symbol in order to be handled in evals.nim
internalAssert n.sons[0].sym.magic == mExpandToAst internalAssert n.sons[0].sym.magic == mExpandToAst
n.typ = getSysSym("PNimrodNode").typ # expandedSym.getReturnType #n.typ = getSysSym("PNimrodNode").typ # expandedSym.getReturnType
n.typ = if getCompilerProc("NimNode") != nil: sysTypeFromName"NimNode"
else: sysTypeFromName"PNimrodNode"
result = n result = n
proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym, proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym,
@ -1581,7 +1656,7 @@ proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = semDirectOp(c, n, flags) result = semDirectOp(c, n, flags)
proc createFlowVar(c: PContext; t: PType; info: TLineInfo): PType = proc createFlowVar(c: PContext; t: PType; info: TLineInfo): PType =
result = newType(tyGenericInvokation, c.module) result = newType(tyGenericInvocation, c.module)
addSonSkipIntLit(result, magicsys.getCompilerProc("FlowVar").typ) addSonSkipIntLit(result, magicsys.getCompilerProc("FlowVar").typ)
addSonSkipIntLit(result, t) addSonSkipIntLit(result, t)
result = instGenericContainer(c, info, result, allowMetaTypes = false) result = instGenericContainer(c, info, result, allowMetaTypes = false)
@ -1611,6 +1686,12 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
# DON'T forget to update ast.SpecialSemMagics if you add a magic here! # DON'T forget to update ast.SpecialSemMagics if you add a magic here!
result = n result = n
case s.magic # magics that need special treatment case s.magic # magics that need special treatment
of mAddr:
checkSonsLen(n, 2)
result = semAddr(c, n.sons[1])
of mTypeOf:
checkSonsLen(n, 2)
result = semTypeOf(c, n.sons[1])
of mDefined: result = semDefined(c, setMs(n, s), false) of mDefined: result = semDefined(c, setMs(n, s), false)
of mDefinedInScope: result = semDefined(c, setMs(n, s), true) of mDefinedInScope: result = semDefined(c, setMs(n, s), true)
of mCompiles: result = semCompiles(c, setMs(n, s), flags) of mCompiles: result = semCompiles(c, setMs(n, s), flags)
@ -1805,6 +1886,14 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
addSonSkipIntLit(typ, n.sons[i].typ) addSonSkipIntLit(typ, n.sons[i].typ)
result.typ = typ result.typ = typ
proc isTupleType(n: PNode): bool =
if n.len == 0:
return false # don't interpret () as type
for i in countup(0, n.len - 1):
if n[i].typ == nil or n[i].typ.kind != tyTypeDesc:
return false
return true
proc checkInitialized(n: PNode, ids: IntSet, info: TLineInfo) = proc checkInitialized(n: PNode, ids: IntSet, info: TLineInfo) =
case n.kind case n.kind
of nkRecList: of nkRecList:
@ -1820,7 +1909,8 @@ proc checkInitialized(n: PNode, ids: IntSet, info: TLineInfo) =
of nkOfBranch, nkElse: checkInitialized(lastSon(n.sons[i]), ids, info) of nkOfBranch, nkElse: checkInitialized(lastSon(n.sons[i]), ids, info)
else: internalError(info, "checkInitialized") else: internalError(info, "checkInitialized")
of nkSym: of nkSym:
if tfNeedsInit in n.sym.typ.flags and n.sym.name.id notin ids: if {tfNotNil, tfNeedsInit} * n.sym.typ.flags != {} and
n.sym.name.id notin ids:
message(info, errGenerated, "field not initialized: " & n.sym.name.s) message(info, errGenerated, "field not initialized: " & n.sym.name.s)
else: internalError(info, "checkInitialized") else: internalError(info, "checkInitialized")
@ -1838,12 +1928,11 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
var ids = initIntSet() var ids = initIntSet()
for i in 1.. <n.len: for i in 1.. <n.len:
let it = n.sons[i] let it = n.sons[i]
if it.kind != nkExprColonExpr or it.sons[0].kind notin {nkSym, nkIdent}: if it.kind != nkExprColonExpr:
localError(n.info, errNamedExprExpected) localError(n.info, errNamedExprExpected)
break break
var id: PIdent let id = considerQuotedIdent(it.sons[0])
if it.sons[0].kind == nkIdent: id = it.sons[0].ident
else: id = it.sons[0].sym.name
if containsOrIncl(ids, id.id): if containsOrIncl(ids, id.id):
localError(it.info, errFieldInitTwice, id.s) localError(it.info, errFieldInitTwice, id.s)
var e = semExprWithType(c, it.sons[1], flags*{efAllowDestructor}) var e = semExprWithType(c, it.sons[1], flags*{efAllowDestructor})
@ -1861,7 +1950,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
it.sons[0] = newSymNode(f) it.sons[0] = newSymNode(f)
e = fitNode(c, f.typ, e) e = fitNode(c, f.typ, e)
# small hack here in a nkObjConstr the ``nkExprColonExpr`` node can have # small hack here in a nkObjConstr the ``nkExprColonExpr`` node can have
# 3 childen the last being the field check # 3 children the last being the field check
if check != nil: if check != nil:
check.sons[0] = it.sons[0] check.sons[0] = it.sons[0]
it.add(check) it.add(check)
@ -1937,7 +2026,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
case n.kind case n.kind
of nkIdent, nkAccQuoted: of nkIdent, nkAccQuoted:
var s = lookUp(c, n) var s = lookUp(c, n)
semCaptureSym(s, c.p.owner) if c.inTypeClass == 0: semCaptureSym(s, c.p.owner)
result = semSym(c, n, s, flags) result = semSym(c, n, s, flags)
if s.kind in {skProc, skMethod, skConverter}+skIterators: if s.kind in {skProc, skMethod, skConverter}+skIterators:
#performProcvarCheck(c, n, s) #performProcvarCheck(c, n, s)
@ -1945,7 +2034,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
if result.kind == nkSym: if result.kind == nkSym:
markIndirect(c, result.sym) markIndirect(c, result.sym)
# if isGenericRoutine(result.sym): # if isGenericRoutine(result.sym):
# localError(n.info, errInstantiateXExplicitely, s.name.s) # localError(n.info, errInstantiateXExplicitly, s.name.s)
of nkSym: of nkSym:
# because of the changed symbol binding, this does not mean that we # because of the changed symbol binding, this does not mean that we
# don't have to check the symbol for semantics here again! # don't have to check the symbol for semantics here again!
@ -1994,7 +2083,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkBind: of nkBind:
message(n.info, warnDeprecated, "bind") message(n.info, warnDeprecated, "bind")
result = semExpr(c, n.sons[0], flags) result = semExpr(c, n.sons[0], flags)
of nkTypeOfExpr, nkTupleTy, nkRefTy..nkEnumTy, nkStaticTy: of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy:
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter}) var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter})
result.typ = makeTypeDesc(c, typ) result.typ = makeTypeDesc(c, typ)
#result = symNodeFromType(c, typ, n.info) #result = symNodeFromType(c, typ, n.info)
@ -2068,21 +2157,26 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkPar: of nkPar:
case checkPar(n) case checkPar(n)
of paNone: result = errorNode(c, n) of paNone: result = errorNode(c, n)
of paTuplePositions: result = semTuplePositionsConstr(c, n, flags) of paTuplePositions:
var tupexp = semTuplePositionsConstr(c, n, flags)
if isTupleType(tupexp):
# reinterpret as type
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter})
result.typ = makeTypeDesc(c, typ)
else:
result = tupexp
of paTupleFields: result = semTupleFieldsConstr(c, n, flags) of paTupleFields: result = semTupleFieldsConstr(c, n, flags)
of paSingle: result = semExpr(c, n.sons[0], flags) of paSingle: result = semExpr(c, n.sons[0], flags)
of nkCurly: result = semSetConstr(c, n) of nkCurly: result = semSetConstr(c, n)
of nkBracket: result = semArrayConstr(c, n, flags) of nkBracket: result = semArrayConstr(c, n, flags)
of nkObjConstr: result = semObjConstr(c, n, flags) of nkObjConstr: result = semObjConstr(c, n, flags)
of nkLambdaKinds: result = semLambda(c, n, flags) of nkLambda: result = semLambda(c, n, flags)
of nkDo: result = semDo(c, n, flags)
of nkDerefExpr: result = semDeref(c, n) of nkDerefExpr: result = semDeref(c, n)
of nkAddr: of nkAddr:
result = n result = n
checkSonsLen(n, 1) checkSonsLen(n, 1)
n.sons[0] = semExprWithType(c, n.sons[0]) result = semAddr(c, n.sons[0])
if isAssignable(c, n.sons[0]) notin {arLValue, arLocalLValue}:
localError(n.info, errExprHasNoAddress)
n.typ = makePtrType(c, n.sons[0].typ)
of nkHiddenAddr, nkHiddenDeref: of nkHiddenAddr, nkHiddenDeref:
checkSonsLen(n, 1) checkSonsLen(n, 1)
n.sons[0] = semExpr(c, n.sons[0], flags) n.sons[0] = semExpr(c, n.sons[0], flags)
@ -2148,6 +2242,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = semPragmaBlock(c, n) result = semPragmaBlock(c, n)
of nkStaticStmt: of nkStaticStmt:
result = semStaticStmt(c, n) result = semStaticStmt(c, n)
of nkDefer:
localError(n.info, errGenerated, "'defer' not allowed in this context")
else: else:
localError(n.info, errInvalidExpressionX, localError(n.info, errInvalidExpressionX,
renderTree(n, {renderNoComments})) renderTree(n, {renderNoComments}))

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -19,12 +19,13 @@ type
proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode = proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
case n.kind case n.kind
of nkEmpty..pred(nkIdent), succ(nkIdent)..nkNilLit: result = n of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = n
of nkIdent: of nkIdent, nkSym:
result = n result = n
let ident = considerQuotedIdent(n)
var L = sonsLen(forLoop) var L = sonsLen(forLoop)
if c.replaceByFieldName: if c.replaceByFieldName:
if n.ident.id == forLoop[0].ident.id: if ident.id == considerQuotedIdent(forLoop[0]).id:
let fieldName = if c.tupleType.isNil: c.field.name.s let fieldName = if c.tupleType.isNil: c.field.name.s
elif c.tupleType.n.isNil: "Field" & $c.tupleIndex elif c.tupleType.n.isNil: "Field" & $c.tupleIndex
else: c.tupleType.n.sons[c.tupleIndex].sym.name.s else: c.tupleType.n.sons[c.tupleIndex].sym.name.s
@ -32,7 +33,7 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
return return
# other fields: # other fields:
for i in ord(c.replaceByFieldName)..L-3: for i in ord(c.replaceByFieldName)..L-3:
if n.ident.id == forLoop[i].ident.id: if ident.id == considerQuotedIdent(forLoop[i]).id:
var call = forLoop.sons[L-2] var call = forLoop.sons[L-2]
var tupl = call.sons[i+1-ord(c.replaceByFieldName)] var tupl = call.sons[i+1-ord(c.replaceByFieldName)]
if c.field.isNil: if c.field.isNil:
@ -98,7 +99,7 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
of nkRecList: of nkRecList:
for t in items(typ): semForObjectFields(c, t, forLoop, father) for t in items(typ): semForObjectFields(c, t, forLoop, father)
else: else:
illFormedAst(typ) illFormedAstLocal(typ)
proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
# so that 'break' etc. work as expected, we produce # so that 'break' etc. work as expected, we produce
@ -155,7 +156,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
dec(c.p.nestedLoopCounter) dec(c.p.nestedLoopCounter)
# for TR macros this 'while true: ...; break' loop is pretty bad, so # for TR macros this 'while true: ...; break' loop is pretty bad, so
# we avoid it now if we can: # we avoid it now if we can:
if hasSonWith(stmts, nkBreakStmt): if containsNode(stmts, {nkBreakStmt}):
var b = newNodeI(nkBreakStmt, n.info) var b = newNodeI(nkBreakStmt, n.info)
b.add(ast.emptyNode) b.add(ast.emptyNode)
stmts.add(b) stmts.add(b)

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -118,6 +118,9 @@ proc makeRange(typ: PType, first, last: BiggestInt): PType =
let lowerNode = newIntNode(nkIntLit, minA) let lowerNode = newIntNode(nkIntLit, minA)
if typ.kind == tyInt and minA == maxA: if typ.kind == tyInt and minA == maxA:
result = getIntLitType(lowerNode) result = getIntLitType(lowerNode)
elif typ.kind in {tyUint, tyUInt64}:
# these are not ordinal types, so you get no subrange type for these:
result = typ
else: else:
var n = newNode(nkRange) var n = newNode(nkRange)
addSon(n, lowerNode) addSon(n, lowerNode)
@ -135,8 +138,9 @@ proc makeRangeF(typ: PType, first, last: BiggestFloat): PType =
addSonSkipIntLit(result, skipTypes(typ, {tyRange})) addSonSkipIntLit(result, skipTypes(typ, {tyRange}))
proc getIntervalType*(m: TMagic, n: PNode): PType = proc getIntervalType*(m: TMagic, n: PNode): PType =
# Nimrod requires interval arithmetic for ``range`` types. Lots of tedious # Nim requires interval arithmetic for ``range`` types. Lots of tedious
# work but the feature is very nice for reducing explicit conversions. # work but the feature is very nice for reducing explicit conversions.
const ordIntLit = {nkIntLit..nkUInt64Lit}
result = n.typ result = n.typ
template commutativeOp(opr: expr) {.immediate.} = template commutativeOp(opr: expr) {.immediate.} =
@ -170,13 +174,19 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
let a = n.sons[1].typ let a = n.sons[1].typ
if isFloatRange(a): if isFloatRange(a):
# abs(-5.. 1) == (1..5) # abs(-5.. 1) == (1..5)
result = makeRangeF(a, abs(getFloat(a.n.sons[1])), if a.n[0].floatVal <= 0.0:
abs(getFloat(a.n.sons[0]))) result = makeRangeF(a, 0.0, abs(getFloat(a.n.sons[0])))
else:
result = makeRangeF(a, abs(getFloat(a.n.sons[1])),
abs(getFloat(a.n.sons[0])))
of mAbsI, mAbsI64: of mAbsI, mAbsI64:
let a = n.sons[1].typ let a = n.sons[1].typ
if isIntRange(a): if isIntRange(a):
result = makeRange(a, `|abs|`(getInt(a.n.sons[1])), if a.n[0].intVal <= 0:
`|abs|`(getInt(a.n.sons[0]))) result = makeRange(a, 0, `|abs|`(getInt(a.n.sons[0])))
else:
result = makeRange(a, `|abs|`(getInt(a.n.sons[1])),
`|abs|`(getInt(a.n.sons[0])))
of mSucc: of mSucc:
let a = n.sons[1].typ let a = n.sons[1].typ
let b = n.sons[2].typ let b = n.sons[2].typ
@ -202,15 +212,15 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
var a = n.sons[1] var a = n.sons[1]
var b = n.sons[2] var b = n.sons[2]
# symmetrical: # symmetrical:
if b.kind notin {nkIntLit..nkUInt32Lit}: swap(a, b) if b.kind notin ordIntLit: swap(a, b)
if b.kind in {nkIntLit..nkUInt32Lit}: if b.kind in ordIntLit:
let x = b.intVal|+|1 let x = b.intVal|+|1
if (x and -x) == x and x >= 0: if (x and -x) == x and x >= 0:
result = makeRange(a.typ, 0, b.intVal) result = makeRange(a.typ, 0, b.intVal)
of mModU: of mModU:
let a = n.sons[1] let a = n.sons[1]
let b = n.sons[2] let b = n.sons[2]
if b.kind in {nkIntLit..nkUInt32Lit}: if a.kind in ordIntLit:
if b.intVal >= 0: if b.intVal >= 0:
result = makeRange(a.typ, 0, b.intVal-1) result = makeRange(a.typ, 0, b.intVal-1)
else: else:
@ -226,9 +236,9 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
result = makeRange(a.typ, b.intVal+1, -(b.intVal+1)) result = makeRange(a.typ, b.intVal+1, -(b.intVal+1))
of mDivI, mDivI64, mDivU: of mDivI, mDivI64, mDivU:
binaryOp(`|div|`) binaryOp(`|div|`)
of mMinI, mMinI64: of mMinI:
commutativeOp(min) commutativeOp(min)
of mMaxI, mMaxI64: of mMaxI:
commutativeOp(max) commutativeOp(max)
else: discard else: discard
@ -276,10 +286,11 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mNot: result = newIntNodeT(1 - getInt(a), n) of mNot: result = newIntNodeT(1 - getInt(a), n)
of mCard: result = newIntNodeT(nimsets.cardSet(a), n) of mCard: result = newIntNodeT(nimsets.cardSet(a), n)
of mBitnotI, mBitnotI64: result = newIntNodeT(not getInt(a), n) of mBitnotI, mBitnotI64: result = newIntNodeT(not getInt(a), n)
of mLengthStr: result = newIntNodeT(len(getStr(a)), n) of mLengthStr, mXLenStr: result = newIntNodeT(len(getStr(a)), n)
of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), n) of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), n)
of mLengthSeq, mLengthOpenArray: result = newIntNodeT(sonsLen(a), n) # BUGFIX of mLengthSeq, mLengthOpenArray, mXLenSeq:
of mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: result = a # throw `+` away result = newIntNodeT(sonsLen(a), n) # BUGFIX
of mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away
of mToFloat, mToBiggestFloat: of mToFloat, mToBiggestFloat:
result = newFloatNodeT(toFloat(int(getInt(a))), n) result = newFloatNodeT(toFloat(int(getInt(a))), n)
of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n) of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n)
@ -299,10 +310,10 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mAddI, mAddI64: result = newIntNodeT(getInt(a) + getInt(b), n) of mAddI, mAddI64: result = newIntNodeT(getInt(a) + getInt(b), n)
of mSubI, mSubI64: result = newIntNodeT(getInt(a) - getInt(b), n) of mSubI, mSubI64: result = newIntNodeT(getInt(a) - getInt(b), n)
of mMulI, mMulI64: result = newIntNodeT(getInt(a) * getInt(b), n) of mMulI, mMulI64: result = newIntNodeT(getInt(a) * getInt(b), n)
of mMinI, mMinI64: of mMinI:
if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n) if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n)
else: result = newIntNodeT(getInt(a), n) else: result = newIntNodeT(getInt(a), n)
of mMaxI, mMaxI64: of mMaxI:
if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n) if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n)
else: result = newIntNodeT(getInt(b), n) else: result = newIntNodeT(getInt(b), n)
of mShlI, mShlI64: of mShlI, mShlI64:
@ -419,8 +430,9 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mNewString, mNewStringOfCap, of mNewString, mNewStringOfCap,
mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh, mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh,
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, mDotDot,
mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn, mParallel: mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn,
mParallel, mPlugin:
discard discard
else: internalError(a.info, "evalOp(" & $m & ')') else: internalError(a.info, "evalOp(" & $m & ')')
@ -513,7 +525,7 @@ proc rangeCheck(n: PNode, value: BiggestInt) =
proc foldConv*(n, a: PNode; check = false): PNode = proc foldConv*(n, a: PNode; check = false): PNode =
# XXX range checks? # XXX range checks?
case skipTypes(n.typ, abstractRange).kind case skipTypes(n.typ, abstractRange).kind
of tyInt..tyInt64: of tyInt..tyInt64, tyUInt..tyUInt64:
case skipTypes(a.typ, abstractRange).kind case skipTypes(a.typ, abstractRange).kind
of tyFloat..tyFloat64: of tyFloat..tyFloat64:
result = newIntNodeT(int(getFloat(a)), n) result = newIntNodeT(int(getFloat(a)), n)
@ -533,7 +545,7 @@ proc foldConv*(n, a: PNode; check = false): PNode =
discard discard
else: else:
result = a result = a
result.typ = n.typ result.typ = takeType(n.typ, a.typ)
proc getArrayConstr(m: PSym, n: PNode): PNode = proc getArrayConstr(m: PSym, n: PNode): PNode =
if n.kind == nkBracket: if n.kind == nkBracket:
@ -665,8 +677,8 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
of mLow: of mLow:
result = newIntNodeT(firstOrd(n.sons[1].typ), n) result = newIntNodeT(firstOrd(n.sons[1].typ), n)
of mHigh: of mHigh:
if skipTypes(n.sons[1].typ, abstractVar).kind notin if skipTypes(n.sons[1].typ, abstractVar).kind notin
{tyOpenArray, tyVarargs, tySequence, tyString}: {tySequence, tyString, tyCString, tyOpenArray, tyVarargs}:
result = newIntNodeT(lastOrd(skipTypes(n[1].typ, abstractVar)), n) result = newIntNodeT(lastOrd(skipTypes(n[1].typ, abstractVar)), n)
else: else:
var a = getArrayConstr(m, n.sons[1]) var a = getArrayConstr(m, n.sons[1])

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -26,9 +26,22 @@ proc getIdentNode(n: PNode): PNode =
illFormedAst(n) illFormedAst(n)
result = n result = n
type
GenericCtx = object
toMixin: IntSet
cursorInBody: bool # only for nimsuggest
type
TSemGenericFlag = enum
withinBind, withinTypeDesc, withinMixin
TSemGenericFlags = set[TSemGenericFlag]
proc semGenericStmt(c: PContext, n: PNode,
flags: TSemGenericFlags, ctx: var GenericCtx): PNode
proc semGenericStmtScope(c: PContext, n: PNode, proc semGenericStmtScope(c: PContext, n: PNode,
flags: TSemGenericFlags, flags: TSemGenericFlags,
ctx: var IntSet): PNode = ctx: var GenericCtx): PNode =
openScope(c) openScope(c)
result = semGenericStmt(c, n, flags, ctx) result = semGenericStmt(c, n, flags, ctx)
closeScope(c) closeScope(c)
@ -37,7 +50,8 @@ template macroToExpand(s: expr): expr =
s.kind in {skMacro, skTemplate} and (s.typ.len == 1 or sfImmediate in s.flags) s.kind in {skMacro, skTemplate} and (s.typ.len == 1 or sfImmediate in s.flags)
proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
ctx: var IntSet): PNode = ctx: var GenericCtx): PNode =
semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
case s.kind case s.kind
of skUnknown: of skUnknown:
@ -60,13 +74,20 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
else: else:
result = symChoice(c, n, s, scOpen) result = symChoice(c, n, s, scOpen)
of skGenericParam: of skGenericParam:
result = newSymNodeTypeDesc(s, n.info) if s.typ != nil and s.typ.kind == tyStatic:
if s.typ.n != nil:
result = s.typ.n
else:
result = n
else:
result = newSymNodeTypeDesc(s, n.info)
styleCheckUse(n.info, s) styleCheckUse(n.info, s)
of skParam: of skParam:
result = n result = n
styleCheckUse(n.info, s) styleCheckUse(n.info, s)
of skType: of skType:
if (s.typ != nil) and (s.typ.kind != tyGenericParam): if (s.typ != nil) and
(s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}):
result = newSymNodeTypeDesc(s, n.info) result = newSymNodeTypeDesc(s, n.info)
else: else:
result = n result = n
@ -76,17 +97,17 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
styleCheckUse(n.info, s) styleCheckUse(n.info, s)
proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
ctx: var IntSet): PNode = ctx: var GenericCtx): PNode =
result = n result = n
let ident = considerQuotedIdent(n) let ident = considerQuotedIdent(n)
var s = searchInScopes(c, ident).skipAlias(n) var s = searchInScopes(c, ident).skipAlias(n)
if s == nil: if s == nil:
if ident.id notin ctx and withinMixin notin flags: if ident.id notin ctx.toMixin and withinMixin notin flags:
localError(n.info, errUndeclaredIdentifier, ident.s) localError(n.info, errUndeclaredIdentifier, ident.s)
else: else:
if withinBind in flags: if withinBind in flags:
result = symChoice(c, n, s, scClosed) result = symChoice(c, n, s, scClosed)
elif s.name.id in ctx: elif s.name.id in ctx.toMixin:
result = symChoice(c, n, s, scForceOpen) result = symChoice(c, n, s, scForceOpen)
else: else:
result = semGenericStmtSymbol(c, n, s, ctx) result = semGenericStmtSymbol(c, n, s, ctx)
@ -98,8 +119,10 @@ proc newDot(n, b: PNode): PNode =
result.add(b) result.add(b)
proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
ctx: var IntSet; isMacro: var bool): PNode = ctx: var GenericCtx; isMacro: var bool): PNode =
assert n.kind == nkDotExpr assert n.kind == nkDotExpr
semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
let luf = if withinMixin notin flags: {checkUndeclared} else: {} let luf = if withinMixin notin flags: {checkUndeclared} else: {}
var s = qualifiedLookUp(c, n, luf) var s = qualifiedLookUp(c, n, luf)
@ -115,7 +138,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
isMacro = s.kind in {skTemplate, skMacro} isMacro = s.kind in {skTemplate, skMacro}
if withinBind in flags: if withinBind in flags:
result = newDot(result, symChoice(c, n, s, scClosed)) result = newDot(result, symChoice(c, n, s, scClosed))
elif s.name.id in ctx: elif s.name.id in ctx.toMixin:
result = newDot(result, symChoice(c, n, s, scForceOpen)) result = newDot(result, symChoice(c, n, s, scForceOpen))
else: else:
let sym = semGenericStmtSymbol(c, n, s, ctx) let sym = semGenericStmtSymbol(c, n, s, ctx)
@ -130,9 +153,11 @@ proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) =
styleCheckDef(n.info, s, kind) styleCheckDef(n.info, s, kind)
proc semGenericStmt(c: PContext, n: PNode, proc semGenericStmt(c: PContext, n: PNode,
flags: TSemGenericFlags, ctx: var IntSet): PNode = flags: TSemGenericFlags, ctx: var GenericCtx): PNode =
result = n result = n
if gCmd == cmdIdeTools: suggestStmt(c, n) #if gCmd == cmdIdeTools: suggestStmt(c, n)
semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
case n.kind case n.kind
of nkIdent, nkAccQuoted: of nkIdent, nkAccQuoted:
result = lookup(c, n, flags, ctx) result = lookup(c, n, flags, ctx)
@ -155,14 +180,15 @@ proc semGenericStmt(c: PContext, n: PNode,
of nkBind: of nkBind:
result = semGenericStmt(c, n.sons[0], flags+{withinBind}, ctx) result = semGenericStmt(c, n.sons[0], flags+{withinBind}, ctx)
of nkMixinStmt: of nkMixinStmt:
result = semMixinStmt(c, n, ctx) result = semMixinStmt(c, n, ctx.toMixin)
of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit: of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit:
# check if it is an expression macro: # check if it is an expression macro:
checkMinSonsLen(n, 1) checkMinSonsLen(n, 1)
let fn = n.sons[0] let fn = n.sons[0]
var s = qualifiedLookUp(c, fn, {}) var s = qualifiedLookUp(c, fn, {})
if s == nil and withinMixin notin flags and if s == nil and withinMixin notin flags and
fn.kind in {nkIdent, nkAccQuoted} and considerQuotedIdent(fn).id notin ctx: fn.kind in {nkIdent, nkAccQuoted} and
considerQuotedIdent(fn).id notin ctx.toMixin:
localError(n.info, errUndeclaredIdentifier, fn.renderTree) localError(n.info, errUndeclaredIdentifier, fn.renderTree)
var first = 0 var first = 0
@ -170,7 +196,7 @@ proc semGenericStmt(c: PContext, n: PNode,
if s != nil: if s != nil:
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles} mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles}
let scOption = if s.name.id in ctx: scForceOpen else: scOpen let scOption = if s.name.id in ctx.toMixin: scForceOpen else: scOpen
case s.kind case s.kind
of skMacro: of skMacro:
if macroToExpand(s): if macroToExpand(s):
@ -217,7 +243,7 @@ proc semGenericStmt(c: PContext, n: PNode,
elif fn.kind == nkDotExpr: elif fn.kind == nkDotExpr:
result.sons[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext) result.sons[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext)
first = 1 first = 1
# Consider 'when defined(globalsSlot): ThreadVarSetValue(globalsSlot, ...)' # Consider 'when declared(globalsSlot): ThreadVarSetValue(globalsSlot, ...)'
# in threads.nim: the subtle preprocessing here binds 'globalsSlot' which # in threads.nim: the subtle preprocessing here binds 'globalsSlot' which
# is not exported and yet the generic 'threadProcWrapper' works correctly. # is not exported and yet the generic 'threadProcWrapper' works correctly.
let flags = if mixinContext: flags+{withinMixin} else: flags let flags = if mixinContext: flags+{withinMixin} else: flags
@ -330,7 +356,7 @@ proc semGenericStmt(c: PContext, n: PNode,
of nkIdent: a = n.sons[i] of nkIdent: a = n.sons[i]
else: illFormedAst(n) else: illFormedAst(n)
addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c)) addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c))
of nkObjectTy, nkTupleTy: of nkObjectTy, nkTupleTy, nkTupleClassTy:
discard discard
of nkFormalParams: of nkFormalParams:
checkMinSonsLen(n, 1) checkMinSonsLen(n, 1)
@ -371,3 +397,8 @@ proc semGenericStmt(c: PContext, n: PNode,
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
result.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx) result.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx)
proc semGenericStmt(c: PContext, n: PNode): PNode =
var ctx: GenericCtx
ctx.toMixin = initIntset()
result = semGenericStmt(c, n, {}, ctx)
semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)

View file

@ -21,7 +21,8 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
var q = a.sym var q = a.sym
if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses: if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses:
continue continue
var s = newSym(skType, q.name, getCurrOwner(), q.info) let symKind = if q.typ.kind == tyStatic: skConst else: skType
var s = newSym(symKind, q.name, getCurrOwner(), q.info)
s.flags = s.flags + {sfUsed, sfFromGeneric} s.flags = s.flags + {sfUsed, sfFromGeneric}
var t = PType(idTableGet(pt, q.typ)) var t = PType(idTableGet(pt, q.typ))
if t == nil: if t == nil:
@ -35,11 +36,12 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
elif t.kind == tyGenericParam: elif t.kind == tyGenericParam:
localError(a.info, errCannotInstantiateX, q.name.s) localError(a.info, errCannotInstantiateX, q.name.s)
t = errorType(c) t = errorType(c)
elif t.kind == tyGenericInvokation: elif t.kind == tyGenericInvocation:
#t = instGenericContainer(c, a, t) #t = instGenericContainer(c, a, t)
t = generateTypeInstance(c, pt, a, t) t = generateTypeInstance(c, pt, a, t)
#t = ReplaceTypeVarsT(cl, t) #t = ReplaceTypeVarsT(cl, t)
s.typ = t s.typ = t
if t.kind == tyStatic: s.ast = t.n
addDecl(c, s) addDecl(c, s)
entry.concreteTypes[i] = t entry.concreteTypes[i] = t
@ -75,11 +77,12 @@ proc removeDefaultParamValues(n: PNode) =
proc freshGenSyms(n: PNode, owner: PSym, symMap: var TIdTable) = proc freshGenSyms(n: PNode, owner: PSym, symMap: var TIdTable) =
# we need to create a fresh set of gensym'ed symbols: # we need to create a fresh set of gensym'ed symbols:
if n.kind == nkSym and sfGenSym in n.sym.flags: if n.kind == nkSym and sfGenSym in n.sym.flags:
var x = PSym(idTableGet(symMap, n.sym)) let s = n.sym
var x = PSym(idTableGet(symMap, s))
if x == nil: if x == nil:
x = copySym(n.sym, false) x = copySym(s, false)
x.owner = owner x.owner = owner
idTablePut(symMap, n.sym, x) idTablePut(symMap, s, x)
n.sym = x n.sym = x
else: else:
for i in 0 .. <safeLen(n): freshGenSyms(n.sons[i], owner, symMap) for i in 0 .. <safeLen(n): freshGenSyms(n.sons[i], owner, symMap)
@ -97,13 +100,18 @@ proc addProcDecls(c: PContext, fn: PSym) =
maybeAddResult(c, fn, fn.ast) maybeAddResult(c, fn, fn.ast)
proc instantiateBody(c: PContext, n: PNode, result: PSym) = proc instantiateBody(c: PContext, n, params: PNode, result: PSym) =
if n.sons[bodyPos].kind != nkEmpty: if n.sons[bodyPos].kind != nkEmpty:
inc c.inGenericInst inc c.inGenericInst
# add it here, so that recursive generic procs are possible: # add it here, so that recursive generic procs are possible:
var b = n.sons[bodyPos] var b = n.sons[bodyPos]
var symMap: TIdTable var symMap: TIdTable
initIdTable symMap initIdTable symMap
if params != nil:
for i in 1 .. <params.len:
let param = params[i].sym
if sfGenSym in param.flags:
idTablePut(symMap, params[i].sym, result.typ.n[param.position+1].sym)
freshGenSyms(b, result, symMap) freshGenSyms(b, result, symMap)
b = semProcBody(c, b) b = semProcBody(c, b)
b = hloBody(c, b) b = hloBody(c, b)
@ -120,7 +128,7 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
openScope(c) openScope(c)
var n = oldPrc.ast var n = oldPrc.ast
n.sons[bodyPos] = copyTree(s.getBody) n.sons[bodyPos] = copyTree(s.getBody)
instantiateBody(c, n, oldPrc) instantiateBody(c, n, nil, oldPrc)
closeScope(c) closeScope(c)
popInfoContext() popInfoContext()
@ -167,17 +175,32 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
result.n = originalParams.shallowCopy result.n = originalParams.shallowCopy
for i in 1 .. <result.len: for i in 1 .. <result.len:
# twrong_field_caching requires these 'resetIdTable' calls:
if i > 1:
resetIdTable(cl.symMap)
resetIdTable(cl.localCache)
result.sons[i] = replaceTypeVarsT(cl, result.sons[i]) result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
propagateToOwner(result, result.sons[i]) propagateToOwner(result, result.sons[i])
let param = replaceTypeVarsN(cl, originalParams[i]) internalAssert originalParams[i].kind == nkSym
result.n.sons[i] = param when true:
if param.kind == nkSym: let oldParam = originalParams[i].sym
# XXX: this won't be true for void params let param = copySym(oldParam)
# implement pass-through of void params and param.owner = prc
# the "sort by distance to point" container param.typ = result.sons[i]
param.sym.owner = prc if oldParam.ast != nil:
addDecl(c, param.sym) param.ast = fitNode(c, param.typ, oldParam.ast)
# don't be lazy here and call replaceTypeVarsN(cl, originalParams[i])!
result.n.sons[i] = newSymNode(param)
addDecl(c, param)
else:
let param = replaceTypeVarsN(cl, originalParams[i])
result.n.sons[i] = param
param.sym.owner = prc
addDecl(c, result.n.sons[i].sym)
resetIdTable(cl.symMap)
resetIdTable(cl.localCache)
result.sons[0] = replaceTypeVarsT(cl, result.sons[0]) result.sons[0] = replaceTypeVarsT(cl, result.sons[0])
result.n.sons[0] = originalParams[0].copyTree result.n.sons[0] = originalParams[0].copyTree
@ -232,7 +255,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
pragma(c, result, n.sons[pragmasPos], allRoutinePragmas) pragma(c, result, n.sons[pragmasPos], allRoutinePragmas)
if isNil(n.sons[bodyPos]): if isNil(n.sons[bodyPos]):
n.sons[bodyPos] = copyTree(fn.getBody) n.sons[bodyPos] = copyTree(fn.getBody)
instantiateBody(c, n, result) instantiateBody(c, n, fn.typ.n, result)
sideEffectsCheck(c, result) sideEffectsCheck(c, result)
paramsTypeCheck(c, result.typ) paramsTypeCheck(c, result.typ)
else: else:

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -34,7 +34,7 @@ proc ithField(n: PNode, field: int): PSym =
else: discard else: discard
proc annotateType*(n: PNode, t: PType) = proc annotateType*(n: PNode, t: PType) =
let x = t.skipTypes(abstractInst) let x = t.skipTypes(abstractInst+{tyRange})
# Note: x can be unequal to t and we need to be careful to use 't' # Note: x can be unequal to t and we need to be careful to use 't'
# to not to skip tyGenericInst # to not to skip tyGenericInst
case n.kind case n.kind

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -10,6 +10,20 @@
# This include file implements the semantic checking for magics. # This include file implements the semantic checking for magics.
# included from sem.nim # included from sem.nim
proc semAddr(c: PContext; n: PNode): PNode =
result = newNodeI(nkAddr, n.info)
let x = semExprWithType(c, n)
if isAssignable(c, x) notin {arLValue, arLocalLValue}:
localError(n.info, errExprHasNoAddress)
result.add x
result.typ = makePtrType(c, x.typ)
proc semTypeOf(c: PContext; n: PNode): PNode =
result = newNodeI(nkTypeOfExpr, n.info)
let typExpr = semExprWithType(c, n, {efInTypeof})
result.add typExpr
result.typ = makeTypeDesc(c, typExpr.typ.skipTypes({tyTypeDesc, tyIter}))
proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode =
var r = isPartOf(n[1], n[2]) var r = isPartOf(n[1], n[2])
result = newIntNodeT(ord(r), n) result = newIntNodeT(ord(r), n)
@ -40,7 +54,7 @@ proc evalTypeTrait(trait: PNode, operand: PType, context: PSym): PNode =
result.typ = newType(tyString, context) result.typ = newType(tyString, context)
result.info = trait.info result.info = trait.info
of "arity": of "arity":
result = newIntNode(nkIntLit, typ.n.len-1) result = newIntNode(nkIntLit, typ.len - ord(typ.kind==tyProc))
result.typ = newType(tyInt, context) result.typ = newType(tyInt, context)
result.info = trait.info result.info = trait.info
else: else:
@ -87,38 +101,28 @@ proc semBindSym(c: PContext, n: PNode): PNode =
else: else:
localError(n.sons[1].info, errUndeclaredIdentifier, sl.strVal) localError(n.sons[1].info, errUndeclaredIdentifier, sl.strVal)
proc semLocals(c: PContext, n: PNode): PNode =
var counter = 0
var tupleType = newTypeS(tyTuple, c)
result = newNodeIT(nkPar, n.info, tupleType)
tupleType.n = newNodeI(nkRecList, n.info)
# for now we skip openarrays ...
for scope in walkScopes(c.currentScope):
if scope == c.topLevelScope: break
for it in items(scope.symbols):
# XXX parameters' owners are wrong for generics; this caused some pain
# for closures too; we should finally fix it.
#if it.owner != c.p.owner: return result
if it.kind in skLocalVars and
it.typ.skipTypes({tyGenericInst, tyVar}).kind notin
{tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyExpr, tyStmt, tyEmpty}:
var field = newSym(skField, it.name, getCurrOwner(), n.info)
field.typ = it.typ.skipTypes({tyGenericInst, tyVar})
field.position = counter
inc(counter)
addSon(tupleType.n, newSymNode(field))
addSonSkipIntLit(tupleType, field.typ)
var a = newSymNode(it, result.info)
if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a)
result.add(a)
proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
proc isStrangeArray(t: PType): bool =
let t = t.skipTypes(abstractInst)
result = t.kind == tyArray and t.firstOrd != 0
proc isNegative(n: PNode): bool =
let n = n.skipConv
if n.kind in {nkCharLit..nkUInt64Lit}:
result = n.intVal < 0
elif n.kind in nkCallKinds and n.sons[0].kind == nkSym:
result = n.sons[0].sym.magic in {mUnaryMinusI, mUnaryMinusI64}
proc magicsAfterOverloadResolution(c: PContext, n: PNode, proc magicsAfterOverloadResolution(c: PContext, n: PNode,
flags: TExprFlags): PNode = flags: TExprFlags): PNode =
case n[0].sym.magic case n[0].sym.magic
of mAddr:
checkSonsLen(n, 2)
result = semAddr(c, n.sons[1])
of mTypeOf:
checkSonsLen(n, 2)
result = semTypeOf(c, n.sons[1])
of mIsPartOf: result = semIsPartOf(c, n, flags) of mIsPartOf: result = semIsPartOf(c, n, flags)
of mTypeTrait: result = semTypeTraits(c, n) of mTypeTrait: result = semTypeTraits(c, n)
of mAstToStr: of mAstToStr:
@ -129,8 +133,45 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic) of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic)
of mShallowCopy: result = semShallowCopy(c, n, flags) of mShallowCopy: result = semShallowCopy(c, n, flags)
of mNBindSym: result = semBindSym(c, n) of mNBindSym: result = semBindSym(c, n)
of mLocals: result = semLocals(c, n)
of mProcCall: of mProcCall:
result = n result = n
result.typ = n[1].typ result.typ = n[1].typ
of mDotDot:
result = n
# disallow negative indexing for now:
if not c.p.bracketExpr.isNil:
if isNegative(n.sons[1]) or (n.len > 2 and isNegative(n.sons[2])):
localError(n.info, "use '^' instead of '-'; negative indexing is obsolete")
of mRoof:
# error correction:
result = n.sons[1]
if c.p.bracketExpr.isNil:
localError(n.info, "no surrounding array access context for '^'")
elif c.p.bracketExpr.checkForSideEffects != seNoSideEffect:
localError(n.info, "invalid context for '^' as '$#' has side effects" %
renderTree(c.p.bracketExpr))
elif c.p.bracketExpr.typ.isStrangeArray:
localError(n.info, "invalid context for '^' as len!=high+1 for '$#'" %
renderTree(c.p.bracketExpr))
else:
# ^x is rewritten to: len(a)-x
let lenExpr = newNodeI(nkCall, n.info)
lenExpr.add newIdentNode(getIdent"len", n.info)
lenExpr.add c.p.bracketExpr
let lenExprB = semExprWithType(c, lenExpr)
if lenExprB.typ.isNil or not isOrdinalType(lenExprB.typ):
localError(n.info, "'$#' has to be of an ordinal type for '^'" %
renderTree(lenExpr))
else:
result = newNodeIT(nkCall, n.info, getSysType(tyInt))
result.add newSymNode(createMagic("-", mSubI), n.info)
result.add lenExprB
result.add n.sons[1]
of mPlugin:
let plugin = getPlugin(n[0].sym)
if plugin.isNil:
localError(n.info, "cannot find plugin " & n[0].sym.name.s)
result = n
else:
result = plugin(c, n)
else: result = n else: result = n

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -317,8 +317,9 @@ proc analyseIf(c: var AnalysisCtx; n: PNode) =
proc analyse(c: var AnalysisCtx; n: PNode) = proc analyse(c: var AnalysisCtx; n: PNode) =
case n.kind case n.kind
of nkAsgn, nkFastAsgn: of nkAsgn, nkFastAsgn:
if n[0].isSingleAssignable and n[1].isLocal: let y = n[1].skipConv
let slot = c.getSlot(n[1].sym) if n[0].isSingleAssignable and y.isLocal:
let slot = c.getSlot(y.sym)
slot.alias = n[0].sym slot.alias = n[0].sym
elif n[0].isLocal: elif n[0].isLocal:
# since we already ensure sfAddrTaken is not in s.flags, we only need to # since we already ensure sfAddrTaken is not in s.flags, we only need to
@ -334,7 +335,7 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
analyse(c, n[0]) analyse(c, n[0])
else: else:
analyseSons(c, n) analyseSons(c, n)
addAsgnFact(c.guards, n[0], n[1]) addAsgnFact(c.guards, n[0], y)
of nkCallKinds: of nkCallKinds:
# direct call: # direct call:
if n[0].kind == nkSym: analyseCall(c, n, n[0].sym) if n[0].kind == nkSym: analyseCall(c, n, n[0].sym)

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -23,7 +23,7 @@ import
# Predefined effects: # Predefined effects:
# io, time (time dependent), gc (performs GC'ed allocation), exceptions, # io, time (time dependent), gc (performs GC'ed allocation), exceptions,
# side effect (accesses global), store (stores into *type*), # side effect (accesses global), store (stores into *type*),
# store_unkown (performs some store) --> store(any)|store(x) # store_unknown (performs some store) --> store(any)|store(x)
# load (loads from *type*), recursive (recursive call), unsafe, # load (loads from *type*), recursive (recursive call), unsafe,
# endless (has endless loops), --> user effects are defined over *patterns* # endless (has endless loops), --> user effects are defined over *patterns*
# --> a TR macro can annotate the proc with user defined annotations # --> a TR macro can annotate the proc with user defined annotations
@ -138,7 +138,7 @@ proc guardDotAccess(a: PEffects; n: PNode) =
if g.kind == skUnknown: if g.kind == skUnknown:
var field: PSym = nil var field: PSym = nil
var ty = n.sons[0].typ.skipTypes(abstractPtrs) var ty = n.sons[0].typ.skipTypes(abstractPtrs)
if ty.kind == tyTuple: if ty.kind == tyTuple and not ty.n.isNil:
field = lookupInRecord(ty.n, g.name) field = lookupInRecord(ty.n, g.name)
else: else:
while ty != nil and ty.kind == tyObject: while ty != nil and ty.kind == tyObject:
@ -194,6 +194,39 @@ proc warnAboutGcUnsafe(n: PNode) =
#assert false #assert false
message(n.info, warnGcUnsafe, renderTree(n)) message(n.info, warnGcUnsafe, renderTree(n))
proc markGcUnsafe(a: PEffects; reason: PSym) =
a.gcUnsafe = true
if a.owner.kind in routineKinds: a.owner.gcUnsafetyReason = reason
proc markGcUnsafe(a: PEffects; reason: PNode) =
a.gcUnsafe = true
if a.owner.kind in routineKinds:
if reason.kind == nkSym:
a.owner.gcUnsafetyReason = reason.sym
else:
a.owner.gcUnsafetyReason = newSym(skUnknown, getIdent("<unknown>"),
a.owner, reason.info)
proc listGcUnsafety(s: PSym; onlyWarning: bool) =
let u = s.gcUnsafetyReason
if u != nil:
let msgKind = if onlyWarning: warnGcUnsafe2 else: errGenerated
if u.kind in {skLet, skVar}:
message(s.info, msgKind,
("'$#' is not GC-safe as it accesses '$#'" &
" which is a global using GC'ed memory") % [s.name.s, u.name.s])
elif u.kind in routineKinds:
# recursive call *always* produces only a warning so the full error
# message is printed:
listGcUnsafety(u, true)
message(s.info, msgKind,
"'$#' is not GC-safe as it calls '$#'" %
[s.name.s, u.name.s])
else:
internalAssert u.kind == skUnknown
message(u.info, msgKind,
"'$#' is not GC-safe as it performs an indirect call here" % s.name.s)
proc useVar(a: PEffects, n: PNode) = proc useVar(a: PEffects, n: PNode) =
let s = n.sym let s = n.sym
if isLocalVar(a, s): if isLocalVar(a, s):
@ -206,10 +239,9 @@ proc useVar(a: PEffects, n: PNode) =
a.init.add s.id a.init.add s.id
if {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind in {skVar, skLet}: if {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind in {skVar, skLet}:
if s.guard != nil: guardGlobal(a, n, s.guard) if s.guard != nil: guardGlobal(a, n, s.guard)
if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem) and if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem):
tfGcSafe notin s.typ.flags: #if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) markGcUnsafe(a, s)
a.gcUnsafe = true
type type
TIntersection = seq[tuple[id, count: int]] # a simple count table TIntersection = seq[tuple[id, count: int]] # a simple count table
@ -230,7 +262,7 @@ proc getEbase(): PType =
proc excType(n: PNode): PType = proc excType(n: PNode): PType =
# reraise is like raising E_Base: # reraise is like raising E_Base:
let t = if n.kind == nkEmpty: getEbase() else: n.typ let t = if n.kind == nkEmpty or n.typ.isNil: getEbase() else: n.typ
result = skipTypes(t, skipPtrs) result = skipTypes(t, skipPtrs)
proc createRaise(n: PNode): PNode = proc createRaise(n: PNode): PNode =
@ -448,7 +480,7 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
if notGcSafe(s.typ) and sfImportc notin s.flags: if notGcSafe(s.typ) and sfImportc notin s.flags:
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
tracked.gcUnsafe = true markGcUnsafe(tracked, s)
mergeLockLevels(tracked, n, s.getLockLevel) mergeLockLevels(tracked, n, s.getLockLevel)
proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
@ -458,7 +490,7 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
if n.kind == nkAddr: if n.kind == nkAddr:
# addr(x[]) can't be proven, but addr(x) can: # addr(x[]) can't be proven, but addr(x) can:
if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return
elif n.kind == nkSym and n.sym.kind in routineKinds: elif (n.kind == nkSym and n.sym.kind in routineKinds) or n.kind in procDefs:
# 'p' is not nil obviously: # 'p' is not nil obviously:
return return
case impliesNotNil(tracked.guards, n) case impliesNotNil(tracked.guards, n)
@ -502,13 +534,13 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
# assume GcUnsafe unless in its type; 'forward' does not matter: # assume GcUnsafe unless in its type; 'forward' does not matter:
if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner): if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner):
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
tracked.gcUnsafe = true markGcUnsafe(tracked, a)
else: else:
mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeEffects(tracked, effectList.sons[exceptionEffects], n)
mergeTags(tracked, effectList.sons[tagEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n)
if notGcSafe(op): if notGcSafe(op):
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
tracked.gcUnsafe = true markGcUnsafe(tracked, a)
notNilCheck(tracked, n, paramType) notNilCheck(tracked, n, paramType)
proc breaksBlock(n: PNode): bool = proc breaksBlock(n: PNode): bool =
@ -528,7 +560,10 @@ proc trackCase(tracked: PEffects, n: PNode) =
track(tracked, n.sons[0]) track(tracked, n.sons[0])
let oldState = tracked.init.len let oldState = tracked.init.len
let oldFacts = tracked.guards.len let oldFacts = tracked.guards.len
let interesting = interestingCaseExpr(n.sons[0]) and warnProveField in gNotes let stringCase = skipTypes(n.sons[0].typ,
abstractVarRange-{tyTypeDesc}).kind in {tyFloat..tyFloat128, tyString}
let interesting = not stringCase and interestingCaseExpr(n.sons[0]) and
warnProveField in gNotes
var inter: TIntersection = @[] var inter: TIntersection = @[]
var toCover = 0 var toCover = 0
for i in 1.. <n.len: for i in 1.. <n.len:
@ -543,13 +578,8 @@ proc trackCase(tracked: PEffects, n: PNode) =
for i in oldState.. <tracked.init.len: for i in oldState.. <tracked.init.len:
addToIntersection(inter, tracked.init[i]) addToIntersection(inter, tracked.init[i])
let exh = case skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc}).kind
of tyFloat..tyFloat128, tyString:
lastSon(n).kind == nkElse
else:
true
setLen(tracked.init, oldState) setLen(tracked.init, oldState)
if exh: if not stringCase or lastSon(n).kind == nkElse:
for id, count in items(inter): for id, count in items(inter):
if count >= toCover: tracked.init.add id if count >= toCover: tracked.init.add id
# else we can't merge # else we can't merge
@ -656,7 +686,7 @@ proc track(tracked: PEffects, n: PNode) =
# and it's not a recursive call: # and it's not a recursive call:
if not (a.kind == nkSym and a.sym == tracked.owner): if not (a.kind == nkSym and a.sym == tracked.owner):
warnAboutGcUnsafe(n) warnAboutGcUnsafe(n)
tracked.gcUnsafe = true markGcUnsafe(tracked, a)
for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i)) for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}: if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
# may not look like an assignment, but it is: # may not look like an assignment, but it is:
@ -682,8 +712,8 @@ proc track(tracked: PEffects, n: PNode) =
of nkVarSection, nkLetSection: of nkVarSection, nkLetSection:
for child in n: for child in n:
let last = lastSon(child) let last = lastSon(child)
if last.kind != nkEmpty: track(tracked, last)
if child.kind == nkIdentDefs and last.kind != nkEmpty: if child.kind == nkIdentDefs and last.kind != nkEmpty:
track(tracked, last)
for i in 0 .. child.len-3: for i in 0 .. child.len-3:
initVar(tracked, child.sons[i], volatileCheck=false) initVar(tracked, child.sons[i], volatileCheck=false)
addAsgnFact(tracked.guards, child.sons[i], last) addAsgnFact(tracked.guards, child.sons[i], last)
@ -732,7 +762,7 @@ proc track(tracked: PEffects, n: PNode) =
setLen(tracked.locked, oldLocked) setLen(tracked.locked, oldLocked)
tracked.currLockLevel = oldLockLevel tracked.currLockLevel = oldLockLevel
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
nkMacroDef, nkTemplateDef: nkMacroDef, nkTemplateDef, nkLambda, nkDo:
discard discard
else: else:
for i in 0 .. <safeLen(n): track(tracked, n.sons[i]) for i in 0 .. <safeLen(n): track(tracked, n.sons[i])
@ -849,19 +879,22 @@ proc trackProc*(s: PSym, body: PNode) =
# after the check, use the formal spec: # after the check, use the formal spec:
effects.sons[tagEffects] = tagsSpec effects.sons[tagEffects] = tagsSpec
if optThreadAnalysis in gGlobalOptions: if sfThread in s.flags and t.gcUnsafe:
if sfThread in s.flags and t.gcUnsafe: if optThreads in gGlobalOptions and optThreadAnalysis in gGlobalOptions:
if optThreads in gGlobalOptions: #localError(s.info, "'$1' is not GC-safe" % s.name.s)
localError(s.info, "'$1' is not GC-safe" % s.name.s) listGcUnsafety(s, onlyWarning=false)
else: else:
localError(s.info, warnGcUnsafe2, s.name.s) listGcUnsafety(s, onlyWarning=true)
if not t.gcUnsafe: s.typ.flags.incl tfGcSafe #localError(s.info, warnGcUnsafe2, s.name.s)
if s.typ.lockLevel == UnspecifiedLockLevel: if not t.gcUnsafe:
s.typ.lockLevel = t.maxLockLevel s.typ.flags.incl tfGcSafe
elif t.maxLockLevel > s.typ.lockLevel: if s.typ.lockLevel == UnspecifiedLockLevel:
localError(s.info, s.typ.lockLevel = t.maxLockLevel
"declared lock level is $1, but real lock level is $2" % elif t.maxLockLevel > s.typ.lockLevel:
[$s.typ.lockLevel, $t.maxLockLevel]) #localError(s.info,
message(s.info, warnLockLevel,
"declared lock level is $1, but real lock level is $2" %
[$s.typ.lockLevel, $t.maxLockLevel])
proc trackTopLevelStmt*(module: PSym; n: PNode) = proc trackTopLevelStmt*(module: PSym; n: PNode) =
if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef,

View file

@ -92,20 +92,16 @@ proc semProc(c: PContext, n: PNode): PNode
include semdestruct include semdestruct
proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} = proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} =
if efAllowDestructor notin flags and n.kind in nkCallKinds+{nkObjConstr}: if efAllowDestructor notin flags and
n.kind in nkCallKinds+{nkObjConstr,nkBracket}:
if instantiateDestructor(c, n.typ) != nil: if instantiateDestructor(c, n.typ) != nil:
localError(n.info, errGenerated, localError(n.info, warnDestructor)
"usage of a type with a destructor in a non destructible context")
# This still breaks too many things: # This still breaks too many things:
when false: when false:
if efDetermineType notin flags and n.typ.kind == tyTypeDesc and if efDetermineType notin flags and n.typ.kind == tyTypeDesc and
c.p.owner.kind notin {skTemplate, skMacro}: c.p.owner.kind notin {skTemplate, skMacro}:
localError(n.info, errGenerated, "value expected, but got a type") localError(n.info, errGenerated, "value expected, but got a type")
proc newDeref(n: PNode): PNode {.inline.} =
result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
addSon(result, n)
proc semExprBranch(c: PContext, n: PNode): PNode = proc semExprBranch(c: PContext, n: PNode): PNode =
result = semExpr(c, n) result = semExpr(c, n)
if result.typ != nil: if result.typ != nil:
@ -340,6 +336,44 @@ proc checkNilable(v: PSym) =
elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags: elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags:
message(v.info, warnProveInit, v.name.s) message(v.info, warnProveInit, v.name.s)
include semasgn
proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
# consider this:
# var
# x = 0
# withOverloadedAssignment = foo()
# y = use(withOverloadedAssignment)
# We need to split this into a statement list with multiple 'var' sections
# in order for this transformation to be correct.
let L = identDefs.len
let value = identDefs[L-1]
if value.typ != nil and tfHasAsgn in value.typ.flags:
# the spec says we need to rewrite 'var x = T()' to 'var x: T; x = T()':
identDefs.sons[L-1] = emptyNode
if result.kind != nkStmtList:
let oldResult = result
oldResult.add identDefs
result = newNodeI(nkStmtList, result.info)
result.add oldResult
else:
let o = copyNode(orig)
o.add identDefs
result.add o
for i in 0 .. L-3:
result.add overloadedAsgn(c, identDefs[i], value)
elif result.kind == nkStmtList:
let o = copyNode(orig)
o.add identDefs
result.add o
else:
result.add identDefs
proc isDiscardUnderscore(v: PSym): bool =
if v.name.s == "_":
v.flags.incl(sfGenSym)
result = true
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
var b: PNode var b: PNode
result = copyNode(n) result = copyNode(n)
@ -359,13 +393,17 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
var def: PNode var def: PNode
if a.sons[length-1].kind != nkEmpty: if a.sons[length-1].kind != nkEmpty:
def = semExprWithType(c, a.sons[length-1], {efAllowDestructor}) def = semExprWithType(c, a.sons[length-1], {efAllowDestructor})
if def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
# prevent the all too common 'var x = int' bug:
localError(def.info, "'typedesc' metatype is not valid here; typed '=' instead of ':'?")
def.typ = errorType(c)
if typ != nil: if typ != nil:
if typ.isMetaType: if typ.isMetaType:
def = inferWithMetatype(c, typ, def) def = inferWithMetatype(c, typ, def)
typ = def.typ typ = def.typ
else: else:
# BUGFIX: ``fitNode`` is needed here! # BUGFIX: ``fitNode`` is needed here!
# check type compability between def.typ and typ # check type compatibility between def.typ and typ
def = fitNode(c, typ, def) def = fitNode(c, typ, def)
#changeType(def.skipConv, typ, check=true) #changeType(def.skipConv, typ, check=true)
else: else:
@ -380,8 +418,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
# this can only happen for errornous var statements: # this can only happen for errornous var statements:
if typ == nil: continue if typ == nil: continue
if not typeAllowed(typ, symkind): typeAllowedCheck(a.info, typ, symkind)
localError(a.info, errXisNoType, typeToString(typ))
var tup = skipTypes(typ, {tyGenericInst}) var tup = skipTypes(typ, {tyGenericInst})
if a.kind == nkVarTuple: if a.kind == nkVarTuple:
if tup.kind != tyTuple: if tup.kind != tyTuple:
@ -393,19 +430,23 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
newSons(b, length) newSons(b, length)
b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator
b.sons[length-1] = def b.sons[length-1] = def
addSon(result, b) addToVarSection(c, result, n, b)
elif tup.kind == tyTuple and def.kind == nkPar and elif tup.kind == tyTuple and def.kind == nkPar and
a.kind == nkIdentDefs and a.len > 3: a.kind == nkIdentDefs and a.len > 3:
message(a.info, warnEachIdentIsTuple) message(a.info, warnEachIdentIsTuple)
for j in countup(0, length-3): for j in countup(0, length-3):
var v = semIdentDef(c, a.sons[j], symkind) var v = semIdentDef(c, a.sons[j], symkind)
if sfGenSym notin v.flags: addInterfaceDecl(c, v) if sfGenSym notin v.flags and not isDiscardUnderscore(v):
addInterfaceDecl(c, v)
when oKeepVariableNames: when oKeepVariableNames:
if c.inUnrolledContext > 0: v.flags.incl(sfShadowed) if c.inUnrolledContext > 0: v.flags.incl(sfShadowed)
else: else:
let shadowed = findShadowedVar(c, v) let shadowed = findShadowedVar(c, v)
if shadowed != nil: if shadowed != nil:
shadowed.flags.incl(sfShadowed) shadowed.flags.incl(sfShadowed)
if shadowed.kind == skResult and sfGenSym notin v.flags:
message(a.info, warnResultShadowed)
# a shadowed variable is an error unless it appears on the right # a shadowed variable is an error unless it appears on the right
# side of the '=': # side of the '=':
if warnShadowIdent in gNotes and not identWithin(def, v.name): if warnShadowIdent in gNotes and not identWithin(def, v.name):
@ -423,7 +464,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
addSon(b, newSymNode(v)) addSon(b, newSymNode(v))
addSon(b, a.sons[length-2]) # keep type desc for doc generator addSon(b, a.sons[length-2]) # keep type desc for doc generator
addSon(b, copyTree(def)) addSon(b, copyTree(def))
addSon(result, b) addToVarSection(c, result, n, b)
else: else:
if def.kind == nkPar: v.ast = def[j] if def.kind == nkPar: v.ast = def[j]
v.typ = tup.sons[j] v.typ = tup.sons[j]
@ -456,7 +497,7 @@ proc semConst(c: PContext, n: PNode): PNode =
if typ == nil: if typ == nil:
localError(a.sons[2].info, errConstExprExpected) localError(a.sons[2].info, errConstExprExpected)
continue continue
if not typeAllowed(typ, skConst) and def.kind != nkNilLit: if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit:
localError(a.info, errXisNoType, typeToString(typ)) localError(a.info, errXisNoType, typeToString(typ))
continue continue
v.typ = typ v.typ = typ
@ -512,7 +553,8 @@ proc semForVars(c: PContext, n: PNode): PNode =
if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal) if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal)
v.typ = iter.sons[i] v.typ = iter.sons[i]
n.sons[i] = newSymNode(v) n.sons[i] = newSymNode(v)
if sfGenSym notin v.flags: addForVarDecl(c, v) if sfGenSym notin v.flags and not isDiscardUnderscore(v):
addForVarDecl(c, v)
inc(c.p.nestedLoopCounter) inc(c.p.nestedLoopCounter)
n.sons[length-1] = semStmt(c, n.sons[length-1]) n.sons[length-1] = semStmt(c, n.sons[length-1])
dec(c.p.nestedLoopCounter) dec(c.p.nestedLoopCounter)
@ -648,12 +690,23 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
#debug s.typ #debug s.typ
s.ast = a s.ast = a
popOwner() popOwner()
let aa = a.sons[2]
if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
aa.sons[0].kind == nkObjectTy:
# give anonymous object a dummy symbol:
var st = s.typ
if st.kind == tyGenericBody: st = st.lastSon
internalAssert st.kind in {tyPtr, tyRef}
internalAssert st.lastSon.sym == nil
st.lastSon.sym = newSym(skType, getIdent(s.name.s & ":ObjectType"),
getCurrOwner(), s.info)
proc checkForMetaFields(n: PNode) = proc checkForMetaFields(n: PNode) =
template checkMeta(t) = template checkMeta(t) =
if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags: if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags:
localError(n.info, errTIsNotAConcreteType, t.typeToString) localError(n.info, errTIsNotAConcreteType, t.typeToString)
if n.isNil: return
case n.kind case n.kind
of nkRecList, nkRecCase: of nkRecList, nkRecCase:
for s in n: checkForMetaFields(s) for s in n: checkForMetaFields(s)
@ -663,8 +716,8 @@ proc checkForMetaFields(n: PNode) =
let t = n.sym.typ let t = n.sym.typ
case t.kind case t.kind
of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyPtr, tyRef, of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyPtr, tyRef,
tyProc, tyGenericInvokation, tyGenericInst: tyProc, tyGenericInvocation, tyGenericInst:
let start = ord(t.kind in {tyGenericInvokation, tyGenericInst}) let start = ord(t.kind in {tyGenericInvocation, tyGenericInst})
for i in start .. <t.sons.len: for i in start .. <t.sons.len:
checkMeta(t.sons[i]) checkMeta(t.sons[i])
else: else:
@ -688,18 +741,8 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
assignType(s.typ, t) assignType(s.typ, t)
s.typ.id = t.id # same id s.typ.id = t.id # same id
checkConstructedType(s.info, s.typ) checkConstructedType(s.info, s.typ)
if s.typ.kind in {tyObject, tyTuple}: if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
checkForMetaFields(s.typ.n) checkForMetaFields(s.typ.n)
let aa = a.sons[2]
if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
aa.sons[0].kind == nkObjectTy:
# give anonymous object a dummy symbol:
var st = s.typ
if st.kind == tyGenericBody: st = st.lastSon
internalAssert st.kind in {tyPtr, tyRef}
internalAssert st.lastSon.sym == nil
st.lastSon.sym = newSym(skType, getIdent(s.name.s & ":ObjectType"),
getCurrOwner(), s.info)
proc semTypeSection(c: PContext, n: PNode): PNode = proc semTypeSection(c: PContext, n: PNode): PNode =
## Processes a type section. This must be done in separate passes, in order ## Processes a type section. This must be done in separate passes, in order
@ -753,7 +796,8 @@ proc lookupMacro(c: PContext, n: PNode): PSym =
else: else:
result = searchInScopes(c, considerQuotedIdent(n), {skMacro, skTemplate}) result = searchInScopes(c, considerQuotedIdent(n), {skMacro, skTemplate})
proc semProcAnnotation(c: PContext, prc: PNode): PNode = proc semProcAnnotation(c: PContext, prc: PNode;
validPragmas: TSpecialWords): PNode =
var n = prc.sons[pragmasPos] var n = prc.sons[pragmasPos]
if n == nil or n.kind == nkEmpty: return if n == nil or n.kind == nkEmpty: return
for i in countup(0, <n.len): for i in countup(0, <n.len):
@ -778,12 +822,18 @@ proc semProcAnnotation(c: PContext, prc: PNode): PNode =
x.add(it.sons[1]) x.add(it.sons[1])
x.add(prc) x.add(prc)
# recursion assures that this works for multiple macro annotations too: # recursion assures that this works for multiple macro annotations too:
return semStmt(c, x) result = semStmt(c, x)
# since a proc annotation can set pragmas, we process these here again.
# This is required for SqueakNim-like export pragmas.
if result.kind in procDefs and result[namePos].kind == nkSym and
result[pragmasPos].kind != nkEmpty:
pragma(c, result[namePos].sym, result[pragmasPos], validPragmas)
return
proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
# XXX semProcAux should be good enough for this now, we will eventually # XXX semProcAux should be good enough for this now, we will eventually
# remove semLambda # remove semLambda
result = semProcAnnotation(c, n) result = semProcAnnotation(c, n, lambdaPragmas)
if result != nil: return result if result != nil: return result
result = n result = n
checkSonsLen(n, bodyPos + 1) checkSonsLen(n, bodyPos + 1)
@ -813,8 +863,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
# we have a list of implicit type parameters: # we have a list of implicit type parameters:
n.sons[genericParamsPos] = gp n.sons[genericParamsPos] = gp
else: else:
s.typ = newTypeS(tyProc, c) s.typ = newProcType(c, n.info)
rawAddSon(s.typ, nil)
if n.sons[pragmasPos].kind != nkEmpty: if n.sons[pragmasPos].kind != nkEmpty:
pragma(c, s, n.sons[pragmasPos], lambdaPragmas) pragma(c, s, n.sons[pragmasPos], lambdaPragmas)
s.options = gOptions s.options = gOptions
@ -826,9 +875,9 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
if gp.len == 0 or (gp.len == 1 and tfRetType in gp[0].typ.flags): if gp.len == 0 or (gp.len == 1 and tfRetType in gp[0].typ.flags):
pushProcCon(c, s) pushProcCon(c, s)
addResult(c, s.typ.sons[0], n.info, skProc) addResult(c, s.typ.sons[0], n.info, skProc)
addResultNode(c, n)
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
n.sons[bodyPos] = transformBody(c.module, semBody, s) n.sons[bodyPos] = transformBody(c.module, semBody, s)
addResultNode(c, n)
popProcCon(c) popProcCon(c)
elif efOperand notin flags: elif efOperand notin flags:
localError(n.info, errGenericLambdaNotAllowed) localError(n.info, errGenericLambdaNotAllowed)
@ -839,6 +888,13 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
popOwner() popOwner()
result.typ = s.typ result.typ = s.typ
proc semDo(c: PContext, n: PNode, flags: TExprFlags): PNode =
# 'do' without params produces a stmt:
if n[genericParamsPos].kind == nkEmpty and n[paramsPos].kind == nkEmpty:
result = semStmt(c, n[bodyPos])
else:
result = semLambda(c, n, flags)
proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
var n = n var n = n
@ -850,13 +906,15 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
openScope(c) openScope(c)
var s = n.sons[namePos].sym var s = n.sons[namePos].sym
pushOwner(s)
addParams(c, n.typ.n, skProc) addParams(c, n.typ.n, skProc)
pushProcCon(c, s) pushProcCon(c, s)
addResult(c, n.typ.sons[0], n.info, skProc) addResult(c, n.typ.sons[0], n.info, skProc)
addResultNode(c, n)
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
n.sons[bodyPos] = transformBody(c.module, semBody, n.sons[namePos].sym) n.sons[bodyPos] = transformBody(c.module, semBody, n.sons[namePos].sym)
addResultNode(c, n)
popProcCon(c) popProcCon(c)
popOwner()
closeScope(c) closeScope(c)
s.ast = result s.ast = result
@ -887,11 +945,12 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
proc semOverride(c: PContext, s: PSym, n: PNode) = proc semOverride(c: PContext, s: PSym, n: PNode) =
case s.name.s.normalize case s.name.s.normalize
of "destroy": of "destroy", "=destroy":
doDestructorStuff(c, s, n) doDestructorStuff(c, s, n)
if not experimentalMode(c): if not experimentalMode(c):
localError n.info, "use the {.experimental.} pragma to enable destructors" localError n.info, "use the {.experimental.} pragma to enable destructors"
of "deepcopy": incl(s.flags, sfUsed)
of "deepcopy", "=deepcopy":
if s.typ.len == 2 and if s.typ.len == 2 and
s.typ.sons[1].skipTypes(abstractInst).kind in {tyRef, tyPtr} and s.typ.sons[1].skipTypes(abstractInst).kind in {tyRef, tyPtr} and
sameType(s.typ.sons[1], s.typ.sons[0]): sameType(s.typ.sons[1], s.typ.sons[0]):
@ -900,7 +959,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
var t = s.typ.sons[1].skipTypes(abstractInst).lastSon.skipTypes(abstractInst) var t = s.typ.sons[1].skipTypes(abstractInst).lastSon.skipTypes(abstractInst)
while true: while true:
if t.kind == tyGenericBody: t = t.lastSon if t.kind == tyGenericBody: t = t.lastSon
elif t.kind == tyGenericInvokation: t = t.sons[0] elif t.kind == tyGenericInvocation: t = t.sons[0]
else: break else: break
if t.kind in {tyObject, tyDistinct, tyEnum}: if t.kind in {tyObject, tyDistinct, tyEnum}:
if t.deepCopy.isNil: t.deepCopy = s if t.deepCopy.isNil: t.deepCopy = s
@ -913,10 +972,35 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
else: else:
localError(n.info, errGenerated, localError(n.info, errGenerated,
"signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T") "signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T")
of "=": discard incl(s.flags, sfUsed)
else: localError(n.info, errGenerated, of "=":
"'destroy' or 'deepCopy' expected for 'override'") incl(s.flags, sfUsed)
incl(s.flags, sfUsed) let t = s.typ
if t.len == 3 and t.sons[0] == nil and t.sons[1].kind == tyVar:
var obj = t.sons[1].sons[0]
while true:
incl(obj.flags, tfHasAsgn)
if obj.kind == tyGenericBody: obj = obj.lastSon
elif obj.kind == tyGenericInvocation: obj = obj.sons[0]
else: break
var objB = t.sons[2]
while true:
if objB.kind == tyGenericBody: objB = objB.lastSon
elif objB.kind == tyGenericInvocation: objB = objB.sons[0]
else: break
if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB):
if obj.assignment.isNil:
obj.assignment = s
else:
localError(n.info, errGenerated,
"cannot bind another '=' to: " & typeToString(obj))
return
localError(n.info, errGenerated,
"signature for '=' must be proc[T: object](x: var T; y: T)")
else:
if sfOverriden in s.flags:
localError(n.info, errGenerated,
"'destroy' or 'deepCopy' expected for 'override'")
type type
TProcCompilationSteps = enum TProcCompilationSteps = enum
@ -931,7 +1015,7 @@ proc isForwardDecl(s: PSym): bool =
proc semProcAux(c: PContext, n: PNode, kind: TSymKind, proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
validPragmas: TSpecialWords, validPragmas: TSpecialWords,
phase = stepRegisterSymbol): PNode = phase = stepRegisterSymbol): PNode =
result = semProcAnnotation(c, n) result = semProcAnnotation(c, n, validPragmas)
if result != nil: return result if result != nil: return result
result = n result = n
checkSonsLen(n, bodyPos + 1) checkSonsLen(n, bodyPos + 1)
@ -948,7 +1032,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
s = semIdentDef(c, n.sons[0], kind) s = semIdentDef(c, n.sons[0], kind)
n.sons[namePos] = newSymNode(s) n.sons[namePos] = newSymNode(s)
s.ast = n s.ast = n
s.scope = c.currentScope #s.scope = c.currentScope
if sfNoForward in c.module.flags and if sfNoForward in c.module.flags and
sfSystemModule notin c.module.flags: sfSystemModule notin c.module.flags:
@ -960,14 +1044,14 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
s.owner = getCurrOwner() s.owner = getCurrOwner()
typeIsDetermined = s.typ == nil typeIsDetermined = s.typ == nil
s.ast = n s.ast = n
s.scope = c.currentScope #s.scope = c.currentScope
# if typeIsDetermined: assert phase == stepCompileBody # if typeIsDetermined: assert phase == stepCompileBody
# else: assert phase == stepDetermineType # else: assert phase == stepDetermineType
# before compiling the proc body, set as current the scope # before compiling the proc body, set as current the scope
# where the proc was declared # where the proc was declared
let oldScope = c.currentScope let oldScope = c.currentScope
c.currentScope = s.scope #c.currentScope = s.scope
pushOwner(s) pushOwner(s)
openScope(c) openScope(c)
var gp: PNode var gp: PNode
@ -986,14 +1070,13 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
# check for semantics again: # check for semantics again:
# semParamList(c, n.sons[ParamsPos], nil, s) # semParamList(c, n.sons[ParamsPos], nil, s)
else: else:
s.typ = newTypeS(tyProc, c) s.typ = newProcType(c, n.info)
rawAddSon(s.typ, nil)
if n.sons[patternPos].kind != nkEmpty: if n.sons[patternPos].kind != nkEmpty:
n.sons[patternPos] = semPattern(c, n.sons[patternPos]) n.sons[patternPos] = semPattern(c, n.sons[patternPos])
if s.kind in skIterators: if s.kind in skIterators:
s.typ.flags.incl(tfIterator) s.typ.flags.incl(tfIterator)
var proto = searchForProc(c, s.scope, s) var proto = searchForProc(c, oldScope, s)
if proto == nil: if proto == nil:
if s.kind == skClosureIterator: s.typ.callConv = ccClosure if s.kind == skClosureIterator: s.typ.callConv = ccClosure
else: s.typ.callConv = lastOptionEntry(c).defaultCC else: s.typ.callConv = lastOptionEntry(c).defaultCC
@ -1001,10 +1084,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
if sfGenSym in s.flags: discard if sfGenSym in s.flags: discard
elif kind in OverloadableSyms: elif kind in OverloadableSyms:
if not typeIsDetermined: if not typeIsDetermined:
addInterfaceOverloadableSymAt(c, s.scope, s) addInterfaceOverloadableSymAt(c, oldScope, s)
else: else:
if not typeIsDetermined: if not typeIsDetermined:
addInterfaceDeclAt(c, s.scope, s) addInterfaceDeclAt(c, oldScope, s)
if n.sons[pragmasPos].kind != nkEmpty: if n.sons[pragmasPos].kind != nkEmpty:
pragma(c, s, n.sons[pragmasPos], validPragmas) pragma(c, s, n.sons[pragmasPos], validPragmas)
else: else:
@ -1034,7 +1117,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
popOwner() popOwner()
pushOwner(s) pushOwner(s)
s.options = gOptions s.options = gOptions
if sfOverriden in s.flags: semOverride(c, s, n) if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
if n.sons[bodyPos].kind != nkEmpty: if n.sons[bodyPos].kind != nkEmpty:
# for DLL generation it is annoying to check for sfImportc! # for DLL generation it is annoying to check for sfImportc!
if sfBorrow in s.flags: if sfBorrow in s.flags:
@ -1046,6 +1129,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
if n.sons[genericParamsPos].kind == nkEmpty or usePseudoGenerics: if n.sons[genericParamsPos].kind == nkEmpty or usePseudoGenerics:
if not usePseudoGenerics: paramsTypeCheck(c, s.typ) if not usePseudoGenerics: paramsTypeCheck(c, s.typ)
pushProcCon(c, s) pushProcCon(c, s)
c.p.wasForwarded = proto != nil
maybeAddResult(c, s, n) maybeAddResult(c, s, n)
if sfImportc notin s.flags: if sfImportc notin s.flags:
# no semantic checking for importc: # no semantic checking for importc:
@ -1057,8 +1141,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
else: else:
if s.typ.sons[0] != nil and kind notin skIterators: if s.typ.sons[0] != nil and kind notin skIterators:
addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info)) addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info))
var toBind = initIntSet() openScope(c)
n.sons[bodyPos] = semGenericStmtScope(c, n.sons[bodyPos], {}, toBind) n.sons[bodyPos] = semGenericStmt(c, n.sons[bodyPos])
closeScope(c)
fixupInstantiatedSymbols(c, s) fixupInstantiatedSymbols(c, s)
if sfImportc in s.flags: if sfImportc in s.flags:
# so we just ignore the body after semantic checking for importc: # so we just ignore the body after semantic checking for importc:
@ -1070,7 +1155,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
elif sfBorrow in s.flags: semBorrow(c, n, s) elif sfBorrow in s.flags: semBorrow(c, n, s)
sideEffectsCheck(c, s) sideEffectsCheck(c, s)
closeScope(c) # close scope for parameters closeScope(c) # close scope for parameters
c.currentScope = oldScope # c.currentScope = oldScope
popOwner() popOwner()
if n.sons[patternPos].kind != nkEmpty: if n.sons[patternPos].kind != nkEmpty:
c.patterns.add(s) c.patterns.add(s)
@ -1128,7 +1213,10 @@ proc semMethod(c: PContext, n: PNode): PNode =
result = semProcAux(c, n, skMethod, methodPragmas) result = semProcAux(c, n, skMethod, methodPragmas)
var s = result.sons[namePos].sym var s = result.sons[namePos].sym
if not isGenericRoutine(s) and result.sons[bodyPos].kind != nkEmpty: if not isGenericRoutine(s):
# why check for the body? bug #2400 has none. Checking for sfForward makes
# no sense either.
# and result.sons[bodyPos].kind != nkEmpty:
if hasObjParam(s): if hasObjParam(s):
methodDef(s, fromCache=false) methodDef(s, fromCache=false)
else: else:
@ -1183,6 +1271,8 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode =
else: discard else: discard
proc semStaticStmt(c: PContext, n: PNode): PNode = proc semStaticStmt(c: PContext, n: PNode): PNode =
#echo "semStaticStmt"
#writeStackTrace()
let a = semStmt(c, n.sons[0]) let a = semStmt(c, n.sons[0])
n.sons[0] = a n.sons[0] = a
evalStaticStmt(c.module, a, c.p.owner) evalStaticStmt(c.module, a, c.p.owner)
@ -1200,7 +1290,8 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
proc usesResult(n: PNode): bool = proc usesResult(n: PNode): bool =
# nkStmtList(expr) properly propagates the void context, # nkStmtList(expr) properly propagates the void context,
# so we don't need to process that all over again: # so we don't need to process that all over again:
if n.kind notin {nkStmtList, nkStmtListExpr} + procDefs: if n.kind notin {nkStmtList, nkStmtListExpr,
nkMacroDef, nkTemplateDef} + procDefs:
if isAtom(n): if isAtom(n):
result = n.kind == nkSym and n.sym.kind == skResult result = n.kind == nkSym and n.sym.kind == skResult
elif n.kind == nkReturnStmt: elif n.kind == nkReturnStmt:
@ -1252,7 +1343,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
var tryStmt = newNodeI(nkTryStmt, n.sons[i].info) var tryStmt = newNodeI(nkTryStmt, n.sons[i].info)
var body = newNodeI(nkStmtList, n.sons[i].info) var body = newNodeI(nkStmtList, n.sons[i].info)
if i < n.sonsLen - 1: if i < n.sonsLen - 1:
body.sons = n.sons[(i+1)..(-1)] body.sons = n.sons[(i+1)..n.len-1]
tryStmt.addSon(body) tryStmt.addSon(body)
tryStmt.addSon(deferPart) tryStmt.addSon(deferPart)
n.sons[i] = semTry(c, tryStmt) n.sons[i] = semTry(c, tryStmt)
@ -1261,10 +1352,14 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
return return
else: else:
n.sons[i] = semExpr(c, n.sons[i]) n.sons[i] = semExpr(c, n.sons[i])
if c.inTypeClass > 0 and n[i].typ != nil and n[i].typ.kind == tyBool: if c.inTypeClass > 0 and n[i].typ != nil:
let verdict = semConstExpr(c, n[i]) case n[i].typ.kind
if verdict.intVal == 0: of tyBool:
localError(result.info, "type class predicate failed") let verdict = semConstExpr(c, n[i])
if verdict.intVal == 0:
localError(result.info, "type class predicate failed")
of tyUnknown: continue
else: discard
if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]): if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]):
voidContext = true voidContext = true
n.typ = enforceVoidContext n.typ = enforceVoidContext

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -105,10 +105,11 @@ proc replaceIdentBySym(n: var PNode, s: PNode) =
else: illFormedAst(n) else: illFormedAst(n)
type type
TemplCtx {.pure, final.} = object TemplCtx = object
c: PContext c: PContext
toBind, toMixin, toInject: IntSet toBind, toMixin, toInject: IntSet
owner: PSym owner: PSym
cursorInBody: bool # only for nimsuggest
proc getIdentNode(c: var TemplCtx, n: PNode): PNode = proc getIdentNode(c: var TemplCtx, n: PNode): PNode =
case n.kind case n.kind
@ -127,7 +128,7 @@ proc getIdentNode(c: var TemplCtx, n: PNode): PNode =
proc isTemplParam(c: TemplCtx, n: PNode): bool {.inline.} = proc isTemplParam(c: TemplCtx, n: PNode): bool {.inline.} =
result = n.kind == nkSym and n.sym.kind == skParam and result = n.kind == nkSym and n.sym.kind == skParam and
n.sym.owner == c.owner n.sym.owner == c.owner and sfGenSym notin n.sym.flags
proc semTemplBody(c: var TemplCtx, n: PNode): PNode proc semTemplBody(c: var TemplCtx, n: PNode): PNode
@ -246,8 +247,8 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
n.sons[i] = semTemplBody(c, n.sons[i]) n.sons[i] = semTemplBody(c, n.sons[i])
closeScope(c) closeScope(c)
proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind) = proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind; start=0) =
for i in countup(0, sonsLen(n) - 1): for i in countup(start, sonsLen(n) - 1):
var a = n.sons[i] var a = n.sons[i]
if a.kind == nkCommentStmt: continue if a.kind == nkCommentStmt: continue
if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a) if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a)
@ -261,6 +262,7 @@ proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind) =
proc semPattern(c: PContext, n: PNode): PNode proc semPattern(c: PContext, n: PNode): PNode
proc semTemplBody(c: var TemplCtx, n: PNode): PNode = proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
result = n result = n
semIdeForTemplateOrGenericCheck(n, c.cursorInBody)
case n.kind case n.kind
of nkIdent: of nkIdent:
if n.ident.id in c.toInject: return n if n.ident.id in c.toInject: return n
@ -348,6 +350,10 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
a.sons[L-1] = semTemplBodyScope(c, a.sons[L-1]) a.sons[L-1] = semTemplBodyScope(c, a.sons[L-1])
of nkVarSection: semTemplSomeDecl(c, n, skVar) of nkVarSection: semTemplSomeDecl(c, n, skVar)
of nkLetSection: semTemplSomeDecl(c, n, skLet) of nkLetSection: semTemplSomeDecl(c, n, skLet)
of nkFormalParams:
checkMinSonsLen(n, 1)
n.sons[0] = semTemplBody(c, n.sons[0])
semTemplSomeDecl(c, n, skParam, 1)
of nkConstSection: of nkConstSection:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i] var a = n.sons[i]
@ -402,7 +408,13 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
if n.kind == nkDotExpr or n.kind == nkAccQuoted: if n.kind == nkDotExpr or n.kind == nkAccQuoted:
let s = qualifiedLookUp(c.c, n, {}) let s = qualifiedLookUp(c.c, n, {})
if s != nil: if s != nil:
if contains(c.toBind, s.id): # do not symchoice a quoted template parameter (bug #2390):
if s.owner == c.owner and s.kind == skParam and
n.kind == nkAccQuoted and n.len == 1:
incl(s.flags, sfUsed)
styleCheckUse(n.info, s)
return newSymNode(s, n.info)
elif contains(c.toBind, s.id):
return symChoice(c.c, n, s, scClosed) return symChoice(c.c, n, s, scClosed)
elif contains(c.toMixin, s.name.id): elif contains(c.toMixin, s.name.id):
return symChoice(c.c, n, s, scForceOpen) return symChoice(c.c, n, s, scForceOpen)
@ -414,6 +426,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode = proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode =
result = n result = n
semIdeForTemplateOrGenericCheck(n, c.cursorInBody)
case n.kind case n.kind
of nkIdent: of nkIdent:
let s = qualifiedLookUp(c.c, n, {}) let s = qualifiedLookUp(c.c, n, {})
@ -429,7 +442,7 @@ proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode =
of nkEmpty, nkSym..nkNilLit: of nkEmpty, nkSym..nkNilLit:
discard discard
else: else:
# dotExpr is ambiguous: note that we explicitely allow 'x.TemplateParam', # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
# so we use the generic code for nkDotExpr too # so we use the generic code for nkDotExpr too
if n.kind == nkDotExpr or n.kind == nkAccQuoted: if n.kind == nkDotExpr or n.kind == nkAccQuoted:
let s = qualifiedLookUp(c.c, n, {}) let s = qualifiedLookUp(c.c, n, {})
@ -469,7 +482,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
s = semIdentVis(c, skTemplate, n.sons[0], {}) s = semIdentVis(c, skTemplate, n.sons[0], {})
styleCheckDef(s) styleCheckDef(s)
# check parameter list: # check parameter list:
s.scope = c.currentScope #s.scope = c.currentScope
pushOwner(s) pushOwner(s)
openScope(c) openScope(c)
n.sons[namePos] = newSymNode(s, n.sons[namePos].info) n.sons[namePos] = newSymNode(s, n.sons[namePos].info)
@ -485,6 +498,11 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
# process parameters: # process parameters:
if n.sons[paramsPos].kind != nkEmpty: if n.sons[paramsPos].kind != nkEmpty:
semParamList(c, n.sons[paramsPos], gp, s) semParamList(c, n.sons[paramsPos], gp, s)
# a template's parameters are not gensym'ed even if that was originally the
# case as we determine whether it's a template parameter in the template
# body by the absence of the sfGenSym flag:
for i in 1 .. s.typ.n.len-1:
s.typ.n.sons[i].sym.flags.excl sfGenSym
if sonsLen(gp) > 0: if sonsLen(gp) > 0:
if n.sons[genericParamsPos].kind == nkEmpty: if n.sons[genericParamsPos].kind == nkEmpty:
# we have a list of implicit type parameters: # we have a list of implicit type parameters:
@ -515,6 +533,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
if s.typ.sons[0].kind notin {tyStmt, tyTypeDesc}: if s.typ.sons[0].kind notin {tyStmt, tyTypeDesc}:
n.sons[bodyPos] = transformToExpr(n.sons[bodyPos]) n.sons[bodyPos] = transformToExpr(n.sons[bodyPos])
# only parameters are resolved, no type checking is performed # only parameters are resolved, no type checking is performed
semIdeForTemplateOrGeneric(c, n.sons[bodyPos], ctx.cursorInBody)
closeScope(c) closeScope(c)
popOwner() popOwner()
s.ast = n s.ast = n
@ -627,7 +646,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
result.sons[i] = semPatternBody(c, n.sons[i]) result.sons[i] = semPatternBody(c, n.sons[i])
else: else:
# dotExpr is ambiguous: note that we explicitely allow 'x.TemplateParam', # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
# so we use the generic code for nkDotExpr too # so we use the generic code for nkDotExpr too
case n.kind case n.kind
of nkDotExpr, nkAccQuoted: of nkDotExpr, nkAccQuoted:

View file

@ -214,51 +214,55 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType =
localError(n.info, errXExpectsOneTypeParam, "range") localError(n.info, errXExpectsOneTypeParam, "range")
result = newOrPrevType(tyError, prev, c) result = newOrPrevType(tyError, prev, c)
proc semArrayIndex(c: PContext, n: PNode): PType =
if isRange(n): result = semRangeAux(c, n, nil)
else:
let e = semExprWithType(c, n, {efDetermineType})
if e.typ.kind == tyFromExpr:
result = makeRangeWithStaticExpr(c, e.typ.n)
elif e.kind in {nkIntLit..nkUInt64Lit}:
result = makeRangeType(c, 0, e.intVal-1, n.info, e.typ)
elif e.kind == nkSym and e.typ.kind == tyStatic:
if e.sym.ast != nil:
return semArrayIndex(c, e.sym.ast)
if not isOrdinalType(e.typ.lastSon):
localError(n[1].info, errOrdinalTypeExpected)
result = makeRangeWithStaticExpr(c, e)
if c.inGenericContext > 0: result.flags.incl tfUnresolved
elif e.kind in nkCallKinds and hasGenericArguments(e):
if not isOrdinalType(e.typ):
localError(n[1].info, errOrdinalTypeExpected)
# This is an int returning call, depending on an
# yet unknown generic param (see tgenericshardcases).
# We are going to construct a range type that will be
# properly filled-out in semtypinst (see how tyStaticExpr
# is handled there).
result = makeRangeWithStaticExpr(c, e)
elif e.kind == nkIdent:
result = e.typ.skipTypes({tyTypeDesc})
else:
let x = semConstExpr(c, e)
if x.kind in {nkIntLit..nkUInt64Lit}:
result = makeRangeType(c, 0, x.intVal-1, n.info,
x.typ.skipTypes({tyTypeDesc}))
else:
result = x.typ.skipTypes({tyTypeDesc})
#localError(n[1].info, errConstExprExpected)
proc semArray(c: PContext, n: PNode, prev: PType): PType = proc semArray(c: PContext, n: PNode, prev: PType): PType =
var indx, base: PType var base: PType
result = newOrPrevType(tyArray, prev, c) result = newOrPrevType(tyArray, prev, c)
if sonsLen(n) == 3: if sonsLen(n) == 3:
# 3 = length(array indx base) # 3 = length(array indx base)
if isRange(n[1]): indx = semRangeAux(c, n[1], nil) var indx = semArrayIndex(c, n[1])
else:
let e = semExprWithType(c, n.sons[1], {efDetermineType})
if e.typ.kind == tyFromExpr:
indx = makeRangeWithStaticExpr(c, e.typ.n)
elif e.kind in {nkIntLit..nkUInt64Lit}:
indx = makeRangeType(c, 0, e.intVal-1, n.info, e.typ)
elif e.kind == nkSym and e.typ.kind == tyStatic:
if e.sym.ast != nil: return semArray(c, e.sym.ast, nil)
internalAssert c.inGenericContext > 0
if not isOrdinalType(e.typ.lastSon):
localError(n[1].info, errOrdinalTypeExpected)
indx = makeRangeWithStaticExpr(c, e)
indx.flags.incl tfUnresolved
elif e.kind in nkCallKinds and hasGenericArguments(e):
if not isOrdinalType(e.typ):
localError(n[1].info, errOrdinalTypeExpected)
# This is an int returning call, depending on an
# yet unknown generic param (see tgenericshardcases).
# We are going to construct a range type that will be
# properly filled-out in semtypinst (see how tyStaticExpr
# is handled there).
indx = makeRangeWithStaticExpr(c, e)
elif e.kind == nkIdent:
indx = e.typ.skipTypes({tyTypeDesc})
else:
let x = semConstExpr(c, e)
if x.kind in {nkIntLit..nkUInt64Lit}:
indx = makeRangeType(c, 0, x.intVal-1, n.info,
x.typ.skipTypes({tyTypeDesc}))
else:
indx = x.typ.skipTypes({tyTypeDesc})
#localError(n[1].info, errConstExprExpected)
addSonSkipIntLit(result, indx) addSonSkipIntLit(result, indx)
if indx.kind == tyGenericInst: indx = lastSon(indx) if indx.kind == tyGenericInst: indx = lastSon(indx)
if indx.kind notin {tyGenericParam, tyStatic, tyFromExpr}: if indx.kind notin {tyGenericParam, tyStatic, tyFromExpr}:
if not isOrdinalType(indx): if not isOrdinalType(indx):
localError(n.sons[1].info, errOrdinalTypeExpected) localError(n.sons[1].info, errOrdinalTypeExpected)
elif enumHasHoles(indx): elif enumHasHoles(indx):
localError(n.sons[1].info, errEnumXHasHoles, indx.sym.name.s) localError(n.sons[1].info, errEnumXHasHoles,
typeToString(indx.skipTypes({tyRange})))
base = semTypeNode(c, n.sons[2], nil) base = semTypeNode(c, n.sons[2], nil)
addSonSkipIntLit(result, base) addSonSkipIntLit(result, base)
else: else:
@ -329,16 +333,27 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
if result.typ.kind != tyGenericParam: if result.typ.kind != tyGenericParam:
# XXX get rid of this hack! # XXX get rid of this hack!
var oldInfo = n.info var oldInfo = n.info
when defined(useNodeIds):
let oldId = n.id
reset(n[]) reset(n[])
when defined(useNodeIds):
n.id = oldId
n.kind = nkSym n.kind = nkSym
n.sym = result n.sym = result
n.info = oldInfo n.info = oldInfo
n.typ = result.typ
else: else:
localError(n.info, errIdentifierExpected) localError(n.info, errIdentifierExpected)
result = errorSym(c, n) result = errorSym(c, n)
proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType =
if sonsLen(n) == 0:
localError(n.info, errTypeExpected)
result = newOrPrevType(tyTuple, prev, c)
for i in countup(0, sonsLen(n) - 1):
addSonSkipIntLit(result, semTypeNode(c, n.sons[i], nil))
proc semTuple(c: PContext, n: PNode, prev: PType): PType = proc semTuple(c: PContext, n: PNode, prev: PType): PType =
if n.sonsLen == 0: return newConstraint(c, tyTuple)
var typ: PType var typ: PType
result = newOrPrevType(tyTuple, prev, c) result = newOrPrevType(tyTuple, prev, c)
result.n = newNodeI(nkRecList, n.info) result.n = newNodeI(nkRecList, n.info)
@ -444,6 +459,7 @@ proc semCaseBranchSetElem(c: PContext, t, b: PNode,
proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int, proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
covered: var BiggestInt) = covered: var BiggestInt) =
for i in countup(0, sonsLen(branch) - 2): for i in countup(0, sonsLen(branch) - 2):
var b = branch.sons[i] var b = branch.sons[i]
if b.kind == nkRange: if b.kind == nkRange:
@ -453,8 +469,11 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
else: else:
# constant sets and arrays are allowed: # constant sets and arrays are allowed:
var r = semConstExpr(c, b) var r = semConstExpr(c, b)
# for ``{}`` we want to trigger the type mismatch in ``fitNode``: if r.kind in {nkCurly, nkBracket} and len(r) == 0 and sonsLen(branch)==2:
if r.kind notin {nkCurly, nkBracket} or len(r) == 0: # discarding ``{}`` and ``[]`` branches silently
delSon(branch, 0)
return
elif r.kind notin {nkCurly, nkBracket} or len(r) == 0:
checkMinSonsLen(t, 1) checkMinSonsLen(t, 1)
branch.sons[i] = skipConv(fitNode(c, t.sons[0].typ, r)) branch.sons[i] = skipConv(fitNode(c, t.sons[0].typ, r))
inc(covered) inc(covered)
@ -575,7 +594,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
f.position = pos f.position = pos
if (rec != nil) and ({sfImportc, sfExportc} * rec.flags != {}) and if (rec != nil) and ({sfImportc, sfExportc} * rec.flags != {}) and
(f.loc.r == nil): (f.loc.r == nil):
f.loc.r = toRope(f.name.s) f.loc.r = rope(f.name.s)
f.flags = f.flags + ({sfImportc, sfExportc} * rec.flags) f.flags = f.flags + ({sfImportc, sfExportc} * rec.flags)
inc(pos) inc(pos)
if containsOrIncl(check, f.name.id): if containsOrIncl(check, f.name.id):
@ -606,19 +625,20 @@ proc addInheritedFieldsAux(c: PContext, check: var IntSet, pos: var int,
inc(pos) inc(pos)
else: internalError(n.info, "addInheritedFieldsAux()") else: internalError(n.info, "addInheritedFieldsAux()")
proc skipGenericInvocation(t: PType): PType {.inline.} =
result = t
if result.kind == tyGenericInvocation:
result = result.sons[0]
while result.kind in {tyGenericInst, tyGenericBody}:
result = lastSon(result)
proc addInheritedFields(c: PContext, check: var IntSet, pos: var int, proc addInheritedFields(c: PContext, check: var IntSet, pos: var int,
obj: PType) = obj: PType) =
assert obj.kind == tyObject
if (sonsLen(obj) > 0) and (obj.sons[0] != nil): if (sonsLen(obj) > 0) and (obj.sons[0] != nil):
addInheritedFields(c, check, pos, obj.sons[0]) addInheritedFields(c, check, pos, obj.sons[0].skipGenericInvocation)
addInheritedFieldsAux(c, check, pos, obj.n) addInheritedFieldsAux(c, check, pos, obj.n)
proc skipGenericInvokation(t: PType): PType {.inline.} =
result = t
if result.kind == tyGenericInvokation:
result = result.sons[0]
if result.kind == tyGenericBody:
result = lastSon(result)
proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
if n.sonsLen == 0: return newConstraint(c, tyObject) if n.sonsLen == 0: return newConstraint(c, tyObject)
var check = initIntSet() var check = initIntSet()
@ -627,14 +647,17 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
# n.sons[0] contains the pragmas (if any). We process these later... # n.sons[0] contains the pragmas (if any). We process these later...
checkSonsLen(n, 3) checkSonsLen(n, 3)
if n.sons[1].kind != nkEmpty: if n.sons[1].kind != nkEmpty:
base = skipTypes(semTypeNode(c, n.sons[1].sons[0], nil), skipPtrs) base = skipTypesOrNil(semTypeNode(c, n.sons[1].sons[0], nil), skipPtrs)
var concreteBase = skipGenericInvokation(base).skipTypes(skipPtrs) if base.isNil:
if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags: localError(n.info, errIllegalRecursionInTypeX, "object")
addInheritedFields(c, check, pos, concreteBase)
else: else:
if concreteBase.kind != tyError: var concreteBase = skipGenericInvocation(base).skipTypes(skipPtrs)
localError(n.sons[1].info, errInheritanceOnlyWithNonFinalObjects) if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags:
base = nil addInheritedFields(c, check, pos, concreteBase)
else:
if concreteBase.kind != tyError:
localError(n.sons[1].info, errInheritanceOnlyWithNonFinalObjects)
base = nil
if n.kind != nkObjectTy: internalError(n.info, "semObjectNode") if n.kind != nkObjectTy: internalError(n.info, "semObjectNode")
result = newOrPrevType(tyObject, prev, c) result = newOrPrevType(tyObject, prev, c)
rawAddSon(result, base) rawAddSon(result, base)
@ -659,25 +682,23 @@ proc findEnforcedStaticType(t: PType): PType =
if t != nil: return t if t != nil: return t
proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
template addDecl(x) =
if sfGenSym notin x.flags: addDecl(c, x)
if kind == skMacro: if kind == skMacro:
let staticType = findEnforcedStaticType(param.typ) let staticType = findEnforcedStaticType(param.typ)
if staticType != nil: if staticType != nil:
var a = copySym(param) var a = copySym(param)
a.typ = staticType.base a.typ = staticType.base
addDecl(a) addDecl(c, a)
elif param.typ.kind == tyTypeDesc: elif param.typ.kind == tyTypeDesc:
addDecl(param) addDecl(c, param)
else: else:
# within a macro, every param has the type PNimrodNode! # within a macro, every param has the type NimNode!
let nn = getSysSym"PNimrodNode" let nn = if getCompilerProc("NimNode") != nil: getSysSym"NimNode"
else: getSysSym"PNimrodNode"
var a = copySym(param) var a = copySym(param)
a.typ = nn.typ a.typ = nn.typ
addDecl(a) addDecl(c, a)
else: else:
addDecl(param) if sfGenSym notin param.flags: addDecl(c, param)
let typedescId = getIdent"typedesc" let typedescId = getIdent"typedesc"
@ -769,18 +790,25 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
@[newTypeS(paramType.kind, c)]) @[newTypeS(paramType.kind, c)])
result = addImplicitGeneric(typ) result = addImplicitGeneric(typ)
else: else:
for i in 0 .. <paramType.sons.len: for i in 0 .. <paramType.len:
if paramType.sons[i] == paramType:
globalError(info, errIllegalRecursionInTypeX, typeToString(paramType))
var lifted = liftingWalk(paramType.sons[i]) var lifted = liftingWalk(paramType.sons[i])
if lifted != nil: if lifted != nil:
paramType.sons[i] = lifted paramType.sons[i] = lifted
result = paramType result = paramType
of tyGenericBody: of tyGenericBody:
result = newTypeS(tyGenericInvokation, c) result = newTypeS(tyGenericInvocation, c)
result.rawAddSon(paramType) result.rawAddSon(paramType)
for i in 0 .. paramType.sonsLen - 2: for i in 0 .. paramType.sonsLen - 2:
result.rawAddSon newTypeS(tyAnything, c) if paramType.sons[i].kind == tyStatic:
# result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true)) var x = copyNode(ast.emptyNode)
x.typ = paramType.sons[i]
result.rawAddSon makeTypeFromExpr(c, x) # aka 'tyUnknown'
else:
result.rawAddSon newTypeS(tyAnything, c)
if paramType.lastSon.kind == tyUserTypeClass: if paramType.lastSon.kind == tyUserTypeClass:
result.kind = tyUserTypeClassInst result.kind = tyUserTypeClassInst
@ -807,7 +835,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
cp.kind = tyUserTypeClassInst cp.kind = tyUserTypeClassInst
return addImplicitGeneric(cp) return addImplicitGeneric(cp)
for i in 1 .. (paramType.sons.len - 2): for i in 1 .. paramType.len-2:
var lifted = liftingWalk(paramType.sons[i]) var lifted = liftingWalk(paramType.sons[i])
if lifted != nil: if lifted != nil:
paramType.sons[i] = lifted paramType.sons[i] = lifted
@ -819,8 +847,8 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
result = liftBody result = liftBody
result.shouldHaveMeta result.shouldHaveMeta
of tyGenericInvokation: of tyGenericInvocation:
for i in 1 .. <paramType.sonsLen: for i in 1 .. <paramType.len:
let lifted = liftingWalk(paramType.sons[i]) let lifted = liftingWalk(paramType.sons[i])
if lifted != nil: paramType.sons[i] = lifted if lifted != nil: paramType.sons[i] = lifted
when false: when false:
@ -853,25 +881,25 @@ proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType =
else: else:
result = semTypeNode(c, n, nil) result = semTypeNode(c, n, nil)
proc semProcTypeNode(c: PContext, n, genericParams: PNode, proc newProcType(c: PContext; info: TLineInfo; prev: PType = nil): PType =
prev: PType, kind: TSymKind; isType=false): PType =
# for historical reasons (code grows) this is invoked for parameter
# lists too and then 'isType' is false.
var
res: PNode
cl: IntSet
checkMinSonsLen(n, 1)
result = newOrPrevType(tyProc, prev, c) result = newOrPrevType(tyProc, prev, c)
result.callConv = lastOptionEntry(c).defaultCC result.callConv = lastOptionEntry(c).defaultCC
result.n = newNodeI(nkFormalParams, n.info) result.n = newNodeI(nkFormalParams, info)
if genericParams != nil and sonsLen(genericParams) == 0:
cl = initIntSet()
rawAddSon(result, nil) # return type rawAddSon(result, nil) # return type
# result.n[0] used to be `nkType`, but now it's `nkEffectList` because # result.n[0] used to be `nkType`, but now it's `nkEffectList` because
# the effects are now stored in there too ... this is a bit hacky, but as # the effects are now stored in there too ... this is a bit hacky, but as
# usual we desperately try to save memory: # usual we desperately try to save memory:
res = newNodeI(nkEffectList, n.info) addSon(result.n, newNodeI(nkEffectList, info))
addSon(result.n, res)
proc semProcTypeNode(c: PContext, n, genericParams: PNode,
prev: PType, kind: TSymKind; isType=false): PType =
# for historical reasons (code grows) this is invoked for parameter
# lists too and then 'isType' is false.
var cl: IntSet
checkMinSonsLen(n, 1)
result = newProcType(c, n.info, prev)
if genericParams != nil and sonsLen(genericParams) == 0:
cl = initIntSet()
var check = initIntSet() var check = initIntSet()
var counter = 0 var counter = 0
for i in countup(1, n.len - 1): for i in countup(1, n.len - 1):
@ -896,7 +924,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if hasDefault: if hasDefault:
def = semExprWithType(c, a.sons[length-1]) def = semExprWithType(c, a.sons[length-1])
# check type compability between def.typ and typ: # check type compatibility between def.typ and typ:
if typ == nil: if typ == nil:
typ = def.typ typ = def.typ
elif def != nil: elif def != nil:
@ -908,6 +936,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if not hasType and not hasDefault: if not hasType and not hasDefault:
if isType: localError(a.info, "':' expected") if isType: localError(a.info, "':' expected")
let tdef = if kind in {skTemplate, skMacro}: tyExpr else: tyAnything let tdef = if kind in {skTemplate, skMacro}: tyExpr else: tyAnything
if tdef == tyAnything:
message(a.info, warnTypelessParam, renderTree(n))
typ = newTypeS(tdef, c) typ = newTypeS(tdef, c)
if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue
@ -933,7 +963,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
r = semTypeNode(c, n.sons[0], nil) r = semTypeNode(c, n.sons[0], nil)
elif kind == skIterator: elif kind == skIterator:
# XXX This is special magic we should likely get rid of # XXX This is special magic we should likely get rid of
r = newTypeS(tyAnything, c) r = newTypeS(tyExpr, c)
if r != nil: if r != nil:
# turn explicit 'void' return type into 'nil' because the rest of the # turn explicit 'void' return type into 'nil' because the rest of the
@ -953,7 +983,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
# we don't need to change the return type to iter[T] # we don't need to change the return type to iter[T]
if not r.isInlineIterator: r = newTypeWithSons(c, tyIter, @[r]) if not r.isInlineIterator: r = newTypeWithSons(c, tyIter, @[r])
result.sons[0] = r result.sons[0] = r
res.typ = r result.n.typ = r
if genericParams != nil: if genericParams != nil:
for n in genericParams: for n in genericParams:
@ -985,7 +1015,7 @@ proc semBlockType(c: PContext, n: PNode, prev: PType): PType =
closeScope(c) closeScope(c)
dec(c.p.nestedBlockCounter) dec(c.p.nestedBlockCounter)
proc semGenericParamInInvokation(c: PContext, n: PNode): PType = proc semGenericParamInInvocation(c: PContext, n: PNode): PType =
result = semTypeNode(c, n, nil) result = semTypeNode(c, n, nil)
proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
@ -998,7 +1028,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody: if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody:
t = t.base t = t.base
result = newOrPrevType(tyGenericInvokation, prev, c) result = newOrPrevType(tyGenericInvocation, prev, c)
addSonSkipIntLit(result, t) addSonSkipIntLit(result, t)
template addToResult(typ) = template addToResult(typ) =
@ -1009,20 +1039,20 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
if t.kind == tyForward: if t.kind == tyForward:
for i in countup(1, sonsLen(n)-1): for i in countup(1, sonsLen(n)-1):
var elem = semGenericParamInInvokation(c, n.sons[i]) var elem = semGenericParamInInvocation(c, n.sons[i])
addToResult(elem) addToResult(elem)
return return
elif t.kind != tyGenericBody: elif t.kind != tyGenericBody:
#we likely got code of the form TypeA[TypeB] where TypeA is # we likely got code of the form TypeA[TypeB] where TypeA is
#not generic. # not generic.
localError(n.info, errNoGenericParamsAllowedForX, s.name.s) localError(n.info, errNoGenericParamsAllowedForX, s.name.s)
return newOrPrevType(tyError, prev, c) return newOrPrevType(tyError, prev, c)
else: else:
var m = newCandidate(c, t) var m = newCandidate(c, t)
matches(c, n, copyTree(n), m) matches(c, n, copyTree(n), m)
if m.state != csMatch: if m.state != csMatch and not m.typedescMatched:
var err = "cannot instantiate " & typeToString(t) & "\n" & let err = "cannot instantiate " & typeToString(t) & "\n" &
"got: (" & describeArgs(c, n) & ")\n" & "got: (" & describeArgs(c, n) & ")\n" &
"but expected: (" & describeArgs(c, t.n, 0) & ")" "but expected: (" & describeArgs(c, t.n, 0) & ")"
localError(n.info, errGenerated, err) localError(n.info, errGenerated, err)
@ -1031,9 +1061,14 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
var isConcrete = true var isConcrete = true
for i in 1 .. <m.call.len: for i in 1 .. <m.call.len:
let typ = m.call[i].typ.skipTypes({tyTypeDesc}) var typ = m.call[i].typ
if containsGenericType(typ): isConcrete = false if typ.kind == tyTypeDesc and typ.sons[0].kind == tyNone:
addToResult(typ) isConcrete = false
addToResult(typ)
else:
typ = typ.skipTypes({tyTypeDesc})
if containsGenericType(typ): isConcrete = false
addToResult(typ)
if isConcrete: if isConcrete:
if s.ast == nil and s.typ.kind != tyCompositeTypeClass: if s.ast == nil and s.typ.kind != tyCompositeTypeClass:
@ -1059,6 +1094,8 @@ proc freshType(res, prev: PType): PType {.inline.} =
proc semTypeClass(c: PContext, n: PNode, prev: PType): PType = proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
# if n.sonsLen == 0: return newConstraint(c, tyTypeClass) # if n.sonsLen == 0: return newConstraint(c, tyTypeClass)
if nfBase2 in n.flags:
message(n.info, warnDeprecated, "use 'concept' instead; 'generic'")
result = newOrPrevType(tyUserTypeClass, prev, c) result = newOrPrevType(tyUserTypeClass, prev, c)
result.n = n result.n = n
@ -1101,9 +1138,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of nkPar: of nkPar:
if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev) if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev)
else: else:
# XXX support anon tuple here result = semAnonTuple(c, n, prev)
localError(n.info, errTypeExpected)
result = newOrPrevType(tyError, prev, c)
of nkCallKinds: of nkCallKinds:
if isRange(n): if isRange(n):
result = semRangeAux(c, n, prev) result = semRangeAux(c, n, prev)
@ -1129,7 +1164,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
case n.len case n.len
of 3: of 3:
result = semTypeNode(c, n.sons[1], prev) result = semTypeNode(c, n.sons[1], prev)
if result.kind in NilableTypes and n.sons[2].kind == nkNilLit: if result.skipTypes({tyGenericInst}).kind in NilableTypes+GenericTypes and
n.sons[2].kind == nkNilLit:
result = freshType(result, prev) result = freshType(result, prev)
result.flags.incl(tfNotNil) result.flags.incl(tfNotNil)
else: else:
@ -1143,6 +1179,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = semAnyRef(c, n, tyPtr, prev) result = semAnyRef(c, n, tyPtr, prev)
elif op.id == ord(wRef): elif op.id == ord(wRef):
result = semAnyRef(c, n, tyRef, prev) result = semAnyRef(c, n, tyRef, prev)
elif op.id == ord(wType):
checkSonsLen(n, 2)
let typExpr = semExprWithType(c, n.sons[1], {efInTypeof})
result = typExpr.typ.skipTypes({tyIter})
else: else:
result = semTypeExpr(c, n) result = semTypeExpr(c, n)
of nkWhenStmt: of nkWhenStmt:
@ -1172,12 +1212,12 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
var typeExpr = semExpr(c, n) var typeExpr = semExpr(c, n)
if typeExpr.typ.kind != tyTypeDesc: if typeExpr.typ.kind != tyTypeDesc:
localError(n.info, errTypeExpected) localError(n.info, errTypeExpected)
return errorType(c) result = errorType(c)
result = typeExpr.typ.base else:
if result.isMetaType: result = typeExpr.typ.base
var toBind = initIntSet() if result.isMetaType:
var preprocessed = semGenericStmt(c, n, {}, toBind) var preprocessed = semGenericStmt(c, n)
return makeTypeFromExpr(c, preprocessed) result = makeTypeFromExpr(c, preprocessed.copyTree)
of nkIdent, nkAccQuoted: of nkIdent, nkAccQuoted:
var s = semTypeIdent(c, n) var s = semTypeIdent(c, n)
if s.typ == nil: if s.typ == nil:
@ -1210,6 +1250,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tyError, prev, c) result = newOrPrevType(tyError, prev, c)
of nkObjectTy: result = semObjectNode(c, n, prev) of nkObjectTy: result = semObjectNode(c, n, prev)
of nkTupleTy: result = semTuple(c, n, prev) of nkTupleTy: result = semTuple(c, n, prev)
of nkTupleClassTy: result = newConstraint(c, tyTuple)
of nkTypeClassTy: result = semTypeClass(c, n, prev) of nkTypeClassTy: result = semTypeClass(c, n, prev)
of nkRefTy: result = semAnyRef(c, n, tyRef, prev) of nkRefTy: result = semAnyRef(c, n, tyRef, prev)
of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev) of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev)
@ -1248,6 +1289,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
else: else:
localError(n.info, errTypeExpected) localError(n.info, errTypeExpected)
result = newOrPrevType(tyError, prev, c) result = newOrPrevType(tyError, prev, c)
n.typ = result
proc setMagicType(m: PSym, kind: TTypeKind, size: int) = proc setMagicType(m: PSym, kind: TTypeKind, size: int) =
m.typ.kind = kind m.typ.kind = kind

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -16,9 +16,9 @@ const
proc sharedPtrCheck(info: TLineInfo, t: PType) = proc sharedPtrCheck(info: TLineInfo, t: PType) =
if t.kind == tyPtr and t.len > 1: if t.kind == tyPtr and t.len > 1:
if t.sons[0].sym.magic in {mShared, mGuarded}: if t.sons[0].sym.magic == mShared:
incl(t.flags, tfShared) incl(t.flags, tfShared)
if t.sons[0].sym.magic == mGuarded: incl(t.flags, tfGuarded) #if t.sons[0].sym.magic == mGuarded: incl(t.flags, tfGuarded)
if tfHasGCedMem in t.flags or t.isGCedMem: if tfHasGCedMem in t.flags or t.isGCedMem:
localError(info, errGenerated, localError(info, errGenerated,
"shared memory may not refer to GC'ed thread local memory") "shared memory may not refer to GC'ed thread local memory")
@ -61,7 +61,7 @@ proc searchInstTypes*(key: PType): PType =
if inst.sons.len < key.sons.len: if inst.sons.len < key.sons.len:
# XXX: This happens for prematurely cached # XXX: This happens for prematurely cached
# types such as TChannel[empty]. Why? # types such as TChannel[empty]. Why?
# See the notes for PActor in handleGenericInvokation # See the notes for PActor in handleGenericInvocation
return return
block matchType: block matchType:
for j in 1 .. high(key.sons): for j in 1 .. high(key.sons):
@ -89,6 +89,7 @@ type
info*: TLineInfo info*: TLineInfo
allowMetaTypes*: bool # allow types such as seq[Number] allowMetaTypes*: bool # allow types such as seq[Number]
# i.e. the result contains unresolved generics # i.e. the result contains unresolved generics
skipTypedesc*: bool # wether we should skip typeDescs
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
@ -101,7 +102,6 @@ template checkMetaInvariants(cl: TReplTypeVars, t: PType) =
echo "UNEXPECTED META ", t.id, " ", instantiationInfo(-1) echo "UNEXPECTED META ", t.id, " ", instantiationInfo(-1)
debug t debug t
writeStackTrace() writeStackTrace()
quit 1
proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType = proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
result = replaceTypeVarsTAux(cl, t) result = replaceTypeVarsTAux(cl, t)
@ -224,7 +224,7 @@ proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType =
result = errorType(cl.c) result = errorType(cl.c)
# In order to prevent endless recursions, we must remember # In order to prevent endless recursions, we must remember
# this bad lookup and replace it with errorType everywhere. # this bad lookup and replace it with errorType everywhere.
# These code paths are only active in nimrod check # These code paths are only active in "nim check"
idTablePut(cl.typeMap, t, result) idTablePut(cl.typeMap, t, result)
elif result.kind == tyGenericParam and not cl.allowMetaTypes: elif result.kind == tyGenericParam and not cl.allowMetaTypes:
internalError(cl.info, "substitution with generic parameter") internalError(cl.info, "substitution with generic parameter")
@ -233,10 +233,12 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
# XXX: relying on allowMetaTypes is a kludge # XXX: relying on allowMetaTypes is a kludge
result = copyType(t, t.owner, cl.allowMetaTypes) result = copyType(t, t.owner, cl.allowMetaTypes)
result.flags.incl tfFromGeneric result.flags.incl tfFromGeneric
result.flags.excl tfInstClearedFlags if not (t.kind in tyMetaTypes or
(t.kind == tyStatic and t.n == nil)):
result.flags.excl tfInstClearedFlags
proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
# tyGenericInvokation[A, tyGenericInvokation[A, B]] # tyGenericInvocation[A, tyGenericInvocation[A, B]]
# is difficult to handle: # is difficult to handle:
var body = t.sons[0] var body = t.sons[0]
if body.kind != tyGenericBody: internalError(cl.info, "no generic body") if body.kind != tyGenericBody: internalError(cl.info, "no generic body")
@ -246,7 +248,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
result = PType(idTableGet(cl.localCache, t)) result = PType(idTableGet(cl.localCache, t))
else: else:
result = searchInstTypes(t) result = searchInstTypes(t)
if result != nil: return if result != nil and eqTypeFlags*result.flags == eqTypeFlags*t.flags: return
for i in countup(1, sonsLen(t) - 1): for i in countup(1, sonsLen(t) - 1):
var x = t.sons[i] var x = t.sons[i]
if x.kind == tyGenericParam: if x.kind == tyGenericParam:
@ -261,7 +263,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
if header != t: if header != t:
# search again after first pass: # search again after first pass:
result = searchInstTypes(header) result = searchInstTypes(header)
if result != nil: return if result != nil and eqTypeFlags*result.flags == eqTypeFlags*t.flags: return
else: else:
header = instCopyType(cl, t) header = instCopyType(cl, t)
@ -277,9 +279,11 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
else: else:
idTablePut(cl.localCache, t, result) idTablePut(cl.localCache, t, result)
let oldSkipTypedesc = cl.skipTypedesc
cl.skipTypedesc = true
for i in countup(1, sonsLen(t) - 1): for i in countup(1, sonsLen(t) - 1):
var x = replaceTypeVarsT(cl, t.sons[i]) var x = replaceTypeVarsT(cl, t.sons[i])
assert x.kind != tyGenericInvokation assert x.kind != tyGenericInvocation
header.sons[i] = x header.sons[i] = x
propagateToOwner(header, x) propagateToOwner(header, x)
idTablePut(cl.typeMap, body.sons[i-1], x) idTablePut(cl.typeMap, body.sons[i-1], x)
@ -290,13 +294,14 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
rawAddSon(result, header.sons[i]) rawAddSon(result, header.sons[i])
var newbody = replaceTypeVarsT(cl, lastSon(body)) var newbody = replaceTypeVarsT(cl, lastSon(body))
cl.skipTypedesc = oldSkipTypedesc
newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags) newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags)
result.flags = result.flags + newbody.flags - tfInstClearedFlags result.flags = result.flags + newbody.flags - tfInstClearedFlags
# This is actually wrong: tgeneric_closure fails with this line: # This is actually wrong: tgeneric_closure fails with this line:
#newbody.callConv = body.callConv #newbody.callConv = body.callConv
# This type may be a generic alias and we want to resolve it here. # This type may be a generic alias and we want to resolve it here.
# One step is enough, because the recursive nature of # One step is enough, because the recursive nature of
# handleGenericInvokation will handle the alias-to-alias-to-alias case # handleGenericInvocation will handle the alias-to-alias-to-alias case
if newbody.isGenericAlias: newbody = newbody.skipGenericAlias if newbody.isGenericAlias: newbody = newbody.skipGenericAlias
rawAddSon(result, newbody) rawAddSon(result, newbody)
checkPartialConstructedType(cl.info, newbody) checkPartialConstructedType(cl.info, newbody)
@ -304,7 +309,13 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
if dc != nil and sfFromGeneric notin newbody.deepCopy.flags: if dc != nil and sfFromGeneric notin newbody.deepCopy.flags:
# 'deepCopy' needs to be instantiated for # 'deepCopy' needs to be instantiated for
# generics *when the type is constructed*: # generics *when the type is constructed*:
newbody.deepCopy = cl.c.instDeepCopy(cl.c, dc, result, cl.info) newbody.deepCopy = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info,
attachedDeepCopy)
let asgn = newbody.assignment
if asgn != nil and sfFromGeneric notin asgn.flags:
# '=' needs to be instantiated for generics when the type is constructed:
newbody.assignment = cl.c.instTypeBoundOp(cl.c, asgn, result, cl.info,
attachedAsgn)
proc eraseVoidParams*(t: PType) = proc eraseVoidParams*(t: PType) =
# transform '(): void' into '()' because old parts of the compiler really # transform '(): void' into '()' because old parts of the compiler really
@ -342,6 +353,8 @@ proc skipIntLiteralParams*(t: PType) =
proc propagateFieldFlags(t: PType, n: PNode) = proc propagateFieldFlags(t: PType, n: PNode) =
# This is meant for objects and tuples # This is meant for objects and tuples
# The type must be fully instantiated! # The type must be fully instantiated!
if n.isNil:
return
internalAssert n.kind != nkRecWhen internalAssert n.kind != nkRecWhen
case n.kind case n.kind
of nkSym: of nkSym:
@ -360,8 +373,8 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
if lookup != nil: return lookup if lookup != nil: return lookup
case t.kind case t.kind
of tyGenericInvokation: of tyGenericInvocation:
result = handleGenericInvokation(cl, t) result = handleGenericInvocation(cl, t)
of tyGenericBody: of tyGenericBody:
localError(cl.info, errCannotInstantiateX, typeToString(t)) localError(cl.info, errCannotInstantiateX, typeToString(t))
@ -370,8 +383,10 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
of tyFromExpr: of tyFromExpr:
if cl.allowMetaTypes: return if cl.allowMetaTypes: return
assert t.n.typ != t
var n = prepareNode(cl, t.n) var n = prepareNode(cl, t.n)
n = cl.c.semConstExpr(cl.c, n) if n.kind != nkEmpty:
n = cl.c.semConstExpr(cl.c, n)
if n.typ.kind == tyTypeDesc: if n.typ.kind == tyTypeDesc:
# XXX: sometimes, chained typedescs enter here. # XXX: sometimes, chained typedescs enter here.
# It may be worth investigating why this is happening, # It may be worth investigating why this is happening,
@ -390,15 +405,14 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
else: else:
result = n.typ result = n.typ
of tyInt: of tyInt, tyFloat:
result = skipIntLit(t) result = skipIntLit(t)
# XXX now there are also float literals
of tyTypeDesc: of tyTypeDesc:
let lookup = PType(idTableGet(cl.typeMap, t)) # lookupTypeVar(cl, t) let lookup = PType(idTableGet(cl.typeMap, t)) # lookupTypeVar(cl, t)
if lookup != nil: if lookup != nil:
result = lookup result = lookup
if tfUnresolved in t.flags: result = result.base if tfUnresolved in t.flags or cl.skipTypedesc: result = result.base
elif t.sons[0].kind != tyNone: elif t.sons[0].kind != tyNone:
result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.sons[0])) result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.sons[0]))
@ -406,15 +420,23 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
result = t result = t
of tyGenericInst: of tyGenericInst:
result = PType(idTableGet(cl.localCache, t))
if result != nil: return result
result = instCopyType(cl, t) result = instCopyType(cl, t)
idTablePut(cl.localCache, t, result)
for i in 1 .. <result.sonsLen: for i in 1 .. <result.sonsLen:
result.sons[i] = replaceTypeVarsT(cl, result.sons[i]) result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
propagateToOwner(result, result.lastSon) propagateToOwner(result, result.lastSon)
else: else:
if containsGenericType(t): if containsGenericType(t):
#if not cl.allowMetaTypes:
result = PType(idTableGet(cl.localCache, t))
if result != nil: return result
result = instCopyType(cl, t) result = instCopyType(cl, t)
result.size = -1 # needs to be recomputed result.size = -1 # needs to be recomputed
#if not cl.allowMetaTypes:
idTablePut(cl.localCache, t, result)
for i in countup(0, sonsLen(result) - 1): for i in countup(0, sonsLen(result) - 1):
if result.sons[i] != nil: if result.sons[i] != nil:

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -14,7 +14,7 @@ import
extccomp, strutils, os, platform, parseopt extccomp, strutils, os, platform, parseopt
when useCaas: when useCaas:
import sockets import net
# We cache modules and the dependency graph. However, we don't check for # We cache modules and the dependency graph. However, we don't check for
# file changes but expect the client to tell us about them, otherwise the # file changes but expect the client to tell us about them, otherwise the
@ -33,7 +33,12 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
parseopt.next(p) parseopt.next(p)
case p.kind case p.kind
of cmdEnd: break of cmdEnd: break
of cmdLongoption, cmdShortOption: processSwitch(pass, p) of cmdLongoption, cmdShortOption:
if p.key == " ":
p.key = "-"
if processArgument(pass, p, argsCount): break
else:
processSwitch(pass, p)
of cmdArgument: of cmdArgument:
if processArgument(pass, p, argsCount): break if processArgument(pass, p, argsCount): break
if pass == passCmd2: if pass == passCmd2:
@ -45,8 +50,6 @@ proc serve*(action: proc (){.nimcall.}) =
curCaasCmd = cmd curCaasCmd = cmd
processCmdLine(passCmd2, cmd) processCmdLine(passCmd2, cmd)
action() action()
gDirtyBufferIdx = 0
gDirtyOriginalIdx = 0
gErrorCounter = 0 gErrorCounter = 0
let typ = getConfigVar("server.type") let typ = getConfigVar("server.type")
@ -61,14 +64,16 @@ proc serve*(action: proc (){.nimcall.}) =
of "tcp", "": of "tcp", "":
when useCaas: when useCaas:
var server = socket() var server = newSocket()
if server == invalidSocket: raiseOSError(osLastError())
let p = getConfigVar("server.port") let p = getConfigVar("server.port")
let port = if p.len > 0: parseInt(p).Port else: 6000.Port let port = if p.len > 0: parseInt(p).Port else: 6000.Port
server.bindAddr(port, getConfigVar("server.address")) server.bindAddr(port, getConfigVar("server.address"))
var inp = "".TaintedString var inp = "".TaintedString
server.listen() server.listen()
new(stdoutSocket) var stdoutSocket = newSocket()
msgs.writelnHook = proc (line: string) =
stdoutSocket.send(line & "\c\L")
while true: while true:
accept(server, stdoutSocket) accept(server, stdoutSocket)
stdoutSocket.readLine(inp) stdoutSocket.readLine(inp)
@ -76,7 +81,7 @@ proc serve*(action: proc (){.nimcall.}) =
stdoutSocket.send("\c\L") stdoutSocket.send("\c\L")
stdoutSocket.close() stdoutSocket.close()
else: else:
quit "server.type not supported; compiler built without caas support" msgQuit "server.type not supported; compiler built without caas support"
else: else:
echo "Invalid server.type:", typ echo "Invalid server.type:", typ
quit 1 msgQuit 1

View file

@ -39,12 +39,14 @@ type
bindings*: TIdTable # maps types to types bindings*: TIdTable # maps types to types
baseTypeMatch: bool # needed for conversions from T to openarray[T] baseTypeMatch: bool # needed for conversions from T to openarray[T]
# for example # for example
proxyMatch*: bool # to prevent instantiations fauxMatch*: TTypeKind # the match was successful only due to the use
# of error or wildcard (unknown) types.
# this is used to prevent instantiations.
genericConverter*: bool # true if a generic converter needs to genericConverter*: bool # true if a generic converter needs to
# be instantiated # be instantiated
coerceDistincts*: bool # this is an explicit coercion that can strip away coerceDistincts*: bool # this is an explicit coercion that can strip away
# a distrinct type # a distrinct type
typedescMatched: bool typedescMatched*: bool
inheritancePenalty: int # to prefer closest father object type inheritancePenalty: int # to prefer closest father object type
errors*: CandidateErrors # additional clarifications to be displayed to the errors*: CandidateErrors # additional clarifications to be displayed to the
# user if overload resolution fails # user if overload resolution fails
@ -66,6 +68,8 @@ const
proc markUsed*(info: TLineInfo, s: PSym) proc markUsed*(info: TLineInfo, s: PSym)
template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone
proc initCandidateAux(ctx: PContext, proc initCandidateAux(ctx: PContext,
c: var TCandidate, callee: PType) {.inline.} = c: var TCandidate, callee: PType) {.inline.} =
c.c = ctx c.c = ctx
@ -87,7 +91,7 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PType) =
initIdTable(c.bindings) initIdTable(c.bindings)
proc put(t: var TIdTable, key, val: PType) {.inline.} = proc put(t: var TIdTable, key, val: PType) {.inline.} =
idTablePut(t, key, val) idTablePut(t, key, val.skipIntLit)
proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
binding: PNode, calleeScope = -1) = binding: PNode, calleeScope = -1) =
@ -95,9 +99,12 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
c.calleeSym = callee c.calleeSym = callee
if callee.kind in skProcKinds and calleeScope == -1: if callee.kind in skProcKinds and calleeScope == -1:
if callee.originatingModule == ctx.module: if callee.originatingModule == ctx.module:
let rootSym = if sfFromGeneric notin callee.flags: callee c.calleeScope = 2
else: callee.owner var owner = callee
c.calleeScope = rootSym.scope.depthLevel while true:
owner = owner.skipGenericOwner
if owner.kind == skModule: break
inc c.calleeScope
else: else:
c.calleeScope = 1 c.calleeScope = 1
else: else:
@ -109,9 +116,12 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1): for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1):
var formalTypeParam = typeParams.sons[i-1].typ var formalTypeParam = typeParams.sons[i-1].typ
var bound = binding[i].typ var bound = binding[i].typ
if bound != nil and formalTypeParam.kind != tyTypeDesc: internalAssert bound != nil
if formalTypeParam.kind == tyTypeDesc:
if bound.kind != tyTypeDesc:
bound = makeTypeDesc(ctx, bound)
else:
bound = bound.skipTypes({tyTypeDesc}) bound = bound.skipTypes({tyTypeDesc})
assert bound != nil
put(c.bindings, formalTypeParam, bound) put(c.bindings, formalTypeParam, bound)
proc newCandidate*(ctx: PContext, callee: PSym, proc newCandidate*(ctx: PContext, callee: PSym,
@ -137,6 +147,7 @@ proc copyCandidate(a: var TCandidate, b: TCandidate) =
proc sumGeneric(t: PType): int = proc sumGeneric(t: PType): int =
var t = t var t = t
var isvar = 1
while true: while true:
case t.kind case t.kind
of tyGenericInst, tyArray, tyRef, tyPtr, tyDistinct, tyArrayConstr, of tyGenericInst, tyArray, tyRef, tyPtr, tyDistinct, tyArrayConstr,
@ -144,38 +155,48 @@ proc sumGeneric(t: PType): int =
t = t.lastSon t = t.lastSon
inc result inc result
of tyVar: of tyVar:
# but do not make 'var T' more specific than 'T'!
t = t.sons[0] t = t.sons[0]
of tyGenericInvokation, tyTuple: inc result
result = ord(t.kind == tyGenericInvokation) inc isvar
of tyGenericInvocation, tyTuple:
result += ord(t.kind == tyGenericInvocation)
for i in 0 .. <t.len: result += t.sons[i].sumGeneric for i in 0 .. <t.len: result += t.sons[i].sumGeneric
break break
of tyProc:
# proc matche proc better than 'stmt' to disambiguate 'spawn'
return 1
of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break
else: return 0 of tyBool, tyChar, tyEnum, tyObject, tyProc, tyPointer,
tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
tyUInt..tyUInt64:
return isvar
else:
return 0
#var ggDebug: bool
proc complexDisambiguation(a, b: PType): int = proc complexDisambiguation(a, b: PType): int =
var x, y: int # 'a' matches better if *every* argument matches better or equal than 'b'.
for i in 1 .. <a.len: x += a.sons[i].sumGeneric var winner = 0
for i in 1 .. <b.len: y += b.sons[i].sumGeneric for i in 1 .. <min(a.len, b.len):
result = x - y let x = a.sons[i].sumGeneric
let y = b.sons[i].sumGeneric
#if ggDebug:
# echo "came her ", typeToString(a.sons[i]), " ", typeToString(b.sons[i])
if x != y:
if winner == 0:
if x > y: winner = 1
else: winner = -1
elif x > y:
if winner != 1:
# contradiction
return 0
else:
if winner != -1:
return 0
result = winner
when false: when false:
proc betterThan(a, b: PType): bool {.inline.} = a.sumGeneric > b.sumGeneric var x, y: int
for i in 1 .. <a.len: x += a.sons[i].sumGeneric
if a.len > 1 and b.len > 1: for i in 1 .. <b.len: y += b.sons[i].sumGeneric
let aa = a.sons[1].sumGeneric result = x - y
let bb = b.sons[1].sumGeneric
var a = a
var b = b
if aa < bb: swap(a, b)
# all must be better
for i in 2 .. <min(a.len, b.len):
if not a.sons[i].betterThan(b.sons[i]): return 0
# a must be longer or of the same length as b:
result = a.len - b.len
proc cmpCandidates*(a, b: TCandidate): int = proc cmpCandidates*(a, b: TCandidate): int =
result = a.exactMatches - b.exactMatches result = a.exactMatches - b.exactMatches
@ -198,10 +219,11 @@ proc cmpCandidates*(a, b: TCandidate): int =
proc writeMatches*(c: TCandidate) = proc writeMatches*(c: TCandidate) =
writeln(stdout, "exact matches: " & $c.exactMatches) writeln(stdout, "exact matches: " & $c.exactMatches)
writeln(stdout, "subtype matches: " & $c.subtypeMatches)
writeln(stdout, "conv matches: " & $c.convMatches)
writeln(stdout, "intconv matches: " & $c.intConvMatches)
writeln(stdout, "generic matches: " & $c.genericMatches) writeln(stdout, "generic matches: " & $c.genericMatches)
writeln(stdout, "subtype matches: " & $c.subtypeMatches)
writeln(stdout, "intconv matches: " & $c.intConvMatches)
writeln(stdout, "conv matches: " & $c.convMatches)
writeln(stdout, "inheritance: " & $c.inheritancePenalty)
proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string = proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string =
if arg.kind in nkSymChoices: if arg.kind in nkSymChoices:
@ -254,7 +276,7 @@ proc concreteType(c: TCandidate, t: PType): PType =
# example code that triggers it: # example code that triggers it:
# proc sort[T](cmp: proc(a, b: T): int = cmp) # proc sort[T](cmp: proc(a, b: T): int = cmp)
if result.kind != tyGenericParam: break if result.kind != tyGenericParam: break
of tyGenericInvokation: of tyGenericInvocation:
internalError("cannot resolve type: " & typeToString(t)) internalError("cannot resolve type: " & typeToString(t))
result = t result = t
else: else:
@ -330,7 +352,8 @@ proc minRel(a, b: TTypeRelation): TTypeRelation =
proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation = proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
result = isNone result = isNone
if sameType(f, a): result = isEqual if sameType(f, a):
result = isEqual
elif sonsLen(a) == sonsLen(f): elif sonsLen(a) == sonsLen(f):
result = isEqual result = isEqual
let firstField = if f.kind == tyTuple: 0 let firstField = if f.kind == tyTuple: 0
@ -349,7 +372,7 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
var y = a.n.sons[i].sym var y = a.n.sons[i].sym
if f.kind == tyObject and typeRel(c, x.typ, y.typ) < isSubtype: if f.kind == tyObject and typeRel(c, x.typ, y.typ) < isSubtype:
return isNone return isNone
if x.name.id != y.name.id and f.kind != tyTuple: return isNone if x.name.id != y.name.id: return isNone
proc allowsNil(f: PType): TTypeRelation {.inline.} = proc allowsNil(f: PType): TTypeRelation {.inline.} =
result = if tfNotNil notin f.flags: isSubtype else: isNone result = if tfNotNil notin f.flags: isSubtype else: isNone
@ -372,17 +395,17 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
# no luck resolving the type, so the inference fails # no luck resolving the type, so the inference fails
return isNone return isNone
let reverseRel = typeRel(c, a, f) let reverseRel = typeRel(c, a, f)
if reverseRel == isGeneric: if reverseRel >= isGeneric:
result = isInferred result = isInferred
inc c.genericMatches #inc c.genericMatches
else: else:
result = typeRel(c, f, a) result = typeRel(c, f, a)
if result <= isSubtype or inconsistentVarTypes(f, a): if result <= isSubtype or inconsistentVarTypes(f, a):
result = isNone result = isNone
if result == isEqual: #if result == isEqual:
inc c.exactMatches # inc c.exactMatches
proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
case a.kind case a.kind
@ -410,7 +433,8 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags: if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags:
return isNone return isNone
elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {}: elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {} and
optThreadAnalysis in gGlobalOptions:
# noSideEffect implies ``tfThread``! # noSideEffect implies ``tfThread``!
return isNone return isNone
elif f.flags * {tfIterator} != a.flags * {tfIterator}: elif f.flags * {tfIterator} != a.flags * {tfIterator}:
@ -424,6 +448,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
return isNone return isNone
when useEffectSystem: when useEffectSystem:
if not compatibleEffects(f, a): return isNone if not compatibleEffects(f, a): return isNone
of tyNil: of tyNil:
result = f.allowsNil result = f.allowsNil
of tyIter: of tyIter:
@ -453,7 +478,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
openScope(c) openScope(c)
inc c.inTypeClass inc c.inTypeClass
finally: defer:
dec c.inTypeClass dec c.inTypeClass
closeScope(c) closeScope(c)
@ -462,10 +487,25 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
var var
typeParamName = ff.base.sons[i-1].sym.name typeParamName = ff.base.sons[i-1].sym.name
typ = ff.sons[i] typ = ff.sons[i]
param = newSym(skType, typeParamName, body.sym, body.sym.info) param: PSym
template paramSym(kind): expr =
newSym(kind, typeParamName, body.sym, body.sym.info)
case typ.kind
of tyStatic:
param = paramSym skConst
param.typ = typ.base
param.ast = typ.n
of tyUnknown:
param = paramSym skVar
param.typ = typ
else:
param = paramSym skType
param.typ = makeTypeDesc(c, typ)
param.typ = makeTypeDesc(c, typ)
addDecl(c, param) addDecl(c, param)
#echo "A ", param.name.s, " ", typeToString(param.typ), " ", param.kind
for param in body.n[0]: for param in body.n[0]:
var var
@ -474,30 +514,19 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
if param.kind == nkVarTy: if param.kind == nkVarTy:
dummyName = param[0] dummyName = param[0]
dummyType = if a.kind != tyVar: makeVarType(c, a) dummyType = if a.kind != tyVar: makeVarType(c, a) else: a
else: a
else: else:
dummyName = param dummyName = param
dummyType = a dummyType = a
internalAssert dummyName.kind == nkIdent internalAssert dummyName.kind == nkIdent
var dummyParam = newSym(skType, dummyName.ident, body.sym, body.sym.info) var dummyParam = newSym(skVar, dummyName.ident, body.sym, body.sym.info)
dummyParam.typ = dummyType dummyParam.typ = dummyType
addDecl(c, dummyParam) addDecl(c, dummyParam)
#echo "B ", dummyName.ident.s, " ", typeToString(dummyType), " ", dummyparam.kind
var checkedBody = c.semTryExpr(c, body.n[3].copyTree) var checkedBody = c.semTryExpr(c, body.n[3].copyTree)
#m.errors = bufferedMsgs
clearBufferedMsgs()
if checkedBody == nil: return isNone if checkedBody == nil: return isNone
if checkedBody.kind == nkStmtList:
for stmt in checkedBody:
case stmt.kind
of nkReturnStmt: discard
of nkTypeSection: discard
of nkConstDef: discard
else: discard
return isGeneric return isGeneric
proc shouldSkipDistinct(rules: PNode, callIdent: PIdent): bool = proc shouldSkipDistinct(rules: PNode, callIdent: PIdent): bool =
@ -517,6 +546,16 @@ proc maybeSkipDistinct(t: PType, callee: PSym): PType =
else: else:
result = t result = t
proc tryResolvingStaticExpr(c: var TCandidate, n: PNode): PNode =
# Consider this example:
# type Value[N: static[int]] = object
# proc foo[N](a: Value[N], r: range[0..(N-1)])
# Here, N-1 will be initially nkStaticExpr that can be evaluated only after
# N is bound to a concrete value during the matching of the first param.
# This proc is used to evaluate such static expressions.
let instantiated = replaceTypesInBody(c.c, c.bindings, n)
result = c.c.semExpr(c.c, instantiated)
proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
# typeRel can be used to establish various relationships between types: # typeRel can be used to establish various relationships between types:
# #
@ -538,7 +577,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
assert(f != nil) assert(f != nil)
if f.kind == tyExpr: if f.kind == tyExpr:
put(c.bindings, f, aOrig) if aOrig != nil: put(c.bindings, f, aOrig)
return isGeneric return isGeneric
assert(aOrig != nil) assert(aOrig != nil)
@ -552,15 +591,14 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
if a.kind == tyGenericInst and if a.kind == tyGenericInst and
skipTypes(f, {tyVar}).kind notin { skipTypes(f, {tyVar}).kind notin {
tyGenericBody, tyGenericInvokation, tyGenericBody, tyGenericInvocation,
tyGenericInst, tyGenericParam} + tyTypeClasses: tyGenericInst, tyGenericParam} + tyTypeClasses:
return typeRel(c, f, lastSon(a)) return typeRel(c, f, lastSon(a))
template bindingRet(res) = template bindingRet(res) =
when res == isGeneric: if doBind:
if doBind: let bound = aOrig.skipTypes({tyRange}).skipIntLit
let bound = aOrig.skipTypes({tyRange}).skipIntLit if doBind: put(c.bindings, f, bound)
if doBind: put(c.bindings, f, bound)
return res return res
template considerPreviousT(body: stmt) {.immediate.} = template considerPreviousT(body: stmt) {.immediate.} =
@ -572,20 +610,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyOr: of tyOr:
# seq[int|string] vs seq[number] # seq[int|string] vs seq[number]
# both int and string must match against number # both int and string must match against number
# but ensure that '[T: A|A]' matches as good as '[T: A]' (bug #2219):
result = isGeneric
for branch in a.sons: for branch in a.sons:
if typeRel(c, f, branch, false) == isNone: let x = typeRel(c, f, branch, false)
return isNone if x == isNone: return isNone
if x < result: result = x
return isGeneric
of tyAnd: of tyAnd:
# seq[Sortable and Iterable] vs seq[Sortable] # seq[Sortable and Iterable] vs seq[Sortable]
# only one match is enough # only one match is enough
for branch in a.sons: for branch in a.sons:
if typeRel(c, f, branch, false) != isNone: let x = typeRel(c, f, branch, false)
return isGeneric if x != isNone:
return if x >= isGeneric: isGeneric else: x
return isNone result = isNone
of tyNot: of tyNot:
case f.kind case f.kind
@ -616,10 +655,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
elif skipTypes(a, {tyRange}).kind == f.kind: result = isSubtype elif skipTypes(a, {tyRange}).kind == f.kind: result = isSubtype
of tyRange: of tyRange:
if a.kind == f.kind: if a.kind == f.kind:
if f.base.kind == tyNone: return isGeneric
result = typeRel(c, base(f), base(a)) result = typeRel(c, base(f), base(a))
# bugfix: accept integer conversions here # bugfix: accept integer conversions here
#if result < isGeneric: result = isNone #if result < isGeneric: result = isNone
if result notin {isNone, isGeneric}: if result notin {isNone, isGeneric}:
# resolve any late-bound static expressions
# that may appear in the range:
for i in 0..1:
if f.n[i].kind == nkStaticExpr:
f.n.sons[i] = tryResolvingStaticExpr(c, f.n[i])
result = typeRangeRel(f, a) result = typeRangeRel(f, a)
else: else:
if skipTypes(f, {tyRange}).kind == a.kind: if skipTypes(f, {tyRange}).kind == a.kind:
@ -657,22 +702,27 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
else: else:
fRange = prev fRange = prev
result = typeRel(c, f.sons[1], a.sons[1]) result = typeRel(c, f.sons[1], a.sons[1])
if result < isGeneric: if result < isGeneric: return isNone
result = isNone if rangeHasStaticIf(fRange):
elif tfUnresolved in fRange.flags and if tfUnresolved in fRange.flags:
rangeHasStaticIf(fRange): # This is a range from an array instantiated with a generic
# This is a range from an array instantiated with a generic # static param. We must extract the static param here and bind
# static param. We must extract the static param here and bind # it to the size of the currently supplied array.
# it to the size of the currently supplied array. var
var rangeStaticT = fRange.getStaticTypeFromRange
rangeStaticT = fRange.getStaticTypeFromRange replacementT = newTypeWithSons(c.c, tyStatic, @[tyInt.getSysType])
replacementT = newTypeWithSons(c.c, tyStatic, @[tyInt.getSysType]) inputUpperBound = a.sons[0].n[1].intVal
inputUpperBound = a.sons[0].n[1].intVal # we must correct for the off-by-one discrepancy between
# we must correct for the off-by-one discrepancy between # ranges and static params:
# ranges and static params: replacementT.n = newIntNode(nkIntLit, inputUpperBound + 1)
replacementT.n = newIntNode(nkIntLit, inputUpperBound + 1) put(c.bindings, rangeStaticT, replacementT)
put(c.bindings, rangeStaticT, replacementT) return isGeneric
result = isGeneric
let len = tryResolvingStaticExpr(c, fRange.n[1])
if len.kind == nkIntLit and len.intVal+1 == lengthOrd(a):
return # if we get this far, the result is already good
else:
return isNone
elif lengthOrd(fRange) != lengthOrd(a): elif lengthOrd(fRange) != lengthOrd(a):
result = isNone result = isNone
else: discard else: discard
@ -737,11 +787,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
inc(c.inheritancePenalty, depth) inc(c.inheritancePenalty, depth)
result = isSubtype result = isSubtype
of tyDistinct: of tyDistinct:
if (a.kind == tyDistinct) and sameDistinctTypes(f, a): result = isEqual if a.kind == tyDistinct and sameDistinctTypes(f, a): result = isEqual
elif c.coerceDistincts: result = typeRel(c, f.base, a) elif c.coerceDistincts: result = typeRel(c, f.base, a)
of tySet: of tySet:
if a.kind == tySet: if a.kind == tySet:
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): if f.sons[0].kind != tyGenericParam and a.sons[0].kind == tyEmpty:
result = isSubtype result = isSubtype
else: else:
result = typeRel(c, f.sons[0], a.sons[0]) result = typeRel(c, f.sons[0], a.sons[0])
@ -821,8 +871,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = typeRel(c, ff, aa) result = typeRel(c, ff, aa)
if result == isNone: return if result == isNone: return
if ff.kind == tyRange and result != isEqual: return isNone if ff.kind == tyRange and result != isEqual: return isNone
#result = isGeneric
result = isGeneric # XXX See bug #2220. A[int] should match A[int] better than some generic X
else: else:
result = typeRel(c, lastSon(f), a) result = typeRel(c, lastSon(f), a)
@ -833,10 +883,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
let ff = lastSon(f) let ff = lastSon(f)
if ff != nil: result = typeRel(c, ff, a) if ff != nil: result = typeRel(c, ff, a)
of tyGenericInvokation: of tyGenericInvocation:
var x = a.skipGenericAlias var x = a.skipGenericAlias
if x.kind == tyGenericInvokation or f.sons[0].kind != tyGenericBody: if x.kind == tyGenericInvocation or f.sons[0].kind != tyGenericBody:
#InternalError("typeRel: tyGenericInvokation -> tyGenericInvokation") #InternalError("typeRel: tyGenericInvocation -> tyGenericInvocation")
# simply no match for now: # simply no match for now:
discard discard
elif x.kind == tyGenericInst and elif x.kind == tyGenericInst and
@ -848,30 +898,49 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
elif typeRel(c, f.sons[i], x.sons[i]) <= isSubtype: return elif typeRel(c, f.sons[i], x.sons[i]) <= isSubtype: return
result = isGeneric result = isGeneric
else: else:
result = typeRel(c, f.sons[0], x) let genericBody = f.sons[0]
result = typeRel(c, genericBody, x)
if result != isNone: if result != isNone:
# see tests/generics/tgeneric3.nim for an example that triggers this
# piece of code:
#
# proc internalFind[T,D](n: PNode[T,D], key: T): ref TItem[T,D]
# proc internalPut[T,D](ANode: ref TNode[T,D], Akey: T, Avalue: D,
# Oldvalue: var D): ref TNode[T,D]
# var root = internalPut[int, int](nil, 312, 312, oldvalue)
# var it1 = internalFind(root, 312) # cannot instantiate: 'D'
#
# we steal the generic parameters from the tyGenericBody: # we steal the generic parameters from the tyGenericBody:
for i in countup(1, sonsLen(f) - 1): for i in countup(1, sonsLen(f) - 1):
var x = PType(idTableGet(c.bindings, f.sons[0].sons[i - 1])) var x = PType(idTableGet(c.bindings, genericBody.sons[i-1]))
if x == nil or x.kind in {tyGenericInvokation, tyGenericParam}: if x == nil:
discard "maybe fine (for eg. a==tyNil)"
elif x.kind in {tyGenericInvocation, tyGenericParam}:
internalError("wrong instantiated type!") internalError("wrong instantiated type!")
put(c.bindings, f.sons[i], x) else:
put(c.bindings, f.sons[i], x)
of tyAnd: of tyAnd:
considerPreviousT: considerPreviousT:
result = isEqual
for branch in f.sons: for branch in f.sons:
if typeRel(c, branch, aOrig) < isSubtype: let x = typeRel(c, branch, aOrig)
return isNone if x < isSubtype: return isNone
# 'and' implies minimum matching result:
bindingRet isGeneric if x < result: result = x
bindingRet result
of tyOr: of tyOr:
considerPreviousT: considerPreviousT:
result = isNone
for branch in f.sons: for branch in f.sons:
if typeRel(c, branch, aOrig) >= isSubtype: let x = typeRel(c, branch, aOrig)
bindingRet isGeneric # 'or' implies maximum matching result:
if x > result: result = x
return isNone if result >= isSubtype:
bindingRet result
else:
result = isNone
of tyNot: of tyNot:
considerPreviousT: considerPreviousT:
@ -930,7 +999,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
else: else:
internalAssert a.sons != nil and a.sons.len > 0 internalAssert a.sons != nil and a.sons.len > 0
c.typedescMatched = true c.typedescMatched = true
result = typeRel(c, f.base, a.base) var aa = a
while aa.kind in {tyTypeDesc, tyGenericParam} and
aa.len > 0:
aa = lastSon(aa)
result = typeRel(c, f.base, aa)
if result > isGeneric: result = isGeneric
else: else:
result = isNone result = isNone
else: else:
@ -954,20 +1028,38 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
return isNone return isNone
if doBind: if doBind:
put(c.bindings, f, concrete) put(c.bindings, f, concrete)
elif result > isGeneric:
result = isGeneric
elif a.kind == tyEmpty: elif a.kind == tyEmpty:
result = isGeneric result = isGeneric
elif x.kind == tyGenericParam: elif x.kind == tyGenericParam:
result = isGeneric result = isGeneric
else: else:
result = typeRel(c, x, a) # check if it fits result = typeRel(c, x, a) # check if it fits
if result > isGeneric: result = isGeneric
of tyStatic: of tyStatic:
if aOrig.kind == tyStatic: let prev = PType(idTableGet(c.bindings, f))
result = typeRel(c, f.lastSon, a) if prev == nil:
if result != isNone: put(c.bindings, f, aOrig) if aOrig.kind == tyStatic:
result = typeRel(c, f.lastSon, a)
if result != isNone and f.n != nil:
if not exprStructuralEquivalent(f.n, aOrig.n):
result = isNone
if result != isNone: put(c.bindings, f, aOrig)
else:
result = isNone
elif prev.kind == tyStatic:
if aOrig.kind == tyStatic:
result = typeRel(c, prev.lastSon, a)
if result != isNone and prev.n != nil:
if not exprStructuralEquivalent(prev.n, aOrig.n):
result = isNone
else: result = isNone
else: else:
# XXX endless recursion?
#result = typeRel(c, prev, aOrig)
result = isNone result = isNone
of tyTypeDesc: of tyTypeDesc:
var prev = PType(idTableGet(c.bindings, f)) var prev = PType(idTableGet(c.bindings, f))
if prev == nil: if prev == nil:
@ -1006,22 +1098,24 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyFromExpr: of tyFromExpr:
# fix the expression, so it contains the already instantiated types # fix the expression, so it contains the already instantiated types
let instantiated = replaceTypesInBody(c.c, c.bindings, f.n) if f.n == nil or f.n.kind == nkEmpty: return isGeneric
let reevaluted = c.c.semExpr(c.c, instantiated) let reevaluated = tryResolvingStaticExpr(c, f.n)
case reevaluted.typ.kind case reevaluated.typ.kind
of tyTypeDesc: of tyTypeDesc:
result = typeRel(c, a, reevaluted.typ.base) result = typeRel(c, a, reevaluated.typ.base)
of tyStatic: of tyStatic:
result = typeRel(c, a, reevaluted.typ.base) result = typeRel(c, a, reevaluated.typ.base)
if result != isNone and reevaluted.typ.n != nil: if result != isNone and reevaluated.typ.n != nil:
if not exprStructuralEquivalent(aOrig.n, reevaluted.typ.n): if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n):
result = isNone result = isNone
else: else:
localError(f.n.info, errTypeExpected) localError(f.n.info, errTypeExpected)
result = isNone result = isNone
of tyNone:
if a.kind == tyNone: result = isEqual
else: else:
internalAssert false internalError " unknown type kind " & $f.kind
proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation = proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
var m: TCandidate var m: TCandidate
@ -1041,7 +1135,7 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
c: PContext): PNode = c: PContext): PNode =
result = newNodeI(kind, arg.info) result = newNodeI(kind, arg.info)
if containsGenericType(f): if containsGenericType(f):
if not m.proxyMatch: if not m.hasFauxMatch:
result.typ = getInstantiatedType(c, arg, m, f) result.typ = getInstantiatedType(c, arg, m, f)
else: else:
result.typ = errorType(c) result.typ = errorType(c)
@ -1106,6 +1200,17 @@ proc isInlineIterator*(t: PType): bool =
result = t.kind == tyIter or result = t.kind == tyIter or
(t.kind == tyBuiltInTypeClass and t.base.kind == tyIter) (t.kind == tyBuiltInTypeClass and t.base.kind == tyIter)
proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
case r
of isConvertible, isIntConv: inc(m.convMatches, convMatch)
of isSubtype, isSubrange: inc(m.subtypeMatches)
of isGeneric, isInferred: inc(m.genericMatches)
of isFromIntLit: inc(m.intConvMatches, 256)
of isInferredConvertible:
inc(m.convMatches)
of isEqual: inc(m.exactMatches)
of isNone: discard
proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
argSemantized, argOrig: PNode): PNode = argSemantized, argOrig: PNode): PNode =
var var
@ -1127,9 +1232,8 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
return argSemantized return argSemantized
if argType.kind == tyStatic: if argType.kind == tyStatic:
if m.callee.kind == tyGenericBody: if m.callee.kind == tyGenericBody and tfGenericTypeParam notin argType.flags:
result = newNodeI(nkType, argOrig.info) result = newNodeIT(nkType, argOrig.info, makeTypeFromExpr(c, arg))
result.typ = makeTypeFromExpr(c, arg)
return return
else: else:
var evaluated = c.semTryConstExpr(c, arg) var evaluated = c.semTryConstExpr(c, arg)
@ -1147,17 +1251,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
if r != isNone and m.calleeSym != nil and if r != isNone and m.calleeSym != nil and
m.calleeSym.kind in {skMacro, skTemplate}: m.calleeSym.kind in {skMacro, skTemplate}:
# XXX: duplicating this is ugly, maybe we should move this # XXX: duplicating this is ugly, but we cannot (!) move this
# directly into typeRel using return-like templates # directly into typeRel using return-like templates
case r incMatches(m, r)
of isConvertible, isIntConv: inc(m.convMatches)
of isSubtype, isSubrange: inc(m.subtypeMatches)
of isGeneric, isInferred: inc(m.genericMatches)
of isInferredConvertible: inc(m.genericMatches); inc(m.convMatches)
of isFromIntLit: inc(m.intConvMatches, 256)
of isEqual: inc(m.exactMatches)
of isNone: discard
if f.kind == tyStmt: if f.kind == tyStmt:
return arg return arg
elif f.kind == tyTypeDesc: elif f.kind == tyTypeDesc:
@ -1165,7 +1261,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
elif f.kind == tyStatic: elif f.kind == tyStatic:
return arg.typ.n return arg.typ.n
else: else:
return argOrig return argSemantized # argOrig
if r != isNone and f.isInlineIterator: if r != isNone and f.isInlineIterator:
var inlined = newTypeS(tyStatic, c) var inlined = newTypeS(tyStatic, c)
@ -1177,21 +1273,22 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
case r case r
of isConvertible: of isConvertible:
inc(m.convMatches) inc(m.convMatches)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenStdConv, f, arg, m, c)
of isIntConv: of isIntConv:
# I'm too lazy to introduce another ``*matches`` field, so we conflate # I'm too lazy to introduce another ``*matches`` field, so we conflate
# ``isIntConv`` and ``isIntLit`` here: # ``isIntConv`` and ``isIntLit`` here:
inc(m.intConvMatches) inc(m.intConvMatches)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenStdConv, f, arg, m, c)
of isSubtype: of isSubtype:
inc(m.subtypeMatches) inc(m.subtypeMatches)
result = implicitConv(nkHiddenSubConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenSubConv, f, arg, m, c)
of isSubrange: of isSubrange:
inc(m.subtypeMatches) inc(m.subtypeMatches)
#result = copyTree(arg) if f.kind == tyVar:
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = arg
else:
result = implicitConv(nkHiddenStdConv, f, arg, m, c)
of isInferred, isInferredConvertible: of isInferred, isInferredConvertible:
inc(m.genericMatches)
if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds: if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
result = c.semInferredLambda(c, m.bindings, arg) result = c.semInferredLambda(c, m.bindings, arg)
else: else:
@ -1200,30 +1297,35 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
if r == isInferredConvertible: if r == isInferredConvertible:
inc(m.convMatches) inc(m.convMatches)
result = implicitConv(nkHiddenStdConv, f, result, m, c) result = implicitConv(nkHiddenStdConv, f, result, m, c)
else:
inc(m.genericMatches)
of isGeneric: of isGeneric:
inc(m.genericMatches) inc(m.genericMatches)
result = copyTree(arg) if arg.typ == nil:
result.typ = getInstantiatedType(c, arg, m, f) result = arg
# BUG: f may not be the right key! elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple:
if skipTypes(result.typ, abstractVar-{tyTypeDesc}).kind in {tyTuple}: result = implicitConv(nkHiddenSubConv, f, arg, m, c)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) elif arg.typ.isEmptyContainer:
# BUGFIX: use ``result.typ`` and not `f` here result = arg.copyTree
result.typ = getInstantiatedType(c, arg, m, f)
else:
result = arg
of isFromIntLit: of isFromIntLit:
# too lazy to introduce another ``*matches`` field, so we conflate # too lazy to introduce another ``*matches`` field, so we conflate
# ``isIntConv`` and ``isIntLit`` here: # ``isIntConv`` and ``isIntLit`` here:
inc(m.intConvMatches, 256) inc(m.intConvMatches, 256)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenStdConv, f, arg, m, c)
of isEqual: of isEqual:
inc(m.exactMatches) inc(m.exactMatches)
result = copyTree(arg) result = arg
if skipTypes(f, abstractVar-{tyTypeDesc}).kind in {tyTuple}: if skipTypes(f, abstractVar-{tyTypeDesc}).kind in {tyTuple}:
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenSubConv, f, arg, m, c)
of isNone: of isNone:
# do not do this in ``typeRel`` as it then can't infere T in ``ref T``: # do not do this in ``typeRel`` as it then can't infere T in ``ref T``:
if a.kind == tyProxy: if a.kind in {tyProxy, tyUnknown}:
inc(m.genericMatches) inc(m.genericMatches)
m.proxyMatch = true m.fauxMatch = a.kind
return copyTree(arg) return arg
result = userConvMatch(c, m, f, a, arg) result = userConvMatch(c, m, f, a, arg)
# check for a base type match, which supports varargs[T] without [] # check for a base type match, which supports varargs[T] without []
# constructor in a call: # constructor in a call:
@ -1250,7 +1352,6 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
# incorrect to simply use the first fitting match. However, to implement # incorrect to simply use the first fitting match. However, to implement
# this correctly is inefficient. We have to copy `m` here to be able to # this correctly is inefficient. We have to copy `m` here to be able to
# roll back the side effects of the unification algorithm. # roll back the side effects of the unification algorithm.
let c = m.c let c = m.c
var x, y, z: TCandidate var x, y, z: TCandidate
initCandidate(c, x, m.callee) initCandidate(c, x, m.callee)
@ -1263,15 +1364,27 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
for i in countup(0, sonsLen(arg) - 1): for i in countup(0, sonsLen(arg) - 1):
if arg.sons[i].sym.kind in {skProc, skMethod, skConverter}+skIterators: if arg.sons[i].sym.kind in {skProc, skMethod, skConverter}+skIterators:
copyCandidate(z, m) copyCandidate(z, m)
var r = typeRel(z, f, arg.sons[i].typ) z.callee = arg.sons[i].typ
z.calleeSym = arg.sons[i].sym
#if arg.sons[i].sym.name.s == "cmp":
# ggDebug = true
# echo "CALLLEEEEEEEE A ", typeToString(z.callee)
# XXX this is still all wrong: (T, T) should be 2 generic matches
# and (int, int) 2 exact matches, etc. Essentially you cannot call
# typeRel here and expect things to work!
let r = typeRel(z, f, arg.sons[i].typ)
incMatches(z, r, 2)
#if arg.sons[i].sym.name.s == "cmp": # and arg.info.line == 606:
# echo "M ", r, " ", arg.info, " ", typeToString(arg.sons[i].sym.typ)
# writeMatches(z)
if r != isNone: if r != isNone:
z.state = csMatch
case x.state case x.state
of csEmpty, csNoMatch: of csEmpty, csNoMatch:
x = z x = z
best = i best = i
x.state = csMatch
of csMatch: of csMatch:
var cmp = cmpCandidates(x, z) let cmp = cmpCandidates(x, z)
if cmp < 0: if cmp < 0:
best = i best = i
x = z x = z
@ -1279,11 +1392,14 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
y = z # z is as good as x y = z # z is as good as x
if x.state == csEmpty: if x.state == csEmpty:
result = nil result = nil
elif (y.state == csMatch) and (cmpCandidates(x, y) == 0): elif y.state == csMatch and cmpCandidates(x, y) == 0:
if x.state != csMatch: if x.state != csMatch:
internalError(arg.info, "x.state is not csMatch") internalError(arg.info, "x.state is not csMatch")
# ambiguous: more than one symbol fits # ambiguous: more than one symbol fits!
result = nil # See tsymchoice_for_expr as an example. 'f.kind == tyExpr' should match
# anyway:
if f.kind == tyExpr: result = arg
else: result = nil
else: else:
# only one valid interpretation found: # only one valid interpretation found:
markUsed(arg.info, arg.sons[best].sym) markUsed(arg.info, arg.sons[best].sym)
@ -1291,6 +1407,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best], result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best],
argOrig) argOrig)
proc setSon(father: PNode, at: int, son: PNode) = proc setSon(father: PNode, at: int, son: PNode) =
if sonsLen(father) <= at: setLen(father.sons, at + 1) if sonsLen(father) <= at: setLen(father.sons, at + 1)
father.sons[at] = son father.sons[at] = son
@ -1302,9 +1419,12 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode =
# a.typ == nil is valid # a.typ == nil is valid
result = a result = a
elif a.typ.isNil: elif a.typ.isNil:
# XXX This is unsound! 'formal' can differ from overloaded routine to
# overloaded routine!
let flags = if formal.kind == tyIter: {efDetermineType, efWantIterator} let flags = if formal.kind == tyIter: {efDetermineType, efWantIterator}
elif formal.kind == tyStmt: {efDetermineType, efWantStmt} else: {efDetermineType, efAllowStmt}
else: {efDetermineType} #elif formal.kind == tyStmt: {efDetermineType, efWantStmt}
#else: {efDetermineType}
result = c.semOperand(c, a, flags) result = c.semOperand(c, a, flags)
else: else:
result = a result = a
@ -1344,6 +1464,10 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
else: else:
m.state = csNoMatch m.state = csNoMatch
return return
if formal.typ.kind == tyVar:
if not n.isLValue:
m.state = csNoMatch
return
var var
# iterates over formal parameters # iterates over formal parameters
@ -1389,13 +1513,14 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
return return
checkConstraint(n.sons[a].sons[1]) checkConstraint(n.sons[a].sons[1])
if m.baseTypeMatch: if m.baseTypeMatch:
assert(container == nil) #assert(container == nil)
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg)) container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
addSon(container, arg) addSon(container, arg)
setSon(m.call, formal.position + 1, container) setSon(m.call, formal.position + 1, container)
if f != formalLen - 1: container = nil if f != formalLen - 1: container = nil
else: else:
setSon(m.call, formal.position + 1, arg) setSon(m.call, formal.position + 1, arg)
inc f
else: else:
# unnamed param # unnamed param
if f >= formalLen: if f >= formalLen:
@ -1409,12 +1534,14 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
copyTree(n.sons[a]), m, c)) copyTree(n.sons[a]), m, c))
else: else:
addSon(m.call, copyTree(n.sons[a])) addSon(m.call, copyTree(n.sons[a]))
elif formal != nil: elif formal != nil and formal.typ.kind == tyVarargs:
# beware of the side-effects in 'prepareOperand'! So only do it for
# varags matching. See tests/metatype/tstatic_overloading.
m.baseTypeMatch = false m.baseTypeMatch = false
n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
n.sons[a], nOrig.sons[a]) n.sons[a], nOrig.sons[a])
if (arg != nil) and m.baseTypeMatch and (container != nil): if arg != nil and m.baseTypeMatch and container != nil:
addSon(container, arg) addSon(container, arg)
incrIndexType(container.typ) incrIndexType(container.typ)
else: else:
@ -1428,7 +1555,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
internalError(n.sons[a].info, "matches") internalError(n.sons[a].info, "matches")
return return
formal = m.callee.n.sons[f].sym formal = m.callee.n.sons[f].sym
if containsOrIncl(marker, formal.position): if containsOrIncl(marker, formal.position) and container.isNil:
# already in namedParams: # already in namedParams:
localError(n.sons[a].info, errCannotBindXTwice, formal.name.s) localError(n.sons[a].info, errCannotBindXTwice, formal.name.s)
m.state = csNoMatch m.state = csNoMatch
@ -1441,17 +1568,24 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
m.state = csNoMatch m.state = csNoMatch
return return
if m.baseTypeMatch: if m.baseTypeMatch:
assert(container == nil) #assert(container == nil)
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg)) if container.isNil:
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
else:
incrIndexType(container.typ)
addSon(container, arg) addSon(container, arg)
setSon(m.call, formal.position + 1, setSon(m.call, formal.position + 1,
implicitConv(nkHiddenStdConv, formal.typ, container, m, c)) implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
if f != formalLen - 1: container = nil #if f != formalLen - 1: container = nil
# pick the formal from the end, so that 'x, y, varargs, z' works:
f = max(f, formalLen - n.len + a + 1)
else: else:
setSon(m.call, formal.position + 1, arg) setSon(m.call, formal.position + 1, arg)
inc(f)
container = nil
checkConstraint(n.sons[a]) checkConstraint(n.sons[a])
inc(a) inc(a)
inc(f)
proc semFinishOperands*(c: PContext, n: PNode) = proc semFinishOperands*(c: PContext, n: PNode) =
# this needs to be called to ensure that after overloading resolution every # this needs to be called to ensure that after overloading resolution every
@ -1496,12 +1630,15 @@ proc argtypeMatches*(c: PContext, f, a: PType): bool =
# instantiate generic converters for that # instantiate generic converters for that
result = res != nil result = res != nil
proc instDeepCopy*(c: PContext; dc: PSym; t: PType; info: TLineInfo): PSym {. proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
procvar.} = op: TTypeAttachedOp): PSym {.procvar.} =
var m: TCandidate var m: TCandidate
initCandidate(c, m, dc.typ) initCandidate(c, m, dc.typ)
var f = dc.typ.sons[1] var f = dc.typ.sons[1]
if f.kind in {tyRef, tyPtr}: f = f.lastSon if op == attachedDeepCopy:
if f.kind in {tyRef, tyPtr}: f = f.lastSon
else:
if f.kind == tyVar: f = f.lastSon
if typeRel(m, f, t) == isNone: if typeRel(m, f, t) == isNone:
localError(info, errGenerated, "cannot instantiate 'deepCopy'") localError(info, errGenerated, "cannot instantiate 'deepCopy'")
else: else:
@ -1567,7 +1704,7 @@ tests:
setup: setup:
var c: TCandidate var c: TCandidate
InitCandidate(nil, c, nil) initCandidate(nil, c, nil)
template yes(x, y) = template yes(x, y) =
test astToStr(x) & " is " & astToStr(y): test astToStr(x) & " is " & astToStr(y):

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2013 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -22,44 +22,57 @@ const
#template sectionSuggest(): expr = "##begin\n" & getStackTrace() & "##end\n" #template sectionSuggest(): expr = "##begin\n" & getStackTrace() & "##end\n"
proc origModuleName(m: PSym): string = template origModuleName(m: PSym): string = m.name.s
result = if m.position == gDirtyBufferIdx:
fileInfos[gDirtyOriginalIdx].shortName
else:
m.name.s
proc symToStr(s: PSym, isLocal: bool, section: string, li: TLineInfo): string = proc symToStr(s: PSym, isLocal: bool, section: string, li: TLineInfo): string =
result = section result = section
result.add(sep) result.add(sep)
result.add($s.kind) if optIdeTerse in gGlobalOptions:
result.add(sep) if s.kind in routineKinds:
if not isLocal and s.kind != skModule: result.add renderTree(s.ast, {renderNoBody, renderNoComments,
let ow = s.owner renderDocComments, renderNoPragmas})
if ow.kind != skModule and ow.owner != nil: else:
let ow2 = ow.owner result.add s.name.s
result.add(ow2.origModuleName) result.add(sep)
result.add(toFullPath(li))
result.add(sep)
result.add($toLinenumber(li))
result.add(sep)
result.add($toColumn(li))
else:
result.add($s.kind)
result.add(sep)
if not isLocal and s.kind != skModule:
let ow = s.owner
if ow.kind != skModule and ow.owner != nil:
let ow2 = ow.owner
result.add(ow2.origModuleName)
result.add('.')
result.add(ow.origModuleName)
result.add('.') result.add('.')
result.add(ow.origModuleName) result.add(s.name.s)
result.add('.') result.add(sep)
result.add(s.name.s) if s.typ != nil:
result.add(sep) result.add(typeToString(s.typ))
if s.typ != nil: result.add(sep)
result.add(typeToString(s.typ)) result.add(toFullPath(li))
result.add(sep) result.add(sep)
result.add(toFullPath(li)) result.add($toLinenumber(li))
result.add(sep) result.add(sep)
result.add($toLinenumber(li)) result.add($toColumn(li))
result.add(sep) result.add(sep)
result.add($toColumn(li)) when not defined(noDocgen):
result.add(sep) result.add(s.extractDocComment.escape)
when not defined(noDocgen):
result.add(s.extractDocComment.escape)
proc symToStr(s: PSym, isLocal: bool, section: string): string = proc symToStr(s: PSym, isLocal: bool, section: string): string =
result = symToStr(s, isLocal, section, s.info) result = symToStr(s, isLocal, section, s.info)
proc filterSym(s: PSym): bool {.inline.} = proc filterSym(s: PSym): bool {.inline.} =
result = s.name.s[0] in lexer.SymChars and s.kind != skModule result = s.kind != skModule
proc filterSymNoOpr(s: PSym): bool {.inline.} =
result = s.kind != skModule and s.name.s[0] in lexer.SymChars and
not isKeyword(s.name)
proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} = proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
let fmoduleId = getModule(f).id let fmoduleId = getModule(f).id
@ -74,9 +87,6 @@ proc suggestField(c: PContext, s: PSym, outputs: var int) =
suggestWriteln(symToStr(s, isLocal=true, sectionSuggest)) suggestWriteln(symToStr(s, isLocal=true, sectionSuggest))
inc outputs inc outputs
when not defined(nimhygiene):
{.pragma: inject.}
template wholeSymTab(cond, section: expr) {.immediate.} = template wholeSymTab(cond, section: expr) {.immediate.} =
var isLocal = true var isLocal = true
for scope in walkScopes(c.currentScope): for scope in walkScopes(c.currentScope):
@ -134,11 +144,20 @@ proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var int) =
proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} = proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
if s.typ != nil and sonsLen(s.typ) > 1 and s.typ.sons[1] != nil: if s.typ != nil and sonsLen(s.typ) > 1 and s.typ.sons[1] != nil:
# special rule: if system and some weird generic match via 'tyExpr'
# or 'tyGenericParam' we won't list it either to reduce the noise (nobody
# wants 'system.`-|` as suggestion
let m = s.getModule()
if m != nil and sfSystemModule in m.flags:
if s.kind == skType: return
var exp = s.typ.sons[1].skipTypes({tyGenericInst, tyVar})
if exp.kind == tyVarargs: exp = elemType(exp)
if exp.kind in {tyExpr, tyStmt, tyGenericParam, tyAnything}: return
result = sigmatch.argtypeMatches(c, s.typ.sons[1], firstArg) result = sigmatch.argtypeMatches(c, s.typ.sons[1], firstArg)
proc suggestOperations(c: PContext, n: PNode, typ: PType, outputs: var int) = proc suggestOperations(c: PContext, n: PNode, typ: PType, outputs: var int) =
assert typ != nil assert typ != nil
wholeSymTab(filterSym(it) and typeFits(c, it, typ), sectionSuggest) wholeSymTab(filterSymNoOpr(it) and typeFits(c, it, typ), sectionSuggest)
proc suggestEverything(c: PContext, n: PNode, outputs: var int) = proc suggestEverything(c: PContext, n: PNode, outputs: var int) =
# do not produce too many symbols: # do not produce too many symbols:
@ -194,19 +213,28 @@ proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) =
else: else:
suggestOperations(c, n, typ, outputs) suggestOperations(c, n, typ, outputs)
type
TCheckPointResult = enum
cpNone, cpFuzzy, cpExact
proc inCheckpoint(current: TLineInfo): TCheckPointResult =
if current.fileIndex == gTrackPos.fileIndex:
if current.line == gTrackPos.line and
abs(current.col-gTrackPos.col) < 4:
return cpExact
if current.line >= gTrackPos.line:
return cpFuzzy
proc findClosestDot(n: PNode): PNode = proc findClosestDot(n: PNode): PNode =
if n.kind == nkDotExpr and msgs.inCheckpoint(n.info) == cpExact: if n.kind == nkDotExpr and inCheckpoint(n.info) == cpExact:
result = n result = n
else: else:
for i in 0.. <safeLen(n): for i in 0.. <safeLen(n):
result = findClosestDot(n.sons[i]) result = findClosestDot(n.sons[i])
if result != nil: return if result != nil: return
const
CallNodes = {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit}
proc findClosestCall(n: PNode): PNode = proc findClosestCall(n: PNode): PNode =
if n.kind in CallNodes and msgs.inCheckpoint(n.info) == cpExact: if n.kind in nkCallKinds and inCheckpoint(n.info) == cpExact:
result = n result = n
else: else:
for i in 0.. <safeLen(n): for i in 0.. <safeLen(n):
@ -214,34 +242,20 @@ proc findClosestCall(n: PNode): PNode =
if result != nil: return if result != nil: return
proc isTracked(current: TLineInfo, tokenLen: int): bool = proc isTracked(current: TLineInfo, tokenLen: int): bool =
for i in countup(0, high(checkPoints)): if current.fileIndex == gTrackPos.fileIndex:
if current.fileIndex == checkPoints[i].fileIndex: if current.line == gTrackPos.line:
if current.line == checkPoints[i].line: let col = gTrackPos.col
let col = checkPoints[i].col if col >= current.col and col <= current.col+tokenLen-1:
if col >= current.col and col <= current.col+tokenLen-1: return true
return true
proc findClosestSym(n: PNode): PNode = proc findClosestSym(n: PNode): PNode =
if n.kind == nkSym and msgs.inCheckpoint(n.info) == cpExact: if n.kind == nkSym and inCheckpoint(n.info) == cpExact:
result = n result = n
elif n.kind notin {nkNone..nkNilLit}: elif n.kind notin {nkNone..nkNilLit}:
for i in 0.. <sonsLen(n): for i in 0.. <sonsLen(n):
result = findClosestSym(n.sons[i]) result = findClosestSym(n.sons[i])
if result != nil: return if result != nil: return
proc safeSemExpr(c: PContext, n: PNode): PNode =
try:
result = c.semExpr(c, n)
except ERecoverableError:
result = ast.emptyNode
proc fuzzySemCheck(c: PContext, n: PNode): PNode =
result = safeSemExpr(c, n)
if result == nil or result.kind == nkEmpty:
result = newNodeI(n.kind, n.info)
if n.kind notin {nkNone..nkNilLit}:
for i in 0 .. < sonsLen(n): result.addSon(fuzzySemCheck(c, n.sons[i]))
var var
usageSym*: PSym usageSym*: PSym
lastLineInfo: TLineInfo lastLineInfo: TLineInfo
@ -261,69 +275,18 @@ proc findDefinition(info: TLineInfo; s: PSym) =
suggestWriteln(symToStr(s, isLocal=false, sectionDef)) suggestWriteln(symToStr(s, isLocal=false, sectionDef))
suggestQuit() suggestQuit()
type
TSourceMap = object
lines: seq[TLineMap]
TEntry = object
pos: int
sym: PSym
TLineMap = object
entries: seq[TEntry]
var
gSourceMaps: seq[TSourceMap] = @[]
proc ensureIdx[T](x: var T, y: int) = proc ensureIdx[T](x: var T, y: int) =
if x.len <= y: x.setLen(y+1) if x.len <= y: x.setLen(y+1)
proc ensureSeq[T](x: var seq[T]) = proc ensureSeq[T](x: var seq[T]) =
if x == nil: newSeq(x, 0) if x == nil: newSeq(x, 0)
proc resetSourceMap*(fileIdx: int32) =
ensureIdx(gSourceMaps, fileIdx)
gSourceMaps[fileIdx].lines = @[]
proc addToSourceMap(sym: PSym, info: TLineInfo) =
ensureIdx(gSourceMaps, info.fileIndex)
ensureSeq(gSourceMaps[info.fileIndex].lines)
ensureIdx(gSourceMaps[info.fileIndex].lines, info.line)
ensureSeq(gSourceMaps[info.fileIndex].lines[info.line].entries)
gSourceMaps[info.fileIndex].lines[info.line].entries.add(TEntry(pos: info.col, sym: sym))
proc defFromLine(entries: var seq[TEntry], col: int32) =
if entries == nil: return
# The sorting is done lazily here on purpose.
# No need to pay the price for it unless the user requests
# "goto definition" on a particular line
sort(entries) do (a,b: TEntry) -> int:
return cmp(a.pos, b.pos)
for e in entries:
# currently, the line-infos for most expressions point to
# one position past the end of the expression. This means
# that the first expr that ends after the cursor column is
# the one we are looking for.
if e.pos >= col:
suggestWriteln(symToStr(e.sym, isLocal=false, sectionDef))
return
proc defFromSourceMap*(i: TLineInfo) =
if not ((i.fileIndex < gSourceMaps.len) and
(gSourceMaps[i.fileIndex].lines != nil) and
(i.line < gSourceMaps[i.fileIndex].lines.len)): return
defFromLine(gSourceMaps[i.fileIndex].lines[i.line].entries, i.col)
proc suggestSym*(info: TLineInfo; s: PSym) {.inline.} = proc suggestSym*(info: TLineInfo; s: PSym) {.inline.} =
## misnamed: should be 'symDeclared' ## misnamed: should be 'symDeclared'
if optUsages in gGlobalOptions: if gIdeCmd == ideUse:
findUsages(info, s) findUsages(info, s)
if optDef in gGlobalOptions: elif gIdeCmd == ideDef:
findDefinition(info, s) findDefinition(info, s)
if isServing:
addToSourceMap(s, info)
proc markUsed(info: TLineInfo; s: PSym) = proc markUsed(info: TLineInfo; s: PSym) =
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
@ -336,31 +299,39 @@ proc useSym*(sym: PSym): PNode =
result = newSymNode(sym) result = newSymNode(sym)
markUsed(result.info, sym) markUsed(result.info, sym)
proc safeSemExpr*(c: PContext, n: PNode): PNode =
# use only for idetools support!
try:
result = c.semExpr(c, n)
except ERecoverableError:
result = ast.emptyNode
proc suggestExpr*(c: PContext, node: PNode) = proc suggestExpr*(c: PContext, node: PNode) =
var cp = msgs.inCheckpoint(node.info) if nfIsCursor notin node.flags:
if cp == cpNone: return if gTrackPos.line < 0: return
var cp = inCheckpoint(node.info)
if cp == cpNone: return
var outputs = 0 var outputs = 0
# This keeps semExpr() from coming here recursively: # This keeps semExpr() from coming here recursively:
if c.inCompilesContext > 0: return if c.inCompilesContext > 0: return
inc(c.inCompilesContext) inc(c.inCompilesContext)
if optSuggest in gGlobalOptions: if gIdeCmd == ideSug:
var n = findClosestDot(node) var n = if nfIsCursor in node.flags: node else: findClosestDot(node)
if n == nil: n = node if n == nil: n = node
else: cp = cpExact if n.kind == nkDotExpr:
if n.kind == nkDotExpr and cp == cpExact:
var obj = safeSemExpr(c, n.sons[0]) var obj = safeSemExpr(c, n.sons[0])
suggestFieldAccess(c, obj, outputs) suggestFieldAccess(c, obj, outputs)
if optIdeDebug in gGlobalOptions:
echo "expression ", renderTree(obj), " has type ", typeToString(obj.typ)
#writeStackTrace()
else: else:
#debug n
suggestEverything(c, n, outputs) suggestEverything(c, n, outputs)
if optContext in gGlobalOptions: elif gIdeCmd == ideCon:
var n = findClosestCall(node) var n = if nfIsCursor in node.flags: node else: findClosestCall(node)
if n == nil: n = node if n == nil: n = node
else: cp = cpExact if n.kind in nkCallKinds:
if n.kind in CallNodes:
var a = copyNode(n) var a = copyNode(n)
var x = safeSemExpr(c, n.sons[0]) var x = safeSemExpr(c, n.sons[0])
if x.kind == nkEmpty or x.typ == nil: x = n.sons[0] if x.kind == nkEmpty or x.typ == nil: x = n.sons[0]
@ -373,13 +344,7 @@ proc suggestExpr*(c: PContext, node: PNode) =
suggestCall(c, a, n, outputs) suggestCall(c, a, n, outputs)
dec(c.inCompilesContext) dec(c.inCompilesContext)
if outputs > 0 and optUsages notin gGlobalOptions: suggestQuit() if outputs > 0 and gIdeCmd != ideUse: suggestQuit()
proc suggestStmt*(c: PContext, n: PNode) = proc suggestStmt*(c: PContext, n: PNode) =
suggestExpr(c, n) suggestExpr(c, n)
proc findSuggest*(c: PContext, n: PNode) =
if n == nil: return
suggestExpr(c, n)
for i in 0.. <safeLen(n):
findSuggest(c, n.sons[i])

View file

@ -45,7 +45,7 @@ proc parseFile(fileIdx: int32): PNode =
var var
p: TParsers p: TParsers
f: File f: File
let filename = fileIdx.toFullPath let filename = fileIdx.toFullPathConsiderDirty
if not open(f, filename): if not open(f, filename):
rawMessage(errCannotOpenFile, filename) rawMessage(errCannotOpenFile, filename)
return return
@ -163,7 +163,7 @@ proc evalPipe(p: var TParsers, n: PNode, filename: string,
proc openParsers(p: var TParsers, fileIdx: int32, inputstream: PLLStream) = proc openParsers(p: var TParsers, fileIdx: int32, inputstream: PLLStream) =
var s: PLLStream var s: PLLStream
p.skin = skinStandard p.skin = skinStandard
let filename = fileIdx.toFullPath let filename = fileIdx.toFullPathConsiderDirty
var pipe = parsePipe(filename, inputstream) var pipe = parsePipe(filename, inputstream)
if pipe != nil: s = evalPipe(p, pipe, filename, inputstream) if pipe != nil: s = evalPipe(p, pipe, filename, inputstream)
else: s = inputstream else: s = inputstream

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -152,6 +152,7 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode =
defs[0] = newSymNode(newVar).PTransNode defs[0] = newSymNode(newVar).PTransNode
defs[1] = it.sons[1].PTransNode defs[1] = it.sons[1].PTransNode
defs[2] = transform(c, it.sons[2]) defs[2] = transform(c, it.sons[2])
newVar.ast = defs[2].PNode
result[i] = defs result[i] = defs
else: else:
if it.kind != nkVarTuple: if it.kind != nkVarTuple:
@ -321,6 +322,7 @@ proc transformYield(c: PTransf, n: PNode): PTransNode =
proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode = proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
result = transformSons(c, n) result = transformSons(c, n)
if gCmd == cmdCompileToCpp or sfCompileToCpp in c.module.flags: return
var n = result.PNode var n = result.PNode
case n.sons[0].kind case n.sons[0].kind
of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
@ -377,6 +379,9 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
result = transformSons(c, n) result = transformSons(c, n)
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
result = transform(c, n.sons[1]) result = transform(c, n.sons[1])
PNode(result).typ = takeType(n.typ, n.sons[1].typ)
#echo n.info, " came here and produced ", typeToString(PNode(result).typ),
# " from ", typeToString(n.typ), " and ", typeToString(n.sons[1].typ)
of tyCString: of tyCString:
if source.kind == tyString: if source.kind == tyString:
result = newTransNode(nkStringToCString, n, 1) result = newTransNode(nkStringToCString, n, 1)
@ -397,7 +402,7 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
if diff < 0: if diff < 0:
result = newTransNode(nkObjUpConv, n, 1) result = newTransNode(nkObjUpConv, n, 1)
result[0] = transform(c, n.sons[1]) result[0] = transform(c, n.sons[1])
elif diff > 0: elif diff > 0 and diff != high(int):
result = newTransNode(nkObjDownConv, n, 1) result = newTransNode(nkObjDownConv, n, 1)
result[0] = transform(c, n.sons[1]) result[0] = transform(c, n.sons[1])
else: else:
@ -409,7 +414,7 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
if diff < 0: if diff < 0:
result = newTransNode(nkObjUpConv, n, 1) result = newTransNode(nkObjUpConv, n, 1)
result[0] = transform(c, n.sons[1]) result[0] = transform(c, n.sons[1])
elif diff > 0: elif diff > 0 and diff != high(int):
result = newTransNode(nkObjDownConv, n, 1) result = newTransNode(nkObjDownConv, n, 1)
result[0] = transform(c, n.sons[1]) result[0] = transform(c, n.sons[1])
else: else:
@ -491,7 +496,8 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
var newC = newTransCon(getCurrOwner(c)) var newC = newTransCon(getCurrOwner(c))
newC.forStmt = n newC.forStmt = n
newC.forLoopBody = loopBody newC.forLoopBody = loopBody
internalAssert iter.kind == skIterator # this can fail for 'nimsuggest' and 'check':
if iter.kind != skIterator: return result
# generate access statements for the parameters (unless they are constant) # generate access statements for the parameters (unless they are constant)
pushTransCon(c, newC) pushTransCon(c, newC)
for i in countup(1, sonsLen(call) - 1): for i in countup(1, sonsLen(call) - 1):
@ -576,8 +582,7 @@ proc getMergeOp(n: PNode): PSym =
case n.kind case n.kind
of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix, of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
nkCallStrLit: nkCallStrLit:
if (n.sons[0].kind == nkSym) and (n.sons[0].sym.kind == skProc) and if n.sons[0].kind == nkSym and n.sons[0].sym.magic == mConStrStr:
(sfMerge in n.sons[0].sym.flags):
result = n.sons[0].sym result = n.sons[0].sym
else: discard else: discard

View file

@ -142,7 +142,7 @@ proc swapOperands*(op: PNode) =
op.sons[2] = tmp op.sons[2] = tmp
proc isRange*(n: PNode): bool {.inline.} = proc isRange*(n: PNode): bool {.inline.} =
if n.kind == nkInfix: if n.kind in nkCallKinds:
if n[0].kind == nkIdent and n[0].ident.id == ord(wDotDot) or if n[0].kind == nkIdent and n[0].ident.id == ord(wDotDot) or
n[0].kind in {nkClosedSymChoice, nkOpenSymChoice} and n[0].kind in {nkClosedSymChoice, nkOpenSymChoice} and
n[0][1].sym.name.id == ord(wDotDot): n[0][1].sym.name.id == ord(wDotDot):

View file

@ -29,7 +29,8 @@ proc hashTree(n: PNode): THash =
if (n.floatVal >= - 1000000.0) and (n.floatVal <= 1000000.0): if (n.floatVal >= - 1000000.0) and (n.floatVal <= 1000000.0):
result = result !& toInt(n.floatVal) result = result !& toInt(n.floatVal)
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
result = result !& hash(n.strVal) if not n.strVal.isNil:
result = result !& hash(n.strVal)
else: else:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
result = result !& hashTree(n.sons[i]) result = result !& hashTree(n.sons[i])

View file

@ -17,7 +17,7 @@ proc lastOrd*(t: PType): BiggestInt
proc lengthOrd*(t: PType): BiggestInt proc lengthOrd*(t: PType): BiggestInt
type type
TPreferedDesc* = enum TPreferedDesc* = enum
preferName, preferDesc, preferExported, preferModuleInfo preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg
proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string
proc base*(t: PType): PType proc base*(t: PType): PType
@ -85,8 +85,6 @@ proc analyseObjectWithTypeField*(t: PType): TTypeFieldResult
# this does a complex analysis whether a call to ``objectInit`` needs to be # this does a complex analysis whether a call to ``objectInit`` needs to be
# made or intializing of the type field suffices or if there is no type field # made or intializing of the type field suffices or if there is no type field
# at all in this type. # at all in this type.
proc typeAllowed*(t: PType, kind: TSymKind): bool
# implementation
proc invalidGenericInst(f: PType): bool = proc invalidGenericInst(f: PType): bool =
result = f.kind == tyGenericInst and lastSon(f) == nil result = f.kind == tyGenericInst and lastSon(f) == nil
@ -141,7 +139,7 @@ proc elemType*(t: PType): PType =
case t.kind case t.kind
of tyGenericInst, tyDistinct: result = elemType(lastSon(t)) of tyGenericInst, tyDistinct: result = elemType(lastSon(t))
of tyArray, tyArrayConstr: result = t.sons[1] of tyArray, tyArrayConstr: result = t.sons[1]
else: result = t.sons[0] else: result = t.lastSon
assert(result != nil) assert(result != nil)
proc skipGeneric(t: PType): PType = proc skipGeneric(t: PType): PType =
@ -252,7 +250,7 @@ proc containsObject(t: PType): bool =
proc isObjectWithTypeFieldPredicate(t: PType): bool = proc isObjectWithTypeFieldPredicate(t: PType): bool =
result = t.kind == tyObject and t.sons[0] == nil and result = t.kind == tyObject and t.sons[0] == nil and
not (t.sym != nil and sfPure in t.sym.flags) and not (t.sym != nil and {sfPure, sfInfixCall} * t.sym.flags != {}) and
tfFinal notin t.flags tfFinal notin t.flags
proc analyseObjectWithTypeFieldAux(t: PType, proc analyseObjectWithTypeFieldAux(t: PType,
@ -398,7 +396,7 @@ proc rangeToStr(n: PNode): string =
const const
typeToStr: array[TTypeKind, string] = ["None", "bool", "Char", "empty", typeToStr: array[TTypeKind, string] = ["None", "bool", "Char", "empty",
"Array Constructor [$1]", "nil", "expr", "stmt", "typeDesc", "Array Constructor [$1]", "nil", "expr", "stmt", "typeDesc",
"GenericInvokation", "GenericBody", "GenericInst", "GenericParam", "GenericInvocation", "GenericBody", "GenericInst", "GenericParam",
"distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple", "distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple",
"set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc", "set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc",
"pointer", "OpenArray[$1]", "string", "CString", "Forward", "pointer", "OpenArray[$1]", "string", "CString", "Forward",
@ -411,11 +409,13 @@ const
"UserTypeClassInst", "CompositeTypeClass", "UserTypeClassInst", "CompositeTypeClass",
"and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor"] "and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor"]
const preferToResolveSymbols = {preferName, preferModuleInfo, preferGenericArg}
proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
var t = typ var t = typ
result = "" result = ""
if t == nil: return if t == nil: return
if prefer in {preferName, preferModuleInfo} and t.sym != nil and if prefer in preferToResolveSymbols and t.sym != nil and
sfAnon notin t.sym.flags: sfAnon notin t.sym.flags:
if t.kind == tyInt and isIntLit(t): if t.kind == tyInt and isIntLit(t):
return t.sym.name.s & " literal(" & $t.n.intVal & ")" return t.sym.name.s & " literal(" & $t.n.intVal & ")"
@ -428,20 +428,26 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
if not isIntLit(t) or prefer == preferExported: if not isIntLit(t) or prefer == preferExported:
result = typeToStr[t.kind] result = typeToStr[t.kind]
else: else:
result = "int literal(" & $t.n.intVal & ")" if prefer == preferGenericArg:
of tyGenericBody, tyGenericInst, tyGenericInvokation: result = $t.n.intVal
else:
result = "int literal(" & $t.n.intVal & ")"
of tyGenericBody, tyGenericInst, tyGenericInvocation:
result = typeToString(t.sons[0]) & '[' result = typeToString(t.sons[0]) & '['
for i in countup(1, sonsLen(t) -1 -ord(t.kind != tyGenericInvokation)): for i in countup(1, sonsLen(t)-1-ord(t.kind != tyGenericInvocation)):
if i > 1: add(result, ", ") if i > 1: add(result, ", ")
add(result, typeToString(t.sons[i])) add(result, typeToString(t.sons[i], preferGenericArg))
add(result, ']') add(result, ']')
of tyTypeDesc: of tyTypeDesc:
if t.base.kind == tyNone: result = "typedesc" if t.base.kind == tyNone: result = "typedesc"
else: result = "typedesc[" & typeToString(t.base) & "]" else: result = "typedesc[" & typeToString(t.base) & "]"
of tyStatic: of tyStatic:
internalAssert t.len > 0 internalAssert t.len > 0
result = "static[" & typeToString(t.sons[0]) & "]" if prefer == preferGenericArg and t.n != nil:
if t.n != nil: result.add "(" & renderTree(t.n) & ")" result = t.n.renderTree
else:
result = "static[" & typeToString(t.sons[0]) & "]"
if t.n != nil: result.add "(" & renderTree(t.n) & ")"
of tyUserTypeClass: of tyUserTypeClass:
internalAssert t.sym != nil and t.sym.owner != nil internalAssert t.sym != nil and t.sym.owner != nil
return t.sym.owner.name.s return t.sym.owner.name.s
@ -501,18 +507,22 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
if prefer == preferModuleInfo: preferModuleInfo else: preferName) if prefer == preferModuleInfo: preferModuleInfo else: preferName)
of tyTuple: of tyTuple:
# we iterate over t.sons here, because t.n may be nil # we iterate over t.sons here, because t.n may be nil
result = "tuple["
if t.n != nil: if t.n != nil:
result = "tuple["
assert(sonsLen(t.n) == sonsLen(t)) 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) assert(t.n.sons[i].kind == nkSym)
add(result, t.n.sons[i].sym.name.s & ": " & typeToString(t.sons[i])) add(result, t.n.sons[i].sym.name.s & ": " & typeToString(t.sons[i]))
if i < sonsLen(t.n) - 1: add(result, ", ") if i < sonsLen(t.n) - 1: add(result, ", ")
add(result, ']')
elif sonsLen(t) == 0:
result = "tuple[]"
else: else:
result = "("
for i in countup(0, sonsLen(t) - 1): for i in countup(0, sonsLen(t) - 1):
add(result, typeToString(t.sons[i])) add(result, typeToString(t.sons[i]))
if i < sonsLen(t) - 1: add(result, ", ") if i < sonsLen(t) - 1: add(result, ", ")
add(result, ']') add(result, ')')
of tyPtr, tyRef, tyVar, tyMutable, tyConst: of tyPtr, tyRef, tyVar, tyMutable, tyConst:
result = typeToStr[t.kind] result = typeToStr[t.kind]
if t.len >= 2: if t.len >= 2:
@ -531,6 +541,9 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
of tyProc: of tyProc:
result = if tfIterator in t.flags: "iterator (" else: "proc (" result = if tfIterator in t.flags: "iterator (" else: "proc ("
for i in countup(1, sonsLen(t) - 1): for i in countup(1, sonsLen(t) - 1):
if t.n != nil and i < t.n.len and t.n[i].kind == nkSym:
add(result, t.n[i].sym.name.s)
add(result, ": ")
add(result, typeToString(t.sons[i])) add(result, typeToString(t.sons[i]))
if i < sonsLen(t) - 1: add(result, ", ") if i < sonsLen(t) - 1: add(result, ", ")
add(result, ')') add(result, ')')
@ -580,7 +593,7 @@ proc firstOrd(t: PType): BiggestInt =
of tyUInt..tyUInt64: result = 0 of tyUInt..tyUInt64: result = 0
of tyEnum: of tyEnum:
# if basetype <> nil then return firstOrd of basetype # if basetype <> nil then return firstOrd of basetype
if (sonsLen(t) > 0) and (t.sons[0] != nil): if sonsLen(t) > 0 and t.sons[0] != nil:
result = firstOrd(t.sons[0]) result = firstOrd(t.sons[0])
else: else:
assert(t.n.sons[0].kind == nkSym) assert(t.n.sons[0].kind == nkSym)
@ -643,12 +656,12 @@ proc lengthOrd(t: PType): BiggestInt =
type type
TDistinctCompare* = enum ## how distinct types are to be compared TDistinctCompare* = enum ## how distinct types are to be compared
dcEq, ## a and b should be the same type dcEq, ## a and b should be the same type
dcEqIgnoreDistinct, ## compare symetrically: (distinct a) == b, a == b dcEqIgnoreDistinct, ## compare symmetrically: (distinct a) == b, a == b
## or a == (distinct b) ## or a == (distinct b)
dcEqOrDistinctOf ## a equals b or a is distinct of b dcEqOrDistinctOf ## a equals b or a is distinct of b
TTypeCmpFlag* = enum TTypeCmpFlag* = enum
IgnoreTupleFields IgnoreTupleFields ## NOTE: Only set this flag for backends!
IgnoreCC IgnoreCC
ExactTypeDescValues ExactTypeDescValues
ExactGenericParams ExactGenericParams
@ -768,11 +781,20 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool =
var x = a.sons[i] var x = a.sons[i]
var y = b.sons[i] var y = b.sons[i]
if IgnoreTupleFields in c.flags: if IgnoreTupleFields in c.flags:
x = skipTypes(x, {tyRange}) x = skipTypes(x, {tyRange, tyGenericInst})
y = skipTypes(y, {tyRange}) y = skipTypes(y, {tyRange, tyGenericInst})
result = sameTypeAux(x, y, c) result = sameTypeAux(x, y, c)
if not result: return if not result: return
if a.n != nil and b.n != nil and IgnoreTupleFields notin c.flags:
for i in countup(0, sonsLen(a.n) - 1):
# check field names:
if a.n.sons[i].kind == nkSym and b.n.sons[i].kind == nkSym:
var x = a.n.sons[i].sym
var y = b.n.sons[i].sym
result = x.name.id == y.name.id
if not result: break
else: internalError(a.n.info, "sameTuple")
template ifFastObjectTypeCheckFailed(a, b: PType, body: stmt) {.immediate.} = template ifFastObjectTypeCheckFailed(a, b: PType, body: stmt) {.immediate.} =
if tfFromGeneric notin a.flags + b.flags: if tfFromGeneric notin a.flags + b.flags:
@ -781,7 +803,7 @@ template ifFastObjectTypeCheckFailed(a, b: PType, body: stmt) {.immediate.} =
else: else:
# expensive structural equality test; however due to the way generic and # expensive structural equality test; however due to the way generic and
# objects work, if one of the types does **not** contain tfFromGeneric, # objects work, if one of the types does **not** contain tfFromGeneric,
# they cannot be equal. The check ``a.sym.Id == b.sym.Id`` checks # they cannot be equal. The check ``a.sym.id == b.sym.id`` checks
# for the same origin and is essential because we don't want "pure" # for the same origin and is essential because we don't want "pure"
# structural type equivalence: # structural type equivalence:
# #
@ -808,8 +830,13 @@ proc sameEnumTypes*(a, b: PType): bool {.inline.} =
proc sameObjectTree(a, b: PNode, c: var TSameTypeClosure): bool = proc sameObjectTree(a, b: PNode, c: var TSameTypeClosure): bool =
if a == b: if a == b:
result = true result = true
elif (a != nil) and (b != nil) and (a.kind == b.kind): elif a != nil and b != nil and a.kind == b.kind:
if sameTypeOrNilAux(a.typ, b.typ, c): var x = a.typ
var y = b.typ
if IgnoreTupleFields in c.flags:
if x != nil: x = skipTypes(x, {tyRange, tyGenericInst})
if y != nil: y = skipTypes(y, {tyRange, tyGenericInst})
if sameTypeOrNilAux(x, y, c):
case a.kind case a.kind
of nkSym: of nkSym:
# same symbol as string is enough: # same symbol as string is enough:
@ -895,6 +922,9 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
result = sameFlags(a, b) result = sameFlags(a, b)
of tyStatic, tyFromExpr: of tyStatic, tyFromExpr:
result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b) result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b)
if result and a.len == b.len and a.len == 1:
cycleCheck()
result = sameTypeAux(a.sons[0], b.sons[0], c)
of tyObject: of tyObject:
ifFastObjectTypeCheckFailed(a, b): ifFastObjectTypeCheckFailed(a, b):
cycleCheck() cycleCheck()
@ -926,7 +956,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
result = sameChildrenAux(a, b, c) and sameFlags(a, b) result = sameChildrenAux(a, b, c) and sameFlags(a, b)
if result and ExactGenericParams in c.flags: if result and ExactGenericParams in c.flags:
result = a.sym.position == b.sym.position result = a.sym.position == b.sym.position
of tyGenericInvokation, tyGenericBody, tySequence, of tyGenericInvocation, tyGenericBody, tySequence,
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr,
tyArray, tyProc, tyConst, tyMutable, tyVarargs, tyIter, tyArray, tyProc, tyConst, tyMutable, tyVarargs, tyIter,
tyOrdinal, tyTypeClasses, tyFieldAccessor: tyOrdinal, tyTypeClasses, tyFieldAccessor:
@ -1010,22 +1040,23 @@ type
TTypeAllowedFlags = set[TTypeAllowedFlag] TTypeAllowedFlags = set[TTypeAllowedFlag]
proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
flags: TTypeAllowedFlags = {}): bool flags: TTypeAllowedFlags = {}): PType
proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind, proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind,
flags: TTypeAllowedFlags = {}): bool = flags: TTypeAllowedFlags = {}): PType =
result = true
if n != nil: if n != nil:
result = typeAllowedAux(marker, n.typ, kind, flags) result = typeAllowedAux(marker, n.typ, kind, flags)
#if not result: debug(n.typ) #if not result: debug(n.typ)
if result: if result == nil:
case n.kind case n.kind
of nkNone..nkNilLit: of nkNone..nkNilLit:
discard discard
else: else:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
result = typeAllowedNode(marker, n.sons[i], kind, flags) let it = n.sons[i]
if not result: break if it.kind == nkRecCase and kind == skConst: return n.typ
result = typeAllowedNode(marker, it, kind, flags)
if result != nil: break
proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]], proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
last: TTypeKind): bool = last: TTypeKind): bool =
@ -1037,84 +1068,90 @@ proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
result = a.kind == last result = a.kind == last
proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
flags: TTypeAllowedFlags = {}): bool = flags: TTypeAllowedFlags = {}): PType =
assert(kind in {skVar, skLet, skConst, skParam, skResult}) assert(kind in {skVar, skLet, skConst, skParam, skResult})
# if we have already checked the type, return true, because we stop the # if we have already checked the type, return true, because we stop the
# evaluation if something is wrong: # evaluation if something is wrong:
result = true result = nil
if typ == nil: return if typ == nil: return
if containsOrIncl(marker, typ.id): return if containsOrIncl(marker, typ.id): return
var t = skipTypes(typ, abstractInst-{tyTypeDesc}) var t = skipTypes(typ, abstractInst-{tyTypeDesc})
case t.kind case t.kind
of tyVar: of tyVar:
if kind == skConst: return false if kind == skConst: return t
var t2 = skipTypes(t.sons[0], abstractInst-{tyTypeDesc}) var t2 = skipTypes(t.sons[0], abstractInst-{tyTypeDesc})
case t2.kind case t2.kind
of tyVar: of tyVar:
result = taHeap in flags # ``var var`` is illegal on the heap: if taHeap notin flags: result = t2 # ``var var`` is illegal on the heap
of tyOpenArray: of tyOpenArray:
result = kind == skParam and typeAllowedAux(marker, t2, kind, flags) if kind != skParam: result = t
else: result = typeAllowedAux(marker, t2, kind, flags)
else: else:
result = kind in {skParam, skResult} and if kind notin {skParam, skResult}: result = t
typeAllowedAux(marker, t2, kind, flags) else: result = typeAllowedAux(marker, t2, kind, flags)
of tyProc: of tyProc:
for i in countup(1, sonsLen(t) - 1): for i in countup(1, sonsLen(t) - 1):
result = typeAllowedAux(marker, t.sons[i], skParam, flags) result = typeAllowedAux(marker, t.sons[i], skParam, flags)
if not result: break if result != nil: break
if result and t.sons[0] != nil: if result.isNil and t.sons[0] != nil:
result = typeAllowedAux(marker, t.sons[0], skResult, flags) result = typeAllowedAux(marker, t.sons[0], skResult, flags)
of tyExpr, tyStmt, tyTypeDesc, tyStatic: of tyTypeDesc:
result = true # XXX: This is still a horrible idea...
# XXX er ... no? these should not be allowed! result = nil
of tyExpr, tyStmt, tyStatic:
if kind notin {skParam, skResult}: result = t
of tyEmpty: of tyEmpty:
result = taField in flags if taField notin flags: result = t
of tyTypeClasses: of tyTypeClasses:
result = tfGenericTypeParam in t.flags or if not (tfGenericTypeParam in t.flags or taField notin flags): result = t
taField notin flags of tyGenericBody, tyGenericParam, tyGenericInvocation,
of tyGenericBody, tyGenericParam, tyGenericInvokation,
tyNone, tyForward, tyFromExpr, tyFieldAccessor: tyNone, tyForward, tyFromExpr, tyFieldAccessor:
result = false result = t
of tyNil: of tyNil:
result = kind == skConst if kind != skConst: result = t
of tyString, tyBool, tyChar, tyEnum, tyInt..tyBigNum, tyCString, tyPointer: of tyString, tyBool, tyChar, tyEnum, tyInt..tyBigNum, tyCString, tyPointer:
result = true result = nil
of tyOrdinal: of tyOrdinal:
result = kind == skParam if kind != skParam: result = t
of tyGenericInst, tyDistinct: of tyGenericInst, tyDistinct:
result = typeAllowedAux(marker, lastSon(t), kind, flags) result = typeAllowedAux(marker, lastSon(t), kind, flags)
of tyRange: of tyRange:
result = skipTypes(t.sons[0], abstractInst-{tyTypeDesc}).kind in if skipTypes(t.sons[0], abstractInst-{tyTypeDesc}).kind notin
{tyChar, tyEnum, tyInt..tyFloat128} {tyChar, tyEnum, tyInt..tyFloat128, tyUInt8..tyUInt32}: result = t
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
result = (kind == skParam) and typeAllowedAux(marker, t.sons[0], skVar, flags) if kind != skParam: result = t
else: result = typeAllowedAux(marker, t.sons[0], skVar, flags)
of tySequence: of tySequence:
result = t.sons[0].kind == tyEmpty or if t.sons[0].kind != tyEmpty:
typeAllowedAux(marker, t.sons[0], skVar, flags+{taHeap}) result = typeAllowedAux(marker, t.sons[0], skVar, flags+{taHeap})
of tyArray: of tyArray:
result = t.sons[1].kind == tyEmpty or if t.sons[1].kind != tyEmpty:
typeAllowedAux(marker, t.sons[1], skVar, flags) result = typeAllowedAux(marker, t.sons[1], skVar, flags)
of tyRef: of tyRef:
if kind == skConst: return false if kind == skConst: result = t
result = typeAllowedAux(marker, t.lastSon, skVar, flags+{taHeap}) else: result = typeAllowedAux(marker, t.lastSon, skVar, flags+{taHeap})
of tyPtr: of tyPtr:
result = typeAllowedAux(marker, t.lastSon, skVar, flags+{taHeap}) result = typeAllowedAux(marker, t.lastSon, skVar, flags+{taHeap})
of tyArrayConstr, tySet, tyConst, tyMutable, tyIter: of tyArrayConstr, tySet, tyConst, tyMutable, tyIter:
for i in countup(0, sonsLen(t) - 1): for i in countup(0, sonsLen(t) - 1):
result = typeAllowedAux(marker, t.sons[i], kind, flags) result = typeAllowedAux(marker, t.sons[i], kind, flags)
if not result: break if result != nil: break
of tyObject, tyTuple: of tyObject, tyTuple:
if kind == skConst and t.kind == tyObject: return false if kind == skConst and t.kind == tyObject and t.sons[0] != nil: return t
let flags = flags+{taField} let flags = flags+{taField}
for i in countup(0, sonsLen(t) - 1): for i in countup(0, sonsLen(t) - 1):
result = typeAllowedAux(marker, t.sons[i], kind, flags) result = typeAllowedAux(marker, t.sons[i], kind, flags)
if not result: break if result != nil: break
if result and t.n != nil: result = typeAllowedNode(marker, t.n, kind, flags) if result.isNil and t.n != nil:
result = typeAllowedNode(marker, t.n, kind, flags)
of tyProxy: of tyProxy:
# for now same as error node; we say it's a valid type as it should # for now same as error node; we say it's a valid type as it should
# prevent cascading errors: # prevent cascading errors:
result = true result = nil
proc typeAllowed(t: PType, kind: TSymKind): bool = proc typeAllowed*(t: PType, kind: TSymKind): PType =
# returns 'nil' on success and otherwise the part of the type that is
# wrong!
var marker = initIntSet() var marker = initIntSet()
result = typeAllowedAux(marker, t, kind, {}) result = typeAllowedAux(marker, t, kind, {})
@ -1402,3 +1439,45 @@ proc skipConv*(n: PNode): PNode =
proc skipConvTakeType*(n: PNode): PNode = proc skipConvTakeType*(n: PNode): PNode =
result = n.skipConv result = n.skipConv
result.typ = n.typ result.typ = n.typ
proc isEmptyContainer*(t: PType): bool =
case t.kind
of tyExpr, tyNil: result = true
of tyArray, tyArrayConstr: result = t.sons[1].kind == tyEmpty
of tySet, tySequence, tyOpenArray, tyVarargs:
result = t.sons[0].kind == tyEmpty
of tyGenericInst: result = isEmptyContainer(t.lastSon)
else: result = false
proc takeType*(formal, arg: PType): PType =
# param: openArray[string] = []
# [] is an array constructor of length 0 of type string!
if arg.kind == tyNil:
# and not (formal.kind == tyProc and formal.callConv == ccClosure):
result = formal
elif formal.kind in {tyOpenArray, tyVarargs, tySequence} and
arg.isEmptyContainer:
let a = copyType(arg.skipTypes({tyGenericInst}), arg.owner, keepId=false)
a.sons[ord(arg.kind in {tyArray, tyArrayConstr})] = formal.sons[0]
result = a
elif formal.kind in {tyTuple, tySet} and arg.kind == formal.kind:
result = formal
else:
result = arg
proc skipHiddenSubConv*(n: PNode): PNode =
if n.kind == nkHiddenSubConv:
# param: openArray[string] = []
# [] is an array constructor of length 0 of type string!
let formal = n.typ
result = n.sons[1]
let arg = result.typ
let dest = takeType(formal, arg)
if dest == arg and formal.kind != tyExpr:
#echo n.info, " came here for ", formal.typeToString
result = n
else:
result = copyTree(result)
result.typ = dest
else:
result = n

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -68,7 +68,6 @@ proc renderType(n: PNode): string =
assert n[i].kind == nkIdent assert n[i].kind == nkIdent
result.add(',' & typeStr) result.add(',' & typeStr)
of nkTupleTy: of nkTupleTy:
assert len(n) > 0
result = "tuple[" result = "tuple["
for i in 0 .. <len(n): result.add(renderType(n[i]) & ',') for i in 0 .. <len(n): result.add(renderType(n[i]) & ',')
result[<len(result)] = ']' result[<len(result)] = ']'

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -16,7 +16,8 @@ import ast except getstr
import import
strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned, strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned,
parser, vmdeps, idents, trees, renderer, options, transf, parseutils parser, vmdeps, idents, trees, renderer, options, transf, parseutils,
vmmarshal
from semfold import leValueConv, ordinalValToString from semfold import leValueConv, ordinalValToString
from evaltempl import evalTemplate from evaltempl import evalTemplate
@ -123,8 +124,12 @@ proc createStrKeepNode(x: var TFullReg) =
if x.node.isNil: if x.node.isNil:
x.node = newNode(nkStrLit) x.node = newNode(nkStrLit)
elif x.node.kind == nkNilLit: elif x.node.kind == nkNilLit:
when defined(useNodeIds):
let id = x.node.id
system.reset(x.node[]) system.reset(x.node[])
x.node.kind = nkStrLit x.node.kind = nkStrLit
when defined(useNodeIds):
x.node.id = id
elif x.node.kind notin {nkStrLit..nkTripleStrLit} or elif x.node.kind notin {nkStrLit..nkTripleStrLit} or
nfAllConst in x.node.flags: nfAllConst in x.node.flags:
# XXX this is hacky; tests/txmlgen triggers it: # XXX this is hacky; tests/txmlgen triggers it:
@ -154,7 +159,7 @@ proc moveConst(x: var TFullReg, y: TFullReg) =
of rkNodeAddr: x.nodeAddr = y.nodeAddr of rkNodeAddr: x.nodeAddr = y.nodeAddr
# this seems to be the best way to model the reference semantics # this seems to be the best way to model the reference semantics
# of PNimrodNode: # of system.NimNode:
template asgnRef(x, y: expr) = moveConst(x, y) template asgnRef(x, y: expr) = moveConst(x, y)
proc copyValue(src: PNode): PNode = proc copyValue(src: PNode): PNode =
@ -367,11 +372,6 @@ template handleJmpBack() {.dirty.} =
globalError(c.debug[pc], errTooManyIterations) globalError(c.debug[pc], errTooManyIterations)
dec(c.loopIterations) dec(c.loopIterations)
proc skipColon(n: PNode): PNode =
result = n
if n.kind == nkExprColonExpr:
result = n.sons[1]
proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
var pc = start var pc = start
var tos = tos var tos = tos
@ -772,10 +772,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
stackTrace(c, tos, pc, errNilAccess) stackTrace(c, tos, pc, errNilAccess)
of opcEcho: of opcEcho:
let rb = instr.regB let rb = instr.regB
for i in ra..ra+rb-1: if rb == 1:
#if regs[i].kind != rkNode: debug regs[i] msgWriteln(regs[ra].node.strVal)
write(stdout, regs[i].node.strVal) else:
writeln(stdout, "") var outp = ""
for i in ra..ra+rb-1:
#if regs[i].kind != rkNode: debug regs[i]
outp.add(regs[i].node.strVal)
msgWriteln(outp)
of opcContainsSet: of opcContainsSet:
decodeBC(rkInt) decodeBC(rkInt)
regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].regToNode)) regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].regToNode))
@ -806,7 +810,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
leValueConv(regs[ra].regToNode, regs[rc].regToNode)): leValueConv(regs[ra].regToNode, regs[rc].regToNode)):
stackTrace(c, tos, pc, errGenerated, stackTrace(c, tos, pc, errGenerated,
msgKindToString(errIllegalConvFromXtoY) % [ msgKindToString(errIllegalConvFromXtoY) % [
"unknown type" , "unknown type"]) $regs[ra].regToNode, "[" & $regs[rb].regToNode & ".." & $regs[rc].regToNode & "]"])
of opcIndCall, opcIndCallAsgn: of opcIndCall, opcIndCallAsgn:
# dest = call regStart, n; where regStart = fn, arg1, ... # dest = call regStart, n; where regStart = fn, arg1, ...
let rb = instr.regB let rb = instr.regB
@ -984,6 +988,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let rb = instr.regBx - wordExcess let rb = instr.regBx - wordExcess
let cnst = c.constants.sons[rb] let cnst = c.constants.sons[rb]
if fitsRegister(cnst.typ): if fitsRegister(cnst.typ):
myreset(regs[ra])
putIntoReg(regs[ra], cnst) putIntoReg(regs[ra], cnst)
else: else:
ensureKind(rkNode) ensureKind(rkNode)
@ -1011,7 +1016,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcQuit: of opcQuit:
if c.mode in {emRepl, emStaticExpr, emStaticStmt}: if c.mode in {emRepl, emStaticExpr, emStaticStmt}:
message(c.debug[pc], hintQuitCalled) message(c.debug[pc], hintQuitCalled)
quit(int(getOrdValue(regs[ra].regToNode))) msgQuit(int8(getOrdValue(regs[ra].regToNode)))
else: else:
return TFullReg(kind: rkNone) return TFullReg(kind: rkNone)
of opcSetLenStr: of opcSetLenStr:
@ -1034,7 +1039,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
decodeB(rkNode) decodeB(rkNode)
let newLen = regs[rb].intVal.int let newLen = regs[rb].intVal.int
if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess) if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess)
else: setLen(regs[ra].node.sons, newLen) else:
let oldLen = regs[ra].node.len
setLen(regs[ra].node.sons, newLen)
if oldLen < newLen:
# XXX This is still not entirely correct
# set to default value:
for i in oldLen .. <newLen:
regs[ra].node.sons[i] = newNodeI(nkEmpty, c.debug[pc])
of opcSwap: of opcSwap:
let rb = instr.regB let rb = instr.regB
if regs[ra].kind == regs[rb].kind: if regs[ra].kind == regs[rb].kind:
@ -1086,14 +1098,20 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcNAdd: of opcNAdd:
decodeBC(rkNode) decodeBC(rkNode)
var u = regs[rb].node var u = regs[rb].node
u.add(regs[rc].node) if u.kind notin {nkEmpty..nkNilLit}:
u.add(regs[rc].node)
else:
stackTrace(c, tos, pc, errGenerated, "cannot add to node kind: " & $u.kind)
regs[ra].node = u regs[ra].node = u
of opcNAddMultiple: of opcNAddMultiple:
decodeBC(rkNode) decodeBC(rkNode)
let x = regs[rc].node let x = regs[rc].node
var u = regs[rb].node var u = regs[rb].node
# XXX can be optimized: if u.kind notin {nkEmpty..nkNilLit}:
for i in 0.. <x.len: u.add(x.sons[i]) # XXX can be optimized:
for i in 0.. <x.len: u.add(x.sons[i])
else:
stackTrace(c, tos, pc, errGenerated, "cannot add to node kind: " & $u.kind)
regs[ra].node = u regs[ra].node = u
of opcNKind: of opcNKind:
decodeB(rkInt) decodeB(rkInt)
@ -1126,7 +1144,21 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
else: else:
stackTrace(c, tos, pc, errFieldXNotFound, "ident") stackTrace(c, tos, pc, errFieldXNotFound, "ident")
of opcNGetType: of opcNGetType:
internalError(c.debug[pc], "unknown opcode " & $instr.opcode) let rb = instr.regB
let rc = instr.regC
if rc == 0:
ensureKind(rkNode)
if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
regs[ra].node = opMapTypeToAst(regs[rb].node.typ, c.debug[pc])
else:
stackTrace(c, tos, pc, errGenerated, "node has no type")
else:
# typeKind opcode:
ensureKind(rkInt)
if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
regs[ra].intVal = ord(regs[rb].node.typ.kind)
#else:
# stackTrace(c, tos, pc, errGenerated, "node has no type")
of opcNStrVal: of opcNStrVal:
decodeB(rkNode) decodeB(rkNode)
createStr regs[ra] createStr regs[ra]
@ -1333,6 +1365,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
while typ.kind == tyTypeDesc and typ.len > 0: typ = typ.sons[0] while typ.kind == tyTypeDesc and typ.len > 0: typ = typ.sons[0]
createStr regs[ra] createStr regs[ra]
regs[ra].node.strVal = typ.typeToString(preferExported) regs[ra].node.strVal = typ.typeToString(preferExported)
of opcMarshalLoad:
let ra = instr.regA
let rb = instr.regB
inc pc
let typ = c.types[c.code[pc].regBx - wordExcess]
putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ))
of opcMarshalStore:
decodeB(rkNode)
inc pc
let typ = c.types[c.code[pc].regBx - wordExcess]
createStrKeepNode(regs[ra])
if regs[ra].node.strVal.isNil: regs[ra].node.strVal = newStringOfCap(1000)
storeAny(regs[ra].node.strVal, typ, regs[rb].regToNode)
inc pc inc pc
proc execute(c: PCtx, start: int): PNode = proc execute(c: PCtx, start: int): PNode =
@ -1363,9 +1408,11 @@ var
globalCtx: PCtx globalCtx: PCtx
proc setupGlobalCtx(module: PSym) = proc setupGlobalCtx(module: PSym) =
if globalCtx.isNil: globalCtx = newCtx(module) if globalCtx.isNil:
else: refresh(globalCtx, module) globalCtx = newCtx(module)
registerAdditionalOps(globalCtx) registerAdditionalOps(globalCtx)
else:
refresh(globalCtx, module)
proc myOpen(module: PSym): PPassContext = proc myOpen(module: PSym): PPassContext =
#var c = newEvalContext(module, emRepl) #var c = newEvalContext(module, emRepl)
@ -1404,6 +1451,7 @@ proc evalConstExprAux(module, prc: PSym, n: PNode, mode: TEvalMode): PNode =
newSeq(tos.slots, c.prc.maxSlots) newSeq(tos.slots, c.prc.maxSlots)
#for i in 0 .. <c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty) #for i in 0 .. <c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty)
result = rawExecute(c, start, tos).regToNode result = rawExecute(c, start, tos).regToNode
if result.info.line < 0: result.info = n.info
proc evalConstExpr*(module: PSym, e: PNode): PNode = proc evalConstExpr*(module: PSym, e: PNode): PNode =
result = evalConstExprAux(module, nil, e, emConst) result = evalConstExprAux(module, nil, e, emConst)
@ -1435,7 +1483,9 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
# immediate macros can bypass any type and arity checking so we check the # immediate macros can bypass any type and arity checking so we check the
# arity here too: # arity here too:
if sym.typ.len > n.safeLen and sym.typ.len > 1: if sym.typ.len > n.safeLen and sym.typ.len > 1:
globalError(n.info, "got $#, but expected $# argument(s)" % [$ <n.safeLen, $ <sym.typ.len]) globalError(n.info, "in call '$#' got $#, but expected $# argument(s)" % [
n.renderTree,
$ <n.safeLen, $ <sym.typ.len])
setupGlobalCtx(module) setupGlobalCtx(module)
var c = globalCtx var c = globalCtx
@ -1463,6 +1513,7 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
# temporary storage: # temporary storage:
#for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty) #for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty)
result = rawExecute(c, start, tos).regToNode result = rawExecute(c, start, tos).regToNode
if result.info.line < 0: result.info = n.info
if cyclicTree(result): globalError(n.info, errCyclicTree) if cyclicTree(result): globalError(n.info, errCyclicTree)
dec(evalMacroCounter) dec(evalMacroCounter)
c.callsite = nil c.callsite = nil

View file

@ -16,7 +16,7 @@ const
byteExcess* = 128 # we use excess-K for immediates byteExcess* = 128 # we use excess-K for immediates
wordExcess* = 32768 wordExcess* = 32768
MaxLoopIterations* = 500_000 # max iterations of all loops MaxLoopIterations* = 1500_000 # max iterations of all loops
type type
@ -66,7 +66,8 @@ type
opcMulSet, opcPlusSet, opcMinusSet, opcSymdiffSet, opcConcatStr, opcMulSet, opcPlusSet, opcMinusSet, opcSymdiffSet, opcConcatStr,
opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq, opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
opcSwap, opcIsNil, opcOf, opcIs, opcSwap, opcIsNil, opcOf, opcIs,
opcSubStr, opcParseFloat, opcConv, opcCast, opcQuit, opcReset, opcSubStr, opcParseFloat, opcConv, opcCast,
opcQuit, opcReset,
opcNarrowS, opcNarrowU, opcNarrowS, opcNarrowU,
opcAddStrCh, opcAddStrCh,
@ -132,7 +133,8 @@ type
opcLdImmInt, # dest = immediate value opcLdImmInt, # dest = immediate value
opcNBindSym, opcNBindSym,
opcSetType, # dest.typ = types[Bx] opcSetType, # dest.typ = types[Bx]
opcTypeTrait opcTypeTrait,
opcMarshalLoad, opcMarshalStore
TBlock* = object TBlock* = object
label*: PSym label*: PSym
@ -213,6 +215,7 @@ proc newCtx*(module: PSym): PCtx =
proc refresh*(c: PCtx, module: PSym) = proc refresh*(c: PCtx, module: PSym) =
c.module = module c.module = module
c.prc = PProc(blocks: @[]) c.prc = PProc(blocks: @[])
c.loopIterations = MaxLoopIterations
proc registerCallback*(c: PCtx; name: string; callback: VmCallback) = proc registerCallback*(c: PCtx; name: string; callback: VmCallback) =
c.callbacks.add((name, callback)) c.callbacks.add((name, callback))
@ -220,7 +223,8 @@ proc registerCallback*(c: PCtx; name: string; callback: VmCallback) =
const const
firstABxInstr* = opcTJmp firstABxInstr* = opcTJmp
largeInstrs* = { # instructions which use 2 int32s instead of 1: largeInstrs* = { # instructions which use 2 int32s instead of 1:
opcSubStr, opcConv, opcCast, opcNewSeq, opcOf} opcSubStr, opcConv, opcCast, opcNewSeq, opcOf,
opcMarshalLoad, opcMarshalStore}
slotSomeTemp* = slotTempUnknown slotSomeTemp* = slotTempUnknown
relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack} relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack}

View file

@ -1,13 +1,13 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2013 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
import ast, types, msgs, osproc, streams, options import ast, types, msgs, osproc, streams, options, idents
proc readOutput(p: Process): string = proc readOutput(p: Process): string =
result = "" result = ""
@ -19,11 +19,14 @@ proc readOutput(p: Process): string =
discard p.waitForExit discard p.waitForExit
proc opGorge*(cmd, input: string): string = proc opGorge*(cmd, input: string): string =
var p = startCmd(cmd) try:
if input.len != 0: var p = startProcess(cmd, options={poEvalCommand})
p.inputStream.write(input) if input.len != 0:
p.inputStream.close() p.inputStream.write(input)
result = p.readOutput p.inputStream.close()
result = p.readOutput
except IOError, OSError:
result = ""
proc opSlurp*(file: string, info: TLineInfo, module: PSym): string = proc opSlurp*(file: string, info: TLineInfo, module: PSym): string =
try: try:
@ -36,3 +39,124 @@ proc opSlurp*(file: string, info: TLineInfo, module: PSym): string =
except IOError: except IOError:
localError(info, errCannotOpenFile, file) localError(info, errCannotOpenFile, file)
result = "" result = ""
proc atomicTypeX(name: string; t: PType; info: TLineInfo): PNode =
let sym = newSym(skType, getIdent(name), t.owner, info)
sym.typ = t
result = newSymNode(sym)
result.typ = t
proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode
proc mapTypeToBracket(name: string; t: PType; info: TLineInfo): PNode =
result = newNodeIT(nkBracketExpr, info, t)
result.add atomicTypeX(name, t, info)
for i in 0 .. < t.len:
if t.sons[i] == nil:
let void = atomicTypeX("void", t, info)
void.typ = newType(tyEmpty, t.owner)
result.add void
else:
result.add mapTypeToAst(t.sons[i], info)
proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
template atomicType(name): expr = atomicTypeX(name, t, info)
case t.kind
of tyNone: result = atomicType("none")
of tyBool: result = atomicType("bool")
of tyChar: result = atomicType("char")
of tyNil: result = atomicType("nil")
of tyExpr: result = atomicType("expr")
of tyStmt: result = atomicType("stmt")
of tyEmpty: result = atomicType"void"
of tyArrayConstr, tyArray:
result = newNodeIT(nkBracketExpr, info, t)
result.add atomicType("array")
result.add mapTypeToAst(t.sons[0], info)
result.add mapTypeToAst(t.sons[1], info)
of tyTypeDesc:
if t.base != nil:
result = newNodeIT(nkBracketExpr, info, t)
result.add atomicType("typeDesc")
result.add mapTypeToAst(t.base, info)
else:
result = atomicType"typeDesc"
of tyGenericInvocation:
result = newNodeIT(nkBracketExpr, info, t)
for i in 0 .. < t.len:
result.add mapTypeToAst(t.sons[i], info)
of tyGenericInst, tyGenericBody, tyOrdinal, tyUserTypeClassInst:
result = mapTypeToAst(t.lastSon, info)
of tyDistinct:
if allowRecursion:
result = mapTypeToBracket("distinct", t, info)
else:
result = atomicType(t.sym.name.s)
of tyGenericParam, tyForward: result = atomicType(t.sym.name.s)
of tyObject:
if allowRecursion:
result = newNodeIT(nkObjectTy, info, t)
if t.sons[0] == nil:
result.add ast.emptyNode
else:
result.add mapTypeToAst(t.sons[0], info)
result.add copyTree(t.n)
else:
result = atomicType(t.sym.name.s)
of tyEnum:
result = newNodeIT(nkEnumTy, info, t)
result.add copyTree(t.n)
of tyTuple: result = mapTypeToBracket("tuple", t, info)
of tySet: result = mapTypeToBracket("set", t, info)
of tyPtr: result = mapTypeToBracket("ptr", t, info)
of tyRef: result = mapTypeToBracket("ref", t, info)
of tyVar: result = mapTypeToBracket("var", t, info)
of tySequence: result = mapTypeToBracket("seq", t, info)
of tyProc: result = mapTypeToBracket("proc", t, info)
of tyOpenArray: result = mapTypeToBracket("openArray", t, info)
of tyRange:
result = newNodeIT(nkBracketExpr, info, t)
result.add atomicType("range")
result.add t.n.sons[0].copyTree
result.add t.n.sons[1].copyTree
of tyPointer: result = atomicType"pointer"
of tyString: result = atomicType"string"
of tyCString: result = atomicType"cstring"
of tyInt: result = atomicType"int"
of tyInt8: result = atomicType"int8"
of tyInt16: result = atomicType"int16"
of tyInt32: result = atomicType"int32"
of tyInt64: result = atomicType"int64"
of tyFloat: result = atomicType"float"
of tyFloat32: result = atomicType"float32"
of tyFloat64: result = atomicType"float64"
of tyFloat128: result = atomicType"float128"
of tyUInt: result = atomicType"uint"
of tyUInt8: result = atomicType"uint8"
of tyUInt16: result = atomicType"uint16"
of tyUInt32: result = atomicType"uint32"
of tyUInt64: result = atomicType"uint64"
of tyBigNum: result = atomicType"bignum"
of tyConst: result = mapTypeToBracket("const", t, info)
of tyMutable: result = mapTypeToBracket("mutable", t, info)
of tyVarargs: result = mapTypeToBracket("varargs", t, info)
of tyIter: result = mapTypeToBracket("iter", t, info)
of tyProxy: result = atomicType"error"
of tyBuiltInTypeClass: result = mapTypeToBracket("builtinTypeClass", t, info)
of tyUserTypeClass:
result = mapTypeToBracket("concept", t, info)
result.add t.n.copyTree
of tyCompositeTypeClass: result = mapTypeToBracket("compositeTypeClass", t, info)
of tyAnd: result = mapTypeToBracket("and", t, info)
of tyOr: result = mapTypeToBracket("or", t, info)
of tyNot: result = mapTypeToBracket("not", t, info)
of tyAnything: result = atomicType"anything"
of tyStatic, tyFromExpr, tyFieldAccessor:
result = newNodeIT(nkBracketExpr, info, t)
result.add atomicType("static")
if t.n != nil:
result.add t.n.copyTree
proc opMapTypeToAst*(t: PType; info: TLineInfo): PNode =
result = mapTypeToAst(t, info, true)

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -76,6 +76,11 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
elif opc in {opcLdConst, opcAsgnConst}: elif opc in {opcLdConst, opcAsgnConst}:
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA,
c.constants[x.regBx-wordExcess].renderTree) c.constants[x.regBx-wordExcess].renderTree)
elif opc in {opcMarshalLoad, opcMarshalStore}:
let y = c.code[i+1]
result.addf("\t$#\tr$#, r$#, $#", ($opc).substr(3), x.regA, x.regB,
c.types[y.regBx-wordExcess].typeToString)
inc i
else: else:
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess) result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess)
result.add("\t#") result.add("\t#")
@ -368,7 +373,7 @@ proc sameConstant*(a, b: PNode): bool =
case a.kind case a.kind
of nkSym: result = a.sym == b.sym of nkSym: result = a.sym == b.sym
of nkIdent: result = a.ident.id == b.ident.id of nkIdent: result = a.ident.id == b.ident.id
of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
of nkType, nkNilLit: result = a.typ == b.typ of nkType, nkNilLit: result = a.typ == b.typ
@ -604,7 +609,8 @@ proc genNarrowU(c: PCtx; n: PNode; dest: TDest) =
let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
# uint is uint64 in the VM, we we only need to mask the result for # uint is uint64 in the VM, we we only need to mask the result for
# other unsigned types: # other unsigned types:
if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32}: if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32} or
(t.kind == tyInt and t.size == 4):
c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) c.gABC(n, opcNarrowU, dest, TRegister(t.size*8))
proc genBinaryABCnarrow(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = proc genBinaryABCnarrow(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
@ -695,8 +701,7 @@ proc genCard(c: PCtx; n: PNode; dest: var TDest) =
c.gABC(n, opcCard, dest, tmp) c.gABC(n, opcCard, dest, tmp)
c.freeTemp(tmp) c.freeTemp(tmp)
proc genMagic(c: PCtx; n: PNode; dest: var TDest) = proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
let m = n.sons[0].sym.magic
case m case m
of mAnd: c.genAndOr(n, opcFJmp, dest) of mAnd: c.genAndOr(n, opcFJmp, dest)
of mOr: c.genAndOr(n, opcTJmp, dest) of mOr: c.genAndOr(n, opcTJmp, dest)
@ -741,9 +746,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
c.gABC(n, opcNewStr, dest, tmp) c.gABC(n, opcNewStr, dest, tmp)
c.freeTemp(tmp) c.freeTemp(tmp)
# XXX buggy # XXX buggy
of mLengthOpenArray, mLengthArray, mLengthSeq: of mLengthOpenArray, mLengthArray, mLengthSeq, mXLenSeq:
genUnaryABI(c, n, dest, opcLenSeq) genUnaryABI(c, n, dest, opcLenSeq)
of mLengthStr: of mLengthStr, mXLenStr:
genUnaryABI(c, n, dest, opcLenStr) genUnaryABI(c, n, dest, opcLenStr)
of mIncl, mExcl: of mIncl, mExcl:
unused(n, dest) unused(n, dest)
@ -790,7 +795,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
genUnaryABC(c, n, dest, opcUnaryMinusInt) genUnaryABC(c, n, dest, opcUnaryMinusInt)
genNarrow(c, n, dest) genNarrow(c, n, dest)
of mUnaryMinusF64: genUnaryABC(c, n, dest, opcUnaryMinusFloat) of mUnaryMinusF64: genUnaryABC(c, n, dest, opcUnaryMinusFloat)
of mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: gen(c, n.sons[1], dest) of mUnaryPlusI, mUnaryPlusF64: gen(c, n.sons[1], dest)
of mBitnotI, mBitnotI64: of mBitnotI, mBitnotI64:
genUnaryABC(c, n, dest, opcBitnotInt) genUnaryABC(c, n, dest, opcBitnotInt)
genNarrowU(c, n, dest) genNarrowU(c, n, dest)
@ -893,7 +898,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
of mHigh: of mHigh:
if dest < 0: dest = c.getTemp(n.typ) if dest < 0: dest = c.getTemp(n.typ)
let tmp = c.genx(n.sons[1]) let tmp = c.genx(n.sons[1])
if n.sons[1].typ.skipTypes(abstractVar-{tyTypeDesc}).kind == tyString: case n.sons[1].typ.skipTypes(abstractVar-{tyTypeDesc}).kind:
of tyString, tyCString:
c.gABI(n, opcLenStr, dest, tmp, 1) c.gABI(n, opcLenStr, dest, tmp, 1)
else: else:
c.gABI(n, opcLenSeq, dest, tmp, 1) c.gABI(n, opcLenSeq, dest, tmp, 1)
@ -948,7 +954,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
of mNFloatVal: genUnaryABC(c, n, dest, opcNFloatVal) of mNFloatVal: genUnaryABC(c, n, dest, opcNFloatVal)
of mNSymbol: genUnaryABC(c, n, dest, opcNSymbol) of mNSymbol: genUnaryABC(c, n, dest, opcNSymbol)
of mNIdent: genUnaryABC(c, n, dest, opcNIdent) of mNIdent: genUnaryABC(c, n, dest, opcNIdent)
of mNGetType: genUnaryABC(c, n, dest, opcNGetType) of mNGetType:
let tmp = c.genx(n.sons[1])
if dest < 0: dest = c.getTemp(n.typ)
c.gABC(n, opcNGetType, dest, tmp, if n[0].sym.name.s == "typeKind": 1 else: 0)
c.freeTemp(tmp)
#genUnaryABC(c, n, dest, opcNGetType)
of mNStrVal: genUnaryABC(c, n, dest, opcNStrVal) of mNStrVal: genUnaryABC(c, n, dest, opcNStrVal)
of mNSetIntVal: of mNSetIntVal:
unused(n, dest) unused(n, dest)
@ -1001,7 +1012,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
if dest < 0: dest = c.getTemp(n.typ) if dest < 0: dest = c.getTemp(n.typ)
c.gABC(n, opcCallSite, dest) c.gABC(n, opcCallSite, dest)
of mNGenSym: genBinaryABC(c, n, dest, opcGenSym) of mNGenSym: genBinaryABC(c, n, dest, opcGenSym)
of mMinI, mMaxI, mMinI64, mMaxI64, mAbsF64, mMinF64, mMaxF64, mAbsI, mAbsI64: of mMinI, mMaxI, mAbsF64, mMinF64, mMaxF64, mAbsI,
mAbsI64, mDotDot:
c.genCall(n, dest) c.genCall(n, dest)
of mExpandToAst: of mExpandToAst:
if n.len != 2: if n.len != 2:
@ -1020,6 +1032,22 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
# mGCref, mGCunref, # mGCref, mGCunref,
internalError(n.info, "cannot generate code for: " & $m) internalError(n.info, "cannot generate code for: " & $m)
proc genMarshalLoad(c: PCtx, n: PNode, dest: var TDest) =
## Signature: proc to*[T](data: string): T
if dest < 0: dest = c.getTemp(n.typ)
var tmp = c.genx(n.sons[1])
c.gABC(n, opcMarshalLoad, dest, tmp)
c.gABx(n, opcMarshalLoad, 0, c.genType(n.typ))
c.freeTemp(tmp)
proc genMarshalStore(c: PCtx, n: PNode, dest: var TDest) =
## Signature: proc `$$`*[T](x: T): string
if dest < 0: dest = c.getTemp(n.typ)
var tmp = c.genx(n.sons[1])
c.gABC(n, opcMarshalStore, dest, tmp)
c.gABx(n, opcMarshalStore, 0, c.genType(n.sons[1].typ))
c.freeTemp(tmp)
const const
atomicTypes = {tyBool, tyChar, atomicTypes = {tyBool, tyChar,
tyExpr, tyStmt, tyTypeDesc, tyStatic, tyExpr, tyStmt, tyTypeDesc, tyStatic,
@ -1244,8 +1272,8 @@ proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
c.globals.add(getNullValue(s.typ, n.info)) c.globals.add(getNullValue(s.typ, n.info))
s.position = c.globals.len s.position = c.globals.len
# This is rather hard to support, due to the laziness of the VM code # This is rather hard to support, due to the laziness of the VM code
# generator. See tests/compile/tmacro2 for why this is necesary: # generator. See tests/compile/tmacro2 for why this is necessary:
# var decls{.compileTime.}: seq[PNimrodNode] = @[] # var decls{.compileTime.}: seq[NimNode] = @[]
let dest = c.getTemp(s.typ) let dest = c.getTemp(s.typ)
c.gABx(n, opcLdGlobal, dest, s.position) c.gABx(n, opcLdGlobal, dest, s.position)
let tmp = c.genx(s.ast) let tmp = c.genx(s.ast)
@ -1356,7 +1384,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
of tyCString, tyString: of tyCString, tyString:
result = newNodeIT(nkStrLit, info, t) result = newNodeIT(nkStrLit, info, t)
of tyVar, tyPointer, tyPtr, tySequence, tyExpr, of tyVar, tyPointer, tyPtr, tySequence, tyExpr,
tyStmt, tyTypeDesc, tyStatic, tyRef: tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil:
result = newNodeIT(nkNilLit, info, t) result = newNodeIT(nkNilLit, info, t)
of tyProc: of tyProc:
if t.callConv != ccClosure: if t.callConv != ccClosure:
@ -1383,7 +1411,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
addSon(result, getNullValue(t.sons[i], info)) addSon(result, getNullValue(t.sons[i], info))
of tySet: of tySet:
result = newNodeIT(nkCurly, info, t) result = newNodeIT(nkCurly, info, t)
else: internalError("getNullValue: " & $t.kind) else: internalError(info, "getNullValue: " & $t.kind)
proc ldNullOpcode(t: PType): TOpcode = proc ldNullOpcode(t: PType): TOpcode =
if fitsRegister(t): opcLdNullReg else: opcLdNull if fitsRegister(t): opcLdNullReg else: opcLdNull
@ -1525,6 +1553,15 @@ proc matches(s: PSym; x: string): bool =
dec L dec L
result = true result = true
proc matches(s: PSym; y: varargs[string]): bool =
var s = s
var L = y.len-1
while L >= 0:
if s == nil or y[L].cmpIgnoreStyle(s.name.s) != 0: return false
s = if sfFromGeneric in s.flags: s.owner.owner else: s.owner
dec L
result = true
proc procIsCallback(c: PCtx; s: PSym): bool = proc procIsCallback(c: PCtx; s: PSym): bool =
if s.offset < -1: return true if s.offset < -1: return true
var i = -2 var i = -2
@ -1562,8 +1599,17 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
else: else:
internalError(n.info, "cannot generate code for: " & s.name.s) internalError(n.info, "cannot generate code for: " & s.name.s)
of nkCallKinds: of nkCallKinds:
if n.sons[0].kind == nkSym and n.sons[0].sym.magic != mNone: if n.sons[0].kind == nkSym:
genMagic(c, n, dest) let s = n.sons[0].sym
if s.magic != mNone:
genMagic(c, n, dest, s.magic)
elif matches(s, "stdlib", "marshal", "to"):
genMarshalLoad(c, n, dest)
elif matches(s, "stdlib", "marshal", "$$"):
genMarshalStore(c, n, dest)
else:
genCall(c, n, dest)
clearDest(c, n, dest)
else: else:
genCall(c, n, dest) genCall(c, n, dest)
clearDest(c, n, dest) clearDest(c, n, dest)
@ -1602,7 +1648,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
genBreak(c, n) genBreak(c, n)
of nkTryStmt: genTry(c, n, dest) of nkTryStmt: genTry(c, n, dest)
of nkStmtList: of nkStmtList:
unused(n, dest) #unused(n, dest)
# XXX Fix this bug properly, lexim triggers it
for x in n: gen(c, x) for x in n: gen(c, x)
of nkStmtListExpr: of nkStmtListExpr:
let L = n.len-1 let L = n.len-1

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.

283
compiler/vmmarshal.nim Normal file
View file

@ -0,0 +1,283 @@
#
#
# The Nim Compiler
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Implements marshaling for the VM.
import streams, json, intsets, tables, ast, astalgo, idents, types, msgs
proc ptrToInt(x: PNode): int {.inline.} =
result = cast[int](x) # don't skip alignment
proc getField(n: PNode; position: int): PSym =
case n.kind
of nkRecList:
for i in countup(0, sonsLen(n) - 1):
result = getField(n.sons[i], position)
if result != nil: return
of nkRecCase:
result = getField(n.sons[0], position)
if result != nil: return
for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind
of nkOfBranch, nkElse:
result = getField(lastSon(n.sons[i]), position)
if result != nil: return
else: internalError(n.info, "getField(record case branch)")
of nkSym:
if n.sym.position == position: result = n.sym
else: discard
proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet)
proc storeObj(s: var string; typ: PType; x: PNode; stored: var IntSet) =
internalAssert x.kind in {nkObjConstr, nkPar}
let start = ord(x.kind == nkObjConstr)
for i in countup(start, sonsLen(x) - 1):
if i > start: s.add(", ")
var it = x.sons[i]
if it.kind == nkExprColonExpr:
internalAssert it.sons[0].kind == nkSym
let field = it.sons[0].sym
s.add(escapeJson(field.name.s))
s.add(": ")
storeAny(s, field.typ, it.sons[1], stored)
elif typ.n != nil:
let field = getField(typ.n, i)
s.add(escapeJson(field.name.s))
s.add(": ")
storeAny(s, field.typ, it, stored)
proc skipColon*(n: PNode): PNode =
result = n
if n.kind == nkExprColonExpr:
result = n.sons[1]
proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet) =
case t.kind
of tyNone: assert false
of tyBool: s.add($(a.intVal != 0))
of tyChar:
let ch = char(a.intVal)
if ch < '\128':
s.add(escapeJson($ch))
else:
s.add($int(ch))
of tyArray, tySequence:
if t.kind == tySequence and a.kind == nkNilLit: s.add("null")
else:
s.add("[")
for i in 0 .. a.len-1:
if i > 0: s.add(", ")
storeAny(s, t.elemType, a[i], stored)
s.add("]")
of tyTuple:
s.add("{")
for i in 0.. <t.len:
if i > 0: s.add(", ")
s.add("\"Field" & $i)
s.add("\": ")
storeAny(s, t.sons[i], a[i].skipColon, stored)
s.add("}")
of tyObject:
s.add("{")
storeObj(s, t, a, stored)
s.add("}")
of tySet:
s.add("[")
for i in 0.. <a.len:
if i > 0: s.add(", ")
if a[i].kind == nkRange:
var x = copyNode(a[i][0])
storeAny(s, t.lastSon, x, stored)
while x.intVal+1 <= a[i][1].intVal:
s.add(", ")
storeAny(s, t.lastSon, x, stored)
inc x.intVal
else:
storeAny(s, t.lastSon, a[i], stored)
s.add("]")
of tyRange, tyGenericInst: storeAny(s, t.lastSon, a, stored)
of tyEnum:
# we need a slow linear search because of enums with holes:
for e in items(t.n):
if e.sym.position == a.intVal:
s.add e.sym.name.s.escapeJson
break
of tyPtr, tyRef:
var x = a
if isNil(x) or x.kind == nkNilLit: s.add("null")
elif stored.containsOrIncl(x.ptrToInt):
# already stored, so we simply write out the pointer as an int:
s.add($x.ptrToInt)
else:
# else as a [value, key] pair:
# (reversed order for convenient x[0] access!)
s.add("[")
s.add($x.ptrToInt)
s.add(", ")
storeAny(s, t.lastSon, a, stored)
s.add("]")
of tyString, tyCString:
if a.kind == nkNilLit or a.strVal.isNil: s.add("null")
else: s.add(escapeJson(a.strVal))
of tyInt..tyInt64, tyUInt..tyUInt64: s.add($a.intVal)
of tyFloat..tyFloat128: s.add($a.floatVal)
else:
internalError a.info, "cannot marshal at compile-time " & t.typeToString
proc storeAny*(s: var string; t: PType; a: PNode) =
var stored = initIntSet()
storeAny(s, t, a, stored)
proc loadAny(p: var JsonParser, t: PType,
tab: var Table[BiggestInt, PNode]): PNode =
case t.kind
of tyNone: assert false
of tyBool:
case p.kind
of jsonFalse: result = newIntNode(nkIntLit, 0)
of jsonTrue: result = newIntNode(nkIntLit, 1)
else: raiseParseErr(p, "'true' or 'false' expected for a bool")
next(p)
of tyChar:
if p.kind == jsonString:
var x = p.str
if x.len == 1:
result = newIntNode(nkIntLit, ord(x[0]))
next(p)
return
elif p.kind == jsonInt:
result = newIntNode(nkIntLit, getInt(p))
next(p)
return
raiseParseErr(p, "string of length 1 expected for a char")
of tyEnum:
if p.kind == jsonString:
for e in items(t.n):
if e.sym.name.s == p.str:
result = newIntNode(nkIntLit, e.sym.position)
next(p)
return
raiseParseErr(p, "string expected for an enum")
of tyArray:
if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for an array")
next(p)
result = newNode(nkBracket)
while p.kind != jsonArrayEnd and p.kind != jsonEof:
result.add loadAny(p, t.elemType, tab)
if p.kind == jsonArrayEnd: next(p)
else: raiseParseErr(p, "']' end of array expected")
of tySequence:
case p.kind
of jsonNull:
result = newNode(nkNilLit)
next(p)
of jsonArrayStart:
next(p)
result = newNode(nkBracket)
while p.kind != jsonArrayEnd and p.kind != jsonEof:
result.add loadAny(p, t.elemType, tab)
if p.kind == jsonArrayEnd: next(p)
else: raiseParseErr(p, "")
else:
raiseParseErr(p, "'[' expected for a seq")
of tyTuple:
if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
next(p)
result = newNode(nkPar)
var i = 0
while p.kind != jsonObjectEnd and p.kind != jsonEof:
if p.kind != jsonString:
raiseParseErr(p, "string expected for a field name")
next(p)
if i >= t.len:
raiseParseErr(p, "too many fields to tuple type " & typeToString(t))
result.add loadAny(p, t.sons[i], tab)
inc i
if p.kind == jsonObjectEnd: next(p)
else: raiseParseErr(p, "'}' end of object expected")
of tyObject:
if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
next(p)
result = newNode(nkPar)
result.sons = @[]
while p.kind != jsonObjectEnd and p.kind != jsonEof:
if p.kind != jsonString:
raiseParseErr(p, "string expected for a field name")
let field = lookupInRecord(t.n, getIdent(p.str))
if field.isNil:
raiseParseErr(p, "unknown field for object of type " & typeToString(t))
next(p)
if field.position >= result.sons.len:
setLen(result.sons, field.position+1)
result.sons[field.position] = loadAny(p, field.typ, tab)
if p.kind == jsonObjectEnd: next(p)
else: raiseParseErr(p, "'}' end of object expected")
of tySet:
if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for a set")
next(p)
result = newNode(nkCurly)
while p.kind != jsonArrayEnd and p.kind != jsonEof:
result.add loadAny(p, t.lastSon, tab)
next(p)
if p.kind == jsonArrayEnd: next(p)
else: raiseParseErr(p, "']' end of array expected")
of tyPtr, tyRef:
case p.kind
of jsonNull:
result = newNode(nkNilLit)
next(p)
of jsonInt:
result = tab[p.getInt]
if result.isNil:
raiseParseErr(p, "cannot load object with address " & $p.getInt)
next(p)
of jsonArrayStart:
next(p)
if p.kind == jsonInt:
let idx = p.getInt
next(p)
result = loadAny(p, t.lastSon, tab)
tab[idx] = result
else: raiseParseErr(p, "index for ref type expected")
if p.kind == jsonArrayEnd: next(p)
else: raiseParseErr(p, "']' end of ref-address pair expected")
else: raiseParseErr(p, "int for pointer type expected")
of tyString, tyCString:
case p.kind
of jsonNull:
result = newNode(nkNilLit)
next(p)
of jsonString:
result = newStrNode(nkStrLit, p.str)
next(p)
else: raiseParseErr(p, "string expected")
of tyInt..tyInt64, tyUInt..tyUInt64:
if p.kind == jsonInt:
result = newIntNode(nkIntLit, getInt(p))
next(p)
return
raiseParseErr(p, "int expected")
of tyFloat..tyFloat128:
if p.kind == jsonFloat:
result = newFloatNode(nkFloatLit, getFloat(p))
next(p)
return
raiseParseErr(p, "float expected")
of tyRange, tyGenericInst: result = loadAny(p, t.lastSon, tab)
else:
internalError "cannot marshal at compile-time " & t.typeToString
proc loadAny*(s: string; t: PType): PNode =
var tab = initTable[BiggestInt, PNode]()
var p: JsonParser
open(p, newStringStream(s), "unknown file")
next(p)
result = loadAny(p, t, tab)
close(p)

View file

@ -1,7 +1,7 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2014 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.

Some files were not shown because too many files have changed in this diff Show more