diff options
author | Daniil Yarancev <21169548+Yardanico@users.noreply.github.com> | 2018-06-05 21:25:45 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-06-05 21:25:45 +0300 |
commit | 642641359821b6a63c6cf7edaaa45873b7ea59c7 (patch) | |
tree | 627af3020528cb916b3174bd94304307ca875c77 /compiler | |
parent | fb44c522e6173528efa8035ecc459c84887d0167 (diff) | |
parent | 3cbc07ac7877b03c605498760fe198e3200cc197 (diff) | |
download | Nim-642641359821b6a63c6cf7edaaa45873b7ea59c7.tar.gz |
Merge pull request #2 from nim-lang/devel
Update
Diffstat (limited to 'compiler')
117 files changed, 10528 insertions, 9186 deletions
diff --git a/compiler/aliases.nim b/compiler/aliases.nim index cd7e7f19a..f79210dd7 100644 --- a/compiler/aliases.nim +++ b/compiler/aliases.nim @@ -34,10 +34,10 @@ proc isPartOfAux(n: PNode, b: PType, marker: var IntSet): TAnalysisResult = of nkOfBranch, nkElse: result = isPartOfAux(lastSon(n.sons[i]), b, marker) if result == arYes: return - else: internalError("isPartOfAux(record case branch)") + else: discard "isPartOfAux(record case branch)" of nkSym: result = isPartOfAux(n.sym.typ, b, marker) - else: internalError(n.info, "isPartOfAux()") + else: discard proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult = result = arNo @@ -49,7 +49,7 @@ proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult = if a.sons[0] != nil: result = isPartOfAux(a.sons[0].skipTypes(skipPtrs), b, marker) if result == arNo: result = isPartOfAux(a.n, b, marker) - of tyGenericInst, tyDistinct, tyAlias: + of tyGenericInst, tyDistinct, tyAlias, tySink: result = isPartOfAux(lastSon(a), b, marker) of tyArray, tySet, tyTuple: for i in countup(0, sonsLen(a) - 1): diff --git a/compiler/ast.nim b/compiler/ast.nim index 27a44c6c2..5a84b2b02 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -10,7 +10,7 @@ # abstract syntax tree + symbol table import - msgs, hashes, nversion, options, strutils, securehash, ropes, idents, + msgs, hashes, nversion, options, strutils, std / sha1, ropes, idents, intsets, idgen type @@ -221,7 +221,8 @@ type nkGotoState, # used for the state machine (for iterators) nkState, # give a label to a code section (for iterators) nkBreakState, # special break statement for easier code generation - nkFuncDef # a func + nkFuncDef, # a func + nkTupleConstr # a tuple constructor TNodeKinds* = set[TNodeKind] @@ -261,7 +262,8 @@ type # variable is a thread variable sfCompileTime, # proc can be evaluated at compile time sfConstructor, # proc is a C++ constructor - sfDeadCodeElim, # dead code elimination for the module is turned on + sfDispatcher, # copied method symbol is the dispatcher + # deprecated and unused, except for the con sfBorrow, # proc is borrowed sfInfixCall, # symbol needs infix call syntax in target language; # for interfacing with C++, JS @@ -274,10 +276,9 @@ type TSymFlags* = set[TSymFlag] const - sfDispatcher* = sfDeadCodeElim # copied method symbol is the dispatcher sfNoInit* = sfMainModule # don't generate code to init the variable - sfImmediate* = sfDeadCodeElim + sfImmediate* = sfDispatcher # macro or template is immediately expanded # without considering any possible overloads sfAllUntyped* = sfVolatile # macro or template is immediately expanded \ @@ -305,6 +306,7 @@ const sfEscapes* = sfProcvar # param escapes sfBase* = sfDiscriminant sfIsSelf* = sfOverriden # param is 'self' + sfCustomPragma* = sfRegister # symbol is custom pragma template const # getting ready for the future expr/stmt merge @@ -354,7 +356,7 @@ type tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers tyFloat, tyFloat32, tyFloat64, tyFloat128, tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64, - tyOptAsRef, tyUnused1, tyUnused2, + tyOptAsRef, tySink, tyLent, tyVarargs, tyUnused, tyProxy # used as errornous type (for idetools) @@ -631,16 +633,17 @@ type mCpuEndian, mHostOS, mHostCPU, mBuildOS, mBuildCPU, mAppType, mNaN, mInf, mNegInf, mCompileOption, mCompileOptionArg, - mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, mNKind, + mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, + mNKind, mNSymKind mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal, mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo, - mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr, + mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mNBindSym, mLocals, mNCallSite, mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mNHint, mNWarning, mNError, mInstantiationInfo, mGetTypeInfo, mNGenSym, mNimvm, mIntDefine, mStrDefine, mRunnableExamples, - mException + mException, mBuiltinType # things that we can evaluate safely at compile time, even if not asked for it: const @@ -731,7 +734,7 @@ type locOther # location is something other TLocFlag* = enum lfIndirect, # backend introduced a pointer - lfFullExternalName, # only used when 'gCmd == cmdPretty': Indicates + lfFullExternalName, # only used when 'conf.cmd == cmdPretty': Indicates # that the symbol has been imported via 'importc: "fullname"' and # no format string. lfNoDeepCopy, # no need for a deep copy @@ -846,6 +849,8 @@ type constraint*: PNode # additional constraints like 'lit|result'; also # misused for the codegenDecl pragma in the hope # it won't cause problems + # for skModule the string literal to output for + # deprecated modules. when defined(nimsuggest): allUsages*: seq[TLineInfo] @@ -940,13 +945,13 @@ const tyGenericParam} StructuralEquivTypes*: TTypeKinds = {tyNil, tyTuple, tyArray, - tySet, tyRange, tyPtr, tyRef, tyVar, tySequence, tyProc, tyOpenArray, + tySet, tyRange, tyPtr, tyRef, tyVar, tyLent, tySequence, tyProc, tyOpenArray, tyVarargs} ConcreteTypes*: TTypeKinds = { # types of the expr that may occur in:: # var x = expr tyBool, tyChar, tyEnum, tyArray, tyObject, - tySet, tyTuple, tyRange, tyPtr, tyRef, tyVar, tySequence, tyProc, + tySet, tyTuple, tyRange, tyPtr, tyRef, tyVar, tyLent, tySequence, tyProc, tyPointer, tyOpenArray, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128, tyUInt..tyUInt64} @@ -978,7 +983,9 @@ const nkIdentKinds* = {nkIdent, nkSym, nkAccQuoted, nkOpenSymChoice, nkClosedSymChoice} + nkPragmaCallKinds* = {nkExprColonExpr, nkCall, nkCallStrLit} nkLiterals* = {nkCharLit..nkTripleStrLit} + nkFloatLiterals* = {nkFloatLit..nkFloat128Lit} nkLambdaKinds* = {nkLambda, nkDo} declarativeDefs* = {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef} procDefs* = nkLambdaKinds + declarativeDefs @@ -1035,9 +1042,9 @@ proc newNode*(kind: TNodeKind): PNode = new(result) result.kind = kind #result.info = UnknownLineInfo() inlined: - result.info.fileIndex = int32(-1) + result.info.fileIndex = InvalidFileIdx result.info.col = int16(-1) - result.info.line = int16(-1) + result.info.line = uint16(0) when defined(useNodeIds): result.id = gNodeId if result.id == nodeIdToDebug: @@ -1071,14 +1078,14 @@ template previouslyInferred*(t: PType): PType = if t.sons.len > 1: t.lastSon else: nil proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym, - info: TLineInfo): PSym = + info: TLineInfo; options: TOptions = {}): 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.options = options result.owner = owner result.offset = -1 result.id = getID() @@ -1088,7 +1095,7 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym, # writeStacktrace() # MessageOut(name.s & " has id: " & toString(result.id)) -var emptyNode* = newNode(nkEmpty) +var emptyNode* = newNode(nkEmpty) # XXX global variable here! # There is a single empty node that is shared! Do not overwrite it! proc isMetaType*(t: PType): bool = @@ -1109,13 +1116,13 @@ proc linkTo*(s: PSym, t: PType): PSym {.discardable.} = s.typ = t result = s -template fileIdx*(c: PSym): int32 = +template fileIdx*(c: PSym): FileIndex = # XXX: this should be used only on module symbols - c.position.int32 + c.position.FileIndex template filename*(c: PSym): string = # XXX: this should be used only on module symbols - c.position.int32.toFilename + c.position.FileIndex.toFilename proc appendToModule*(m: PSym, n: PNode) = ## The compiler will use this internally to add nodes that will be @@ -1318,7 +1325,7 @@ proc copyType*(t: PType, owner: PSym, keepId: bool): PType = proc exactReplica*(t: PType): PType = copyType(t, t.owner, true) 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, s.options) #result.ast = nil # BUGFIX; was: s.ast which made problems result.typ = s.typ if keepId: @@ -1337,8 +1344,9 @@ proc copySym*(s: PSym, keepId: bool = false): PSym = if result.kind in {skVar, skLet, skField}: result.guard = s.guard -proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo): PSym = - result = newSym(s.kind, newIdent, s.owner, info) +proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo; + options: TOptions): PSym = + result = newSym(s.kind, newIdent, s.owner, info, options) # keep ID! result.ast = s.ast result.id = s.id @@ -1427,7 +1435,7 @@ proc propagateToOwner*(owner, elem: PType) = owner.flags.incl tfHasMeta if tfHasAsgn in elem.flags: - let o2 = owner.skipTypes({tyGenericInst, tyAlias}) + let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink}) if o2.kind in {tyTuple, tyObject, tyArray, tySequence, tyOpt, tySet, tyDistinct}: o2.flags.incl tfHasAsgn @@ -1435,7 +1443,7 @@ proc propagateToOwner*(owner, elem: PType) = if owner.kind notin {tyProc, tyGenericInst, tyGenericBody, tyGenericInvocation, tyPtr}: - let elemB = elem.skipTypes({tyGenericInst, tyAlias}) + let elemB = elem.skipTypes({tyGenericInst, tyAlias, tySink}) 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. @@ -1474,7 +1482,7 @@ proc copyNode*(src: PNode): PNode = echo "COMES FROM ", src.id case src.kind of nkCharLit..nkUInt64Lit: result.intVal = src.intVal - of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal + of nkFloatLiterals: result.floatVal = src.floatVal of nkSym: result.sym = src.sym of nkIdent: result.ident = src.ident of nkStrLit..nkTripleStrLit: result.strVal = src.strVal @@ -1493,7 +1501,7 @@ proc shallowCopy*(src: PNode): PNode = echo "COMES FROM ", src.id case src.kind of nkCharLit..nkUInt64Lit: result.intVal = src.intVal - of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal + of nkFloatLiterals: result.floatVal = src.floatVal of nkSym: result.sym = src.sym of nkIdent: result.ident = src.ident of nkStrLit..nkTripleStrLit: result.strVal = src.strVal @@ -1513,7 +1521,7 @@ proc copyTree*(src: PNode): PNode = echo "COMES FROM ", src.id case src.kind of nkCharLit..nkUInt64Lit: result.intVal = src.intVal - of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal + of nkFloatLiterals: result.floatVal = src.floatVal of nkSym: result.sym = src.sym of nkIdent: result.ident = src.ident of nkStrLit..nkTripleStrLit: result.strVal = src.strVal @@ -1557,15 +1565,17 @@ proc getInt*(a: PNode): BiggestInt = case a.kind of nkCharLit..nkUInt64Lit: result = a.intVal else: - internalError(a.info, "getInt") - result = 0 + #internalError(a.info, "getInt") + doAssert false, "getInt" + #result = 0 proc getFloat*(a: PNode): BiggestFloat = case a.kind - of nkFloatLit..nkFloat128Lit: result = a.floatVal + of nkFloatLiterals: result = a.floatVal else: - internalError(a.info, "getFloat") - result = 0.0 + doAssert false, "getFloat" + #internalError(a.info, "getFloat") + #result = 0.0 proc getStr*(a: PNode): string = case a.kind @@ -1574,16 +1584,18 @@ proc getStr*(a: PNode): string = # let's hope this fixes more problems than it creates: result = nil else: - internalError(a.info, "getStr") - result = "" + doAssert false, "getStr" + #internalError(a.info, "getStr") + #result = "" proc getStrOrChar*(a: PNode): string = case a.kind of nkStrLit..nkTripleStrLit: result = a.strVal of nkCharLit..nkUInt64Lit: result = $chr(int(a.intVal)) else: - internalError(a.info, "getStrOrChar") - result = "" + doAssert false, "getStrOrChar" + #internalError(a.info, "getStrOrChar") + #result = "" proc isGenericRoutine*(s: PSym): bool = case s.kind @@ -1608,6 +1620,19 @@ proc originatingModule*(s: PSym): PSym = proc isRoutine*(s: PSym): bool {.inline.} = result = s.kind in skProcKinds +proc isCompileTimeProc*(s: PSym): bool {.inline.} = + result = s.kind == skMacro or + s.kind == skProc and sfCompileTime in s.flags + +proc requiredParams*(s: PSym): int = + # Returns the number of required params (without default values) + # XXX: Perhaps we can store this in the `offset` field of the + # symbol instead? + for i in 1 ..< s.typ.len: + if s.typ.n[i].sym.ast != nil: + return i - 1 + return s.typ.len - 1 + proc hasPattern*(s: PSym): bool {.inline.} = result = isRoutine(s) and s.ast.sons[patternPos].kind != nkEmpty @@ -1615,7 +1640,7 @@ iterator items*(n: PNode): PNode = for i in 0..<n.safeLen: yield n.sons[i] iterator pairs*(n: PNode): tuple[i: int, n: PNode] = - for i in 0..<n.len: yield (i, n.sons[i]) + for i in 0..<n.safeLen: yield (i, n.sons[i]) proc isAtom*(n: PNode): bool {.inline.} = result = n.kind >= nkNone and n.kind <= nkNilLit @@ -1655,6 +1680,33 @@ proc toObject*(typ: PType): PType = if result.kind == tyRef: result = result.lastSon +proc isException*(t: PType): bool = + # check if `y` is object type and it inherits from Exception + assert(t != nil) + + if t.kind != tyObject: + return false + + var base = t + while base != nil: + if base.sym != nil and base.sym.magic == mException: + return true + base = base.lastSon + return false + +proc isImportedException*(t: PType; conf: ConfigRef): bool = + assert(t != nil) + if optNoCppExceptions in conf.globalOptions: + return false + + let base = t.skipTypes({tyAlias, tyPtr, tyDistinct, tyGenericInst}) + + if base.sym != nil and sfCompileToCpp in base.sym.flags: + result = true + +proc isInfixAs*(n: PNode): bool = + return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.id == getIdent("as").id + proc findUnresolvedStatic*(n: PNode): PNode = if n.kind == nkSym and n.typ.kind == tyStatic and n.typ.n == nil: return n diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 196ac8690..15072e175 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -69,22 +69,22 @@ proc debug*(n: PNode) {.deprecated.} template mdbg*: bool {.dirty.} = when compiles(c.module): - c.module.fileIdx == gProjectMainIdx + c.module.fileIdx.int32 == c.config.projectMainIdx elif compiles(c.c.module): - c.c.module.fileIdx == gProjectMainIdx + c.c.module.fileIdx.int32 == c.c.config.projectMainIdx elif compiles(m.c.module): - m.c.module.fileIdx == gProjectMainIdx + m.c.module.fileIdx.int32 == m.c.config.projectMainIdx elif compiles(cl.c.module): - cl.c.module.fileIdx == gProjectMainIdx + cl.c.module.fileIdx.int32 == cl.c.config.projectMainIdx elif compiles(p): when compiles(p.lex): - p.lex.fileIdx == gProjectMainIdx + p.lex.fileIdx.int32 == p.lex.config.projectMainIdx else: - p.module.module.fileIdx == gProjectMainIdx + p.module.module.fileIdx.int32 == p.config.projectMainIdx elif compiles(m.module.fileIdx): - m.module.fileIdx == gProjectMainIdx + m.module.fileIdx.int32 == m.config.projectMainIdx elif compiles(L.fileIdx): - L.fileIdx == gProjectMainIdx + L.fileIdx.int32 == L.config.projectMainIdx else: error() @@ -180,7 +180,7 @@ proc lookupInRecord(n: PNode, field: PIdent): PSym = result = lookupInRecord(n.sons[i], field) if result != nil: return of nkRecCase: - if (n.sons[0].kind != nkSym): internalError(n.info, "lookupInRecord") + if (n.sons[0].kind != nkSym): return nil result = lookupInRecord(n.sons[0], field) if result != nil: return for i in countup(1, sonsLen(n) - 1): @@ -188,10 +188,10 @@ proc lookupInRecord(n: PNode, field: PIdent): PSym = of nkOfBranch, nkElse: result = lookupInRecord(lastSon(n.sons[i]), field) if result != nil: return - else: internalError(n.info, "lookupInRecord(record case branch)") + else: return nil of nkSym: if n.sym.name.id == field.id: result = n.sym - else: internalError(n.info, "lookupInRecord()") + else: return nil proc getModule(s: PSym): PSym = result = s @@ -203,7 +203,7 @@ proc getSymFromList(list: PNode, ident: PIdent, start: int = 0): PSym = if list.sons[i].kind == nkSym: result = list.sons[i].sym if result.name.id == ident.id: return - else: internalError(list.info, "getSymFromList") + else: return nil result = nil proc hashNode(p: RootRef): Hash = diff --git a/compiler/bitsets.nim b/compiler/bitsets.nim index 5454ef5e7..e38732877 100644 --- a/compiler/bitsets.nim +++ b/compiler/bitsets.nim @@ -28,6 +28,7 @@ proc bitSetExcl*(x: var TBitSet, elem: BiggestInt) proc bitSetIn*(x: TBitSet, e: BiggestInt): bool proc bitSetEquals*(x, y: TBitSet): bool proc bitSetContains*(x, y: TBitSet): bool +proc bitSetCard*(x: TBitSet): BiggestInt # implementation proc bitSetIn(x: TBitSet, e: BiggestInt): bool = @@ -69,3 +70,28 @@ proc bitSetContains(x, y: TBitSet): bool = if (x[i] and not y[i]) != int8(0): return false result = true + +# Number of set bits for all values of int8 +const populationCount: array[low(int8)..high(int8), int8] = block: + var arr: array[low(int8)..high(int8), int8] + + proc countSetBits(x: int8): int8 = + return + ( x and 0b00000001'i8) + + ((x and 0b00000010'i8) shr 1) + + ((x and 0b00000100'i8) shr 2) + + ((x and 0b00001000'i8) shr 3) + + ((x and 0b00010000'i8) shr 4) + + ((x and 0b00100000'i8) shr 5) + + ((x and 0b01000000'i8) shr 6) + + ((x and 0b10000000'i8) shr 7) + + + for it in low(int8)..high(int8): + arr[it] = countSetBits(it) + + arr + +proc bitSetCard(x: TBitSet): BiggestInt = + for it in x: + result.inc populationCount[it] diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index d4fad041d..7d355db5f 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -32,7 +32,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, if d.k == locNone: getTemp(p, typ.sons[0], d, needsInit=true) elif d.k notin {locTemp} and not hasNoInit(ri): # reset before pass as 'result' var: - resetLoc(p, d) + discard "resetLoc(p, d)" add(pl, addrLoc(d)) add(pl, ~");$n") line(p, cpsStmts, pl) @@ -71,7 +71,7 @@ proc isInCurrentFrame(p: BProc, n: PNode): bool = if n.sym.kind in {skVar, skResult, skTemp, skLet} and p.prc != nil: result = p.prc.id == n.sym.owner.id of nkDotExpr, nkBracketExpr: - if skipTypes(n.sons[0].typ, abstractInst).kind notin {tyVar,tyPtr,tyRef}: + if skipTypes(n.sons[0].typ, abstractInst).kind notin {tyVar,tyLent,tyPtr,tyRef}: result = isInCurrentFrame(p, n.sons[0]) of nkHiddenStdConv, nkHiddenSubConv, nkConv: result = isInCurrentFrame(p, n.sons[1]) @@ -83,6 +83,8 @@ proc isInCurrentFrame(p: BProc, n: PNode): bool = result = isInCurrentFrame(p, n.sons[0]) else: discard +proc genIndexCheck(p: BProc; arr, idx: TLoc) + proc openArrayLoc(p: BProc, n: PNode): Rope = var a: TLoc @@ -93,18 +95,28 @@ proc openArrayLoc(p: BProc, n: PNode): Rope = initLocExpr(p, q[1], a) initLocExpr(p, q[2], b) initLocExpr(p, q[3], c) - let fmt = - case skipTypes(a.t, abstractVar+{tyPtr}).kind - of tyOpenArray, tyVarargs, tyArray: - "($1)+($2), ($3)-($2)+1" - of tyString, tySequence: - if skipTypes(n.typ, abstractInst).kind == tyVar and - not compileToCpp(p.module): - "(*$1)->data+($2), ($3)-($2)+1" - else: - "$1->data+($2), ($3)-($2)+1" - else: (internalError("openArrayLoc: " & typeToString(a.t)); "") - result = fmt % [rdLoc(a), rdLoc(b), rdLoc(c)] + # but first produce the required index checks: + if optBoundsCheck in p.options: + genIndexCheck(p, a, b) + genIndexCheck(p, a, c) + let ty = skipTypes(a.t, abstractVar+{tyPtr}) + case ty.kind + of tyArray: + let first = firstOrd(ty) + if first == 0: + result = "($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)] + else: + result = "($1)+(($2)-($4)), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), intLiteral(first)] + of tyOpenArray, tyVarargs: + result = "($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)] + of tyString, tySequence: + if skipTypes(n.typ, abstractInst).kind == tyVar and + not compileToCpp(p.module): + result = "(*$1)->data+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)] + else: + result = "$1->data+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)] + else: + internalError(p.config, "openArrayLoc: " & typeToString(a.t)) else: initLocExpr(p, n, a) case skipTypes(a.t, abstractVar).kind @@ -113,25 +125,25 @@ proc openArrayLoc(p: BProc, n: PNode): Rope = of tyString, tySequence: if skipTypes(n.typ, abstractInst).kind == tyVar and not compileToCpp(p.module): - result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)] + result = "(*$1)->data, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p)] else: - result = "$1->data, $1->$2" % [a.rdLoc, lenField(p)] + result = "$1->data, ($1 ? $1->$2 : 0)" % [a.rdLoc, lenField(p)] of tyArray: result = "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))] of tyPtr, tyRef: case lastSon(a.t).kind of tyString, tySequence: - result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)] + result = "(*$1)->data, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p)] of tyArray: result = "$1, $2" % [rdLoc(a), rope(lengthOrd(lastSon(a.t)))] else: - internalError("openArrayLoc: " & typeToString(a.t)) - else: internalError("openArrayLoc: " & typeToString(a.t)) + internalError(p.config, "openArrayLoc: " & typeToString(a.t)) + else: internalError(p.config, "openArrayLoc: " & typeToString(a.t)) proc genArgStringToCString(p: BProc, n: PNode): Rope {.inline.} = var a: TLoc initLocExpr(p, n.sons[0], a) - result = "$1->data" % [a.rdLoc] + result = "($1 ? $1->data : (NCSTRING)\"\")" % [a.rdLoc] proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope = var a: TLoc @@ -228,7 +240,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = getTemp(p, typ.sons[0], d, needsInit=true) elif d.k notin {locTemp} and not hasNoInit(ri): # reset before pass as 'result' var: - resetLoc(p, d) + discard "resetLoc(p, d)" add(pl, addrLoc(d)) genCallPattern() else: @@ -261,7 +273,7 @@ proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope = result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym) else: if tfVarargs notin typ.flags: - localError(ri.info, "wrong argument count") + localError(p.config, ri.info, "wrong argument count") result = nil else: result = genArgNoParam(p, ri.sons[i]) @@ -325,13 +337,13 @@ 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) + internalAssert p.config, 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, tyAlias}) + let t = typ.sons[i].skipTypes({tyGenericInst, tyAlias, tySink}) if t.kind == tyVar: let x = if ri.kind == nkHiddenAddr: ri[0] else: ri if x.typ.kind == tyPtr: @@ -382,7 +394,7 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope = result.add genOtherArg(p, ri, k, typ) result.add(~")") else: - localError(ri.info, "call expression expected for C++ pattern") + localError(p.config, ri.info, "call expression expected for C++ pattern") inc i elif pat[i+1] == '.': result.add genThisArg(p, ri, j, typ) @@ -420,7 +432,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = assert(sonsLen(typ) == sonsLen(typ.n)) # don't call '$' here for efficiency: let pat = ri.sons[0].sym.loc.r.data - internalAssert pat != nil + internalAssert p.config, pat != nil if pat.contains({'#', '(', '@', '\''}): var pl = genPatternCall(p, ri, pat, typ) # simpler version of 'fixupCall' that works with the pl+params combination: @@ -469,7 +481,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = # don't call '$' here for efficiency: let pat = ri.sons[0].sym.loc.r.data - internalAssert pat != nil + internalAssert p.config, pat != nil var start = 3 if ' ' in pat: start = 1 @@ -489,7 +501,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = for i in countup(start, length-1): assert(sonsLen(typ) == sonsLen(typ.n)) if i >= sonsLen(typ): - internalError(ri.info, "varargs for objective C method?") + internalError(p.config, ri.info, "varargs for objective C method?") assert(typ.n.sons[i].kind == nkSym) var param = typ.n.sons[i].sym add(pl, ~" ") @@ -527,7 +539,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = line(p, cpsStmts, pl) proc genCall(p: BProc, e: PNode, d: var TLoc) = - if e.sons[0].typ.skipTypes({tyGenericInst, tyAlias}).callConv == ccClosure: + if e.sons[0].typ.skipTypes({tyGenericInst, tyAlias, tySink}).callConv == ccClosure: genClosureCall(p, nil, e, d) elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags: genInfixCall(p, nil, e, d) @@ -538,7 +550,7 @@ proc genCall(p: BProc, e: PNode, d: var TLoc) = postStmtActions(p) proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) = - if ri.sons[0].typ.skipTypes({tyGenericInst, tyAlias}).callConv == ccClosure: + if ri.sons[0].typ.skipTypes({tyGenericInst, tyAlias, tySink}).callConv == ccClosure: genClosureCall(p, le, ri, d) elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags: genInfixCall(p, le, ri, d) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 5a25a9853..352402e0e 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -13,7 +13,7 @@ proc int64Literal(i: BiggestInt): Rope = if i > low(int64): - result = rfmt(nil, "IL64($1)", rope(i)) + result = "IL64($1)" % [rope(i)] else: result = ~"(IL64(-9223372036854775807) - IL64(1))" @@ -26,18 +26,12 @@ proc intLiteral(i: BiggestInt): Rope = # Nim has the same bug for the same reasons :-) result = ~"(-2147483647 -1)" elif i > low(int64): - result = rfmt(nil, "IL64($1)", rope(i)) + result = "IL64($1)" % [rope(i)] else: result = ~"(IL64(-9223372036854775807) - IL64(1))" -proc getStrLit(m: BModule, s: string): Rope = - discard cgsym(m, "TGenericSeq") - result = getTempName(m) - addf(m.s[cfsData], "STRING_LITERAL($1, $2, $3);$n", - [result, makeCString(s), rope(len(s))]) - proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = - if ty == nil: internalError(n.info, "genLiteral: ty is nil") + if ty == nil: internalError(p.config, n.info, "genLiteral: ty is nil") case n.kind of nkCharLit..nkUInt64Lit: case skipTypes(ty, abstractVarRange).kind @@ -65,25 +59,24 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = else: result = rope("NIM_NIL") of nkStrLit..nkTripleStrLit: - if n.strVal.isNil: - result = ropecg(p.module, "((#NimStringDesc*) NIM_NIL)", []) - elif skipTypes(ty, abstractVarRange).kind == tyString: - let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) - if id == p.module.labels: - # string literal not found in the cache: - result = ropecg(p.module, "((#NimStringDesc*) &$1)", - [getStrLit(p.module, n.strVal)]) - else: - result = ropecg(p.module, "((#NimStringDesc*) &$1$2)", - [p.module.tmpBase, rope(id)]) + case skipTypes(ty, abstractVarRange).kind + of tyNil: + result = genNilStringLiteral(p.module, n.info) + of tyString: + # with the new semantics for 'nil' strings, we can map "" to nil and + # save tons of allocations: + #if n.strVal.len == 0: result = genNilStringLiteral(p.module, n.info) + #else: + result = genStringLiteral(p.module, n) else: - result = makeCString(n.strVal) + if n.strVal.isNil: result = rope("NIM_NIL") + else: result = makeCString(n.strVal) of nkFloatLit, nkFloat64Lit: result = rope(n.floatVal.toStrMaxPrecision) of nkFloat32Lit: result = rope(n.floatVal.toStrMaxPrecision("f")) else: - internalError(n.info, "genLiteral(" & $n.kind & ')') + internalError(p.config, n.info, "genLiteral(" & $n.kind & ')') result = nil proc genLiteral(p: BProc, n: PNode): Rope = @@ -149,10 +142,10 @@ proc getStorageLoc(n: PNode): TStorageLoc = else: result = OnUnknown of nkDerefExpr, nkHiddenDeref: case n.sons[0].typ.kind - of tyVar: result = OnUnknown + of tyVar, tyLent: result = OnUnknown of tyPtr: result = OnStack of tyRef: result = OnHeap - else: internalError(n.info, "getStorageLoc") + else: doAssert(false, "getStorageLoc") of nkBracketExpr, nkDotExpr, nkObjDownConv, nkObjUpConv: result = getStorageLoc(n.sons[0]) else: result = OnUnknown @@ -171,7 +164,7 @@ proc canMove(n: PNode): bool = # result = false proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = - if dest.storage == OnStack or not usesNativeGC(): + if dest.storage == OnStack or not usesNativeGC(p.config): linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) elif dest.storage == OnHeap: # location is on heap @@ -263,7 +256,7 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = # (for objects, etc.): if needToCopy notin flags or tfShallow in skipTypes(dest.t, abstractVarRange).flags: - if dest.storage == OnStack or not usesNativeGC(): + if dest.storage == OnStack or not usesNativeGC(p.config): useStringh(p.module) linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", @@ -282,7 +275,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = # little HACK to support the new 'var T' as return type: linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) return - let ty = skipTypes(dest.t, abstractRange + tyUserTypeClasses) + let ty = skipTypes(dest.t, abstractRange + tyUserTypeClasses + {tyStatic}) case ty.kind of tyRef: genRefAssign(p, dest, src, flags) @@ -297,7 +290,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode): genRefAssign(p, dest, src, flags) else: - if dest.storage == OnStack or not usesNativeGC(): + if dest.storage == OnStack or not usesNativeGC(p.config): linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc) elif dest.storage == OnHeap: # we use a temporary to care for the dreaded self assignment: @@ -333,7 +326,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = elif needsComplexAssignment(ty): if ty.sons[0].isNil and asgnComplexity(ty.n) <= 4: discard getTypeDesc(p.module, ty) - internalAssert ty.n != nil + internalAssert p.config, ty.n != nil genOptAsgnObject(p, dest, src, flags, ty.n, ty) else: genGenericAsgn(p, dest, src, flags) @@ -368,9 +361,9 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = else: linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCString, - tyInt..tyUInt64, tyRange, tyVar: + tyInt..tyUInt64, tyRange, tyVar, tyLent: linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) - else: internalError("genAssignment: " & $ty.kind) + else: internalError(p.config, "genAssignment: " & $ty.kind) if optMemTracker in p.options and dest.storage in {OnHeap, OnUnknown}: #writeStackTrace() @@ -414,9 +407,9 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) = else: linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) of tyPointer, tyChar, tyBool, tyEnum, tyCString, - tyInt..tyUInt64, tyRange, tyVar: + tyInt..tyUInt64, tyRange, tyVar, tyLent: linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) - else: internalError("genDeepCopy: " & $ty.kind) + else: internalError(p.config, "genDeepCopy: " & $ty.kind) proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) = if d.k != locNone: @@ -457,14 +450,14 @@ proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) = proc binaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) = var a, b: TLoc - if d.k != locNone: internalError(e.info, "binaryStmt") + if d.k != locNone: internalError(p.config, e.info, "binaryStmt") initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) lineCg(p, cpsStmts, frmt, rdLoc(a), rdLoc(b)) proc unaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) = var a: TLoc - if d.k != locNone: internalError(e.info, "unaryStmt") + if d.k != locNone: internalError(p.config, e.info, "unaryStmt") initLocExpr(p, e.sons[1], a) lineCg(p, cpsStmts, frmt, [rdLoc(a)]) @@ -699,7 +692,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = case typ.kind of tyRef: d.storage = OnHeap - of tyVar: + of tyVar, tyLent: d.storage = OnUnknown if tfVarIsPtr notin typ.flags and p.module.compileToCpp and e.kind == nkHiddenDeref: @@ -708,7 +701,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = of tyPtr: d.storage = OnUnknown # BUGFIX! else: - internalError(e.info, "genDeref " & $typ.kind) + internalError(p.config, e.info, "genDeref " & $typ.kind) elif p.module.compileToCpp: if typ.kind == tyVar and tfVarIsPtr notin typ.flags and e.kind == nkHiddenDeref: @@ -743,7 +736,7 @@ template inheritLocation(d: var TLoc, a: TLoc) = proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc) = initLocExpr(p, e.sons[0], a) - if e.sons[1].kind != nkSym: internalError(e.info, "genRecordFieldAux") + if e.sons[1].kind != nkSym: internalError(p.config, e.info, "genRecordFieldAux") d.inheritLocation(a) discard getTypeDesc(p.module, a.t) # fill the record's fields.loc @@ -759,7 +752,7 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) = var r = rdLoc(a) case e.sons[1].kind of nkIntLit..nkUInt64Lit: i = int(e.sons[1].intVal) - else: internalError(e.info, "genTupleElem") + else: internalError(p.config, e.info, "genTupleElem") addf(r, ".Field$1", [rope(i)]) putIntoDest(p, d, e, r, a.storage) @@ -776,7 +769,7 @@ proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope; break if not p.module.compileToCpp: add(r, ".Sup") ty = ty.sons[0] - if result == nil: internalError(field.info, "genCheckedRecordField") + if result == nil: internalError(p.config, field.info, "genCheckedRecordField") proc genRecordField(p: BProc, e: PNode, d: var TLoc) = var a: TLoc @@ -793,7 +786,7 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) = var rtyp: PType let field = lookupFieldAgain(p, ty, f, r, addr rtyp) if field.loc.r == nil and rtyp != nil: fillObjectFields(p.module, rtyp) - if field.loc.r == nil: internalError(e.info, "genRecordField 3 " & typeToString(ty)) + if field.loc.r == nil: internalError(p.config, e.info, "genRecordField 3 " & typeToString(ty)) addf(r, ".$1", [field.loc.r]) putIntoDest(p, d, e, r, a.storage) @@ -818,16 +811,16 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = genInExprAux(p, it, u, v, test) let id = nodeTableTestOrSet(p.module.dataCache, newStrNode(nkStrLit, field.name.s), p.module.labels) - let strLit = if id == p.module.labels: getStrLit(p.module, field.name.s) + let strLit = if id == p.module.labels: genStringLiteralDataOnly(p.module, field.name.s, e.info) else: p.module.tmpBase & rope(id) if op.magic == mNot: linefmt(p, cpsStmts, - "if ($1) #raiseFieldError(((#NimStringDesc*) &$2));$n", - rdLoc(test), strLit) + "if ($1) #raiseFieldError($2);$n", + rdLoc(test), genStringLiteralFromData(p.module, strLit, e.info)) else: linefmt(p, cpsStmts, - "if (!($1)) #raiseFieldError(((#NimStringDesc*) &$2));$n", - rdLoc(test), strLit) + "if (!($1)) #raiseFieldError($2);$n", + rdLoc(test), genStringLiteralFromData(p.module, strLit, e.info)) proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = if optFieldCheck in p.options: @@ -839,9 +832,9 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = let field = lookupFieldAgain(p, ty, f, r) if field.loc.r == nil: fillObjectFields(p.module, ty) if field.loc.r == nil: - internalError(e.info, "genCheckedRecordField") # generate the checks: + internalError(p.config, e.info, "genCheckedRecordField") # generate the checks: genFieldCheck(p, e, r, field) - add(r, rfmt(nil, ".$1", field.loc.r)) + add(r, ropecg(p.module, ".$1", field.loc.r)) putIntoDest(p, d, e.sons[0], r, a.storage) else: genRecordField(p, e.sons[0], d) @@ -866,10 +859,10 @@ proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = else: let idx = getOrdValue(y) if idx < firstOrd(ty) or idx > lastOrd(ty): - localError(x.info, errIndexOutOfBounds) + localError(p.config, x.info, "index out of bounds") d.inheritLocation(a) putIntoDest(p, d, n, - rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.storage) + ropecg(p.module, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.storage) proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) = var a, b: TLoc @@ -878,7 +871,24 @@ proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) = var ty = skipTypes(a.t, abstractVarRange) inheritLocation(d, a) putIntoDest(p, d, n, - rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage) + ropecg(p.module, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage) + +proc genIndexCheck(p: BProc; arr, idx: TLoc) = + let ty = skipTypes(arr.t, abstractVarRange) + case ty.kind + of tyOpenArray, tyVarargs: + linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)) #raiseIndexError();$n", + rdLoc(idx), rdLoc(arr)) + of tyArray: + let first = intLiteral(firstOrd(ty)) + if tfUncheckedArray notin ty.flags: + linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseIndexError();$n", + rdCharLoc(idx), first, intLiteral(lastOrd(ty))) + of tySequence, tyString: + linefmt(p, cpsStmts, + "if (!$2 || (NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n", + rdLoc(idx), rdLoc(arr), lenField(p)) + else: discard proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = var a, b: TLoc @@ -889,7 +899,7 @@ proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = rdLoc(b), rdLoc(a)) # BUGFIX: ``>=`` and not ``>``! inheritLocation(d, a) putIntoDest(p, d, n, - rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage) + ropecg(p.module, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage) proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = var a, b: TLoc @@ -899,19 +909,19 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check: if optBoundsCheck in p.options: - if ty.kind == tyString: + if ty.kind == tyString and (not defined(nimNoZeroTerminator) or optLaxStrings in p.options): linefmt(p, cpsStmts, - "if ((NU)($1) > (NU)($2->$3)) #raiseIndexError();$n", - rdLoc(b), rdLoc(a), lenField(p)) + "if (!$2 || (NU)($1) > (NU)($2->$3)) #raiseIndexError();$n", + rdLoc(b), rdLoc(a), lenField(p)) else: linefmt(p, cpsStmts, - "if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n", - rdLoc(b), rdLoc(a), lenField(p)) + "if (!$2 || (NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n", + rdLoc(b), rdLoc(a), lenField(p)) if d.k == locNone: d.storage = OnHeap if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: - a.r = rfmt(nil, "(*$1)", a.r) + a.r = ropecg(p.module, "(*$1)", a.r) putIntoDest(p, d, n, - rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b)), a.storage) + ropecg(p.module, "$1->data[$2]", rdLoc(a), rdCharLoc(b)), a.storage) proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) = var ty = skipTypes(n.sons[0].typ, abstractVarRange + tyUserTypeClasses) @@ -922,7 +932,7 @@ proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) = of tySequence, tyString: genSeqElem(p, n, n.sons[0], n.sons[1], d) of tyCString: genCStringElem(p, n, n.sons[0], n.sons[1], d) of tyTuple: genTupleElem(p, n, d) - else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')') + else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')') proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = # how to generate code? @@ -967,17 +977,17 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = proc genEcho(p: BProc, n: PNode) = # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)`` # is threadsafe. - internalAssert n.kind == nkBracket + internalAssert p.config, n.kind == nkBracket if platform.targetOS == osGenode: # bypass libc and print directly to the Genode LOG session var args: Rope = nil var a: TLoc - for i in countup(0, n.len-1): - if n.sons[i].skipConv.kind == nkNilLit: - add(args, ", \"nil\"") + for it in n.sons: + if it.skipConv.kind == nkNilLit: + add(args, ", \"\"") else: - initLocExpr(p, n.sons[i], a) - addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)]) + initLocExpr(p, it, a) + addf(args, ", $1? ($1)->data:\"\"", [rdLoc(a)]) p.module.includeHeader("<base/log.h>") linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args) else: @@ -993,8 +1003,8 @@ proc genEcho(p: BProc, n: PNode) = makeCString(repeat("%s", n.len) & tnl), args) linefmt(p, cpsStmts, "fflush(stdout);$n") -proc gcUsage(n: PNode) = - if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree) +proc gcUsage(conf: ConfigRef; n: PNode) = + if conf.selectedGC == gcNone: message(conf, n.info, warnGcMem, n.renderTree) proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = # <Nim code> @@ -1023,20 +1033,20 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, e.sons[i + 1], a) if skipTypes(e.sons[i + 1].typ, abstractVarRange).kind == tyChar: inc(L) - add(appends, rfmt(p.module, "#appendChar($1, $2);$n", tmp.r, rdLoc(a))) + add(appends, ropecg(p.module, "#appendChar($1, $2);$n", tmp.r, rdLoc(a))) else: if e.sons[i + 1].kind in {nkStrLit..nkTripleStrLit}: inc(L, len(e.sons[i + 1].strVal)) else: - addf(lens, "$1->$2 + ", [rdLoc(a), lenField(p)]) - add(appends, rfmt(p.module, "#appendString($1, $2);$n", tmp.r, rdLoc(a))) + addf(lens, "($1 ? $1->$2 : 0) + ", [rdLoc(a), lenField(p)]) + add(appends, ropecg(p.module, "#appendString($1, $2);$n", tmp.r, rdLoc(a))) linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", tmp.r, lens, rope(L)) add(p.s(cpsStmts), appends) if d.k == locNone: d = tmp else: genAssignment(p, d, tmp, {}) # no need for deep copying - gcUsage(e) + gcUsage(p.config, e) proc genStrAppend(p: BProc, e: PNode, d: var TLoc) = # <Nim code> @@ -1061,44 +1071,44 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, e.sons[i + 2], a) if skipTypes(e.sons[i + 2].typ, abstractVarRange).kind == tyChar: inc(L) - add(appends, rfmt(p.module, "#appendChar($1, $2);$n", + add(appends, ropecg(p.module, "#appendChar($1, $2);$n", rdLoc(dest), rdLoc(a))) else: if e.sons[i + 2].kind in {nkStrLit..nkTripleStrLit}: inc(L, len(e.sons[i + 2].strVal)) else: - addf(lens, "$1->$2 + ", [rdLoc(a), lenField(p)]) - add(appends, rfmt(p.module, "#appendString($1, $2);$n", + addf(lens, "($1 ? $1->$2 : 0) + ", [rdLoc(a), lenField(p)]) + add(appends, ropecg(p.module, "#appendString($1, $2);$n", rdLoc(dest), rdLoc(a))) linefmt(p, cpsStmts, "$1 = #resizeString($1, $2$3);$n", rdLoc(dest), lens, rope(L)) add(p.s(cpsStmts), appends) - gcUsage(e) + gcUsage(p.config, e) proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = # seq &= x --> # seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x)); # seq->data[seq->len-1] = x; let seqAppendPattern = if not p.module.compileToCpp: - "$1 = ($2) #incrSeqV2(&($1)->Sup, sizeof($3));$n" + "$1 = ($2) #incrSeqV3(&($1)->Sup, $3);$n" else: - "$1 = ($2) #incrSeqV2($1, sizeof($3));$n" + "$1 = ($2) #incrSeqV3($1, $3);$n" var a, b, dest, tmpL: TLoc initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) - let bt = skipTypes(e.sons[2].typ, {tyVar}) + let seqType = skipTypes(e.sons[1].typ, {tyVar}) lineCg(p, cpsStmts, seqAppendPattern, [ rdLoc(a), getTypeDesc(p.module, e.sons[1].typ), - getTypeDesc(p.module, bt)]) + genTypeInfo(p.module, seqType, e.info)]) #if bt != b.t: # echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t) initLoc(dest, locExpr, e.sons[2], OnHeap) getIntTemp(p, tmpL) lineCg(p, cpsStmts, "$1 = $2->$3++;$n", tmpL.r, rdLoc(a), lenField(p)) - dest.r = rfmt(nil, "$1->data[$2]", rdLoc(a), tmpL.r) + dest.r = ropecg(p.module, "$1->data[$2]", rdLoc(a), tmpL.r) genAssignment(p, dest, b, {needToCopy, afDestIsNil}) - gcUsage(e) + gcUsage(p.config, e) proc genReset(p: BProc, n: PNode) = var a: TLoc @@ -1121,7 +1131,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) = let args = [getTypeDesc(p.module, typ), genTypeInfo(p.module, typ, a.lode.info), sizeExpr] - if a.storage == OnHeap and usesNativeGC(): + if a.storage == OnHeap and usesNativeGC(p.config): # use newObjRC1 as an optimization if canFormAcycle(a.t): linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", a.rdLoc) @@ -1144,7 +1154,7 @@ proc genNew(p: BProc, e: PNode) = rawGenNew(p, a, se.rdLoc) else: rawGenNew(p, a, nil) - gcUsage(e) + gcUsage(p.config, e) proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) = let seqtype = skipTypes(dest.t, abstractVarRange) @@ -1152,7 +1162,7 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) = genTypeInfo(p.module, seqtype, dest.lode.info), length] var call: TLoc initLoc(call, locExpr, dest.lode, OnHeap) - if dest.storage == OnHeap and usesNativeGC(): + if dest.storage == OnHeap and usesNativeGC(p.config): if canFormAcycle(dest.t): linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", dest.rdLoc) else: @@ -1168,7 +1178,7 @@ proc genNewSeq(p: BProc, e: PNode) = initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) genNewSeqAux(p, a, b.rdLoc) - gcUsage(e) + gcUsage(p.config, e) proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = let seqtype = skipTypes(e.typ, abstractVarRange) @@ -1178,7 +1188,7 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = "($1)#nimNewSeqOfCap($2, $3)", [ getTypeDesc(p.module, seqtype), genTypeInfo(p.module, seqtype, e.info), a.rdLoc])) - gcUsage(e) + gcUsage(p.config, e) proc genConstExpr(p: BProc, n: PNode): Rope proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = @@ -1202,18 +1212,30 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = # we skip this step here: if not p.module.compileToCpp: if handleConstExpr(p, e, d): return - var tmp: TLoc var t = e.typ.skipTypes(abstractInst) - getTemp(p, t, tmp) let isRef = t.kind == tyRef - var r = rdLoc(tmp) - if isRef: - rawGenNew(p, tmp, nil) - t = t.lastSon.skipTypes(abstractInst) - r = "(*$1)" % [r] - gcUsage(e) + + # check if we need to construct the object in a temporary + var useTemp = + isRef or + (d.k notin {locTemp,locLocalVar,locGlobalVar,locParam,locField}) or + (isPartOf(d.lode, e) != arNo) + + var tmp: TLoc + var r: Rope + if useTemp: + getTemp(p, t, tmp) + r = rdLoc(tmp) + if isRef: + rawGenNew(p, tmp, nil) + t = t.lastSon.skipTypes(abstractInst) + r = "(*$1)" % [r] + gcUsage(p.config, e) + else: + constructLoc(p, tmp) else: - constructLoc(p, tmp) + resetLoc(p, d) + r = rdLoc(d) discard getTypeDesc(p.module, t) let ty = getUniqueType(t) for i in 1 ..< e.len: @@ -1222,20 +1244,24 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = tmp2.r = r let field = lookupFieldAgain(p, ty, it.sons[0].sym, tmp2.r) if field.loc.r == nil: fillObjectFields(p.module, ty) - if field.loc.r == nil: internalError(e.info, "genObjConstr") + if field.loc.r == nil: internalError(p.config, e.info, "genObjConstr") if it.len == 3 and optFieldCheck in p.options: genFieldCheck(p, it.sons[2], r, field) add(tmp2.r, ".") add(tmp2.r, field.loc.r) - tmp2.k = locTemp + if useTemp: + tmp2.k = locTemp + tmp2.storage = if isRef: OnHeap else: OnStack + else: + tmp2.k = d.k + tmp2.storage = if isRef: OnHeap else: d.storage tmp2.lode = it.sons[1] - tmp2.storage = if isRef: OnHeap else: OnStack expr(p, it.sons[1], tmp2) - - if d.k == locNone: - d = tmp - else: - genAssignment(p, d, tmp, {}) + if useTemp: + if d.k == locNone: + d = tmp + else: + genAssignment(p, d, tmp, {}) proc lhsDoesAlias(a, b: PNode): bool = for y in b: @@ -1254,10 +1280,10 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) = genNewSeqAux(p, dest[], intLiteral(sonsLen(n))) for i in countup(0, sonsLen(n) - 1): initLoc(arr, locExpr, n[i], OnHeap) - arr.r = rfmt(nil, "$1->data[$2]", rdLoc(dest[]), intLiteral(i)) + arr.r = ropecg(p.module, "$1->data[$2]", rdLoc(dest[]), intLiteral(i)) arr.storage = OnHeap # we know that sequences are on the heap expr(p, n[i], arr) - gcUsage(n) + gcUsage(p.config, n) if doesAlias: if d.k == locNone: d = tmp @@ -1280,21 +1306,21 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = if L < 10: for i in countup(0, L - 1): initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap) - elem.r = rfmt(nil, "$1->data[$2]", rdLoc(d), intLiteral(i)) + elem.r = ropecg(p.module, "$1->data[$2]", rdLoc(d), intLiteral(i)) elem.storage = OnHeap # we know that sequences are on the heap initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage) - arr.r = rfmt(nil, "$1[$2]", rdLoc(a), intLiteral(i)) + arr.r = ropecg(p.module, "$1[$2]", rdLoc(a), intLiteral(i)) genAssignment(p, elem, arr, {afDestIsNil, needToCopy}) else: var i: TLoc - getTemp(p, getSysType(tyInt), i) + getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), i) let oldCode = p.s(cpsStmts) linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", i.r, L.rope) initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap) - elem.r = rfmt(nil, "$1->data[$2]", rdLoc(d), rdLoc(i)) + elem.r = ropecg(p.module, "$1->data[$2]", rdLoc(d), rdLoc(i)) elem.storage = OnHeap # we know that sequences are on the heap initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage) - arr.r = rfmt(nil, "$1[$2]", rdLoc(a), rdLoc(i)) + arr.r = ropecg(p.module, "$1[$2]", rdLoc(a), rdLoc(i)) genAssignment(p, elem, arr, {afDestIsNil, needToCopy}) lineF(p, cpsStmts, "}$n", []) @@ -1316,7 +1342,7 @@ proc genNewFinalize(p: BProc, e: PNode) = genAssignment(p, a, b, {}) # set the object type: bt = skipTypes(refType.lastSon, abstractRange) genObjectInit(p, cpsStmts, bt, a, false) - gcUsage(e) + gcUsage(p.config, e) proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo): Rope = # unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we @@ -1330,10 +1356,10 @@ proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo): Rope = inc p.module.labels let cache = "Nim_OfCheck_CACHE" & p.module.labels.rope addf(p.module.s[cfsVars], "static TNimType* $#[2];$n", [cache]) - result = rfmt(p.module, "#isObjWithCache($#.m_type, $#, $#)", a, ti, cache) + result = ropecg(p.module, "#isObjWithCache($#.m_type, $#, $#)", a, ti, cache) when false: # former version: - result = rfmt(p.module, "#isObj($1.m_type, $2)", + result = ropecg(p.module, "#isObj($1.m_type, $2)", a, genTypeInfo(p.module, dest, info)) proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = @@ -1343,22 +1369,23 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = var r = rdLoc(a) var nilCheck: Rope = nil var t = skipTypes(a.t, abstractInst) - while t.kind in {tyVar, tyPtr, tyRef}: - if t.kind != tyVar: nilCheck = r - if t.kind != tyVar or not p.module.compileToCpp: - r = rfmt(nil, "(*$1)", r) + while t.kind in {tyVar, tyLent, tyPtr, tyRef}: + if t.kind notin {tyVar, tyLent}: nilCheck = r + if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp: + r = ropecg(p.module, "(*$1)", r) t = skipTypes(t.lastSon, typedescInst) + discard getTypeDesc(p.module, t) if not p.module.compileToCpp: while t.kind == tyObject and t.sons[0] != nil: add(r, ~".Sup") t = skipTypes(t.sons[0], skipPtrs) if isObjLackingTypeField(t): - globalError(x.info, errGenerated, + globalError(p.config, x.info, "no 'of' operator available for pure objects") if nilCheck != nil: - r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r, x.info)) + r = ropecg(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r, x.info)) else: - r = rfmt(p.module, "($1)", genOfHelper(p, dest, r, x.info)) + r = ropecg(p.module, "($1)", genOfHelper(p, dest, r, x.info)) putIntoDest(p, d, x, r, a.storage) proc genOf(p: BProc, n: PNode, d: var TLoc) = @@ -1394,11 +1421,11 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage) of tyString, tySequence: putIntoDest(p, b, e, - "$1->data, $1->$2" % [rdLoc(a), lenField(p)], a.storage) + "$1->data, ($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)], a.storage) of tyArray: putIntoDest(p, b, e, "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.storage) - else: internalError(e.sons[0].info, "genRepr()") + else: internalError(p.config, e.sons[0].info, "genRepr()") putIntoDest(p, d, e, ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b), genTypeInfo(p.module, elemType(t), e.info)]), a.storage) @@ -1407,12 +1434,12 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = ropecg(p.module, "#reprAny($1, $2)", [ rdLoc(a), genTypeInfo(p.module, t, e.info)]), a.storage) of tyEmpty, tyVoid: - localError(e.info, "'repr' doesn't support 'void' type") + localError(p.config, e.info, "'repr' doesn't support 'void' type") else: putIntoDest(p, d, e, ropecg(p.module, "#reprAny($1, $2)", [addrLoc(a), genTypeInfo(p.module, t, e.info)]), a.storage) - gcUsage(e) + gcUsage(p.config, e) proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) = let t = e.sons[1].typ @@ -1424,7 +1451,7 @@ proc genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) = a.r = ropecg(p.module, frmt, [rdLoc(a)]) if d.k == locNone: getTemp(p, n.typ, d) genAssignment(p, d, a, {}) - gcUsage(n) + gcUsage(p.config, n) proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = var a = e.sons[1] @@ -1466,7 +1493,7 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = # YYY: length(sideeffect) is optimized away incorrectly? if op == mHigh: putIntoDest(p, d, e, rope(lastOrd(typ))) else: putIntoDest(p, d, e, rope(lengthOrd(typ))) - else: internalError(e.info, "genArrayLen()") + else: internalError(p.config, e.info, "genArrayLen()") proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = var a, b: TLoc @@ -1477,18 +1504,18 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, e.sons[2], b) let t = skipTypes(e.sons[1].typ, {tyVar}) let setLenPattern = if not p.module.compileToCpp: - "$1 = ($3) #setLengthSeq(&($1)->Sup, sizeof($4), $2);$n" + "$1 = ($3) #setLengthSeqV2(&($1)->Sup, $4, $2);$n" else: - "$1 = ($3) #setLengthSeq($1, sizeof($4), $2);$n" + "$1 = ($3) #setLengthSeqV2($1, $4, $2);$n" lineCg(p, cpsStmts, setLenPattern, [ rdLoc(a), rdLoc(b), getTypeDesc(p.module, t), - getTypeDesc(p.module, t.skipTypes(abstractInst).sons[0])]) - gcUsage(e) + genTypeInfo(p.module, t.skipTypes(abstractInst), e.info)]) + gcUsage(p.config, e) proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) = binaryStmt(p, e, d, "$1 = #setLengthStr($1, $2);$n") - gcUsage(e) + gcUsage(p.config, e) proc genSwap(p: BProc, e: PNode, d: var TLoc) = # swap(a, b) --> @@ -1514,7 +1541,7 @@ proc rdSetElemLoc(a: TLoc, setType: PType): Rope = proc fewCmps(s: PNode): bool = # this function estimates whether it is better to emit code # for constructing the set or generating a bunch of comparisons directly - if s.kind != nkCurly: internalError(s.info, "fewCmps") + if s.kind != nkCurly: return false if (getSize(s.typ) <= platform.intSize) and (nfAllConst in s.flags): result = false # it is better to emit the set generation code elif elemType(s.typ).kind in {tyInt, tyInt16..tyInt64}: @@ -1556,13 +1583,14 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) = b.r = rope("(") var length = sonsLen(e.sons[1]) for i in countup(0, length - 1): - if e.sons[1].sons[i].kind == nkRange: - initLocExpr(p, e.sons[1].sons[i].sons[0], x) - initLocExpr(p, e.sons[1].sons[i].sons[1], y) + let it = e.sons[1].sons[i] + if it.kind == nkRange: + initLocExpr(p, it.sons[0], x) + initLocExpr(p, it.sons[1], y) addf(b.r, "$1 >= $2 && $1 <= $3", [rdCharLoc(a), rdCharLoc(x), rdCharLoc(y)]) else: - initLocExpr(p, e.sons[1].sons[i], x) + initLocExpr(p, it, x) addf(b.r, "$1 == $2", [rdCharLoc(a), rdCharLoc(x)]) if i < length - 1: add(b.r, " || ") add(b.r, ")") @@ -1609,17 +1637,17 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mSymDiffSet: binaryExpr(p, e, d, "($1 ^ $2)") of mInSet: genInOp(p, e, d) - else: internalError(e.info, "genSetOp()") + else: internalError(p.config, e.info, "genSetOp()") else: case op of mIncl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] |=(1U<<($2&7U));$n") of mExcl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] &= ~(1U<<($2&7U));$n") of mCard: unaryExprChar(p, e, d, "#cardSet($1, " & $size & ')') of mLtSet, mLeSet: - getTemp(p, getSysType(tyInt), i) # our counter + getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), i) # our counter initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) - if d.k == locNone: getTemp(p, getSysType(tyBool), d) + if d.k == locNone: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyBool), d) lineF(p, cpsStmts, lookupOpr[op], [rdLoc(i), rope(size), rdLoc(d), rdLoc(a), rdLoc(b)]) of mEqSet: @@ -1627,7 +1655,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = binaryExprChar(p, e, d, "(memcmp($1, $2, " & $(size) & ")==0)") of mMulSet, mPlusSet, mMinusSet, mSymDiffSet: # we inline the simple for loop for better code generation: - getTemp(p, getSysType(tyInt), i) # our counter + getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), i) # our counter initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) if d.k == locNone: getTemp(p, a.t, d) @@ -1637,7 +1665,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = rdLoc(i), rope(size), rdLoc(d), rdLoc(a), rdLoc(b), rope(lookupOpr[op])]) of mInSet: genInOp(p, e, d) - else: internalError(e.info, "genSetOp") + else: internalError(p.config, e.info, "genSetOp") proc genOrd(p: BProc, e: PNode, d: var TLoc) = unaryExprChar(p, e, d, "$1") @@ -1707,7 +1735,7 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc, magic: string) = rope(magic)]), a.storage) proc genConv(p: BProc, e: PNode, d: var TLoc) = - let destType = e.typ.skipTypes({tyVar, tyGenericInst, tyAlias}) + let destType = e.typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink}) if sameBackendType(destType, e.sons[1].typ): expr(p, e.sons[1], d) else: @@ -1716,7 +1744,7 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) = proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = var a: TLoc initLocExpr(p, n.sons[0], a) - putIntoDest(p, d, n, "$1->data" % [rdLoc(a)], + putIntoDest(p, d, n, "($1 ? $1->data : (NCSTRING)\"\")" % [rdLoc(a)], a.storage) proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = @@ -1725,22 +1753,20 @@ proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = putIntoDest(p, d, n, ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]), a.storage) - gcUsage(n) + gcUsage(p.config, n) proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = var x: TLoc var a = e.sons[1] var b = e.sons[2] - if (a.kind == nkNilLit) or (b.kind == nkNilLit): - binaryExpr(p, e, d, "($1 == $2)") - elif (a.kind in {nkStrLit..nkTripleStrLit}) and (a.strVal == ""): + if a.kind in {nkStrLit..nkTripleStrLit} and a.strVal == "": initLocExpr(p, e.sons[2], x) putIntoDest(p, d, e, - rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p))) - elif (b.kind in {nkStrLit..nkTripleStrLit}) and (b.strVal == ""): + ropecg(p.module, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p))) + elif b.kind in {nkStrLit..nkTripleStrLit} and b.strVal == "": initLocExpr(p, e.sons[1], x) putIntoDest(p, d, e, - rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p))) + ropecg(p.module, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p))) else: binaryExpr(p, e, d, "#eqStrings($1, $2)") @@ -1752,7 +1778,7 @@ proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) = assert(e.sons[2].typ != nil) initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) - putIntoDest(p, d, e, rfmt(nil, "(($4)($2) $1 ($4)($3))", + putIntoDest(p, d, e, ropecg(p.module, "(($4)($2) $1 ($4)($3))", rope(opr[m]), rdLoc(a), rdLoc(b), getSimpleTypeDesc(p.module, e[1].typ))) if optNaNCheck in p.options: @@ -1783,7 +1809,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = "$# = #subInt64($#, $#);$n"] const fun: array[mInc..mDec, string] = ["$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n"] - let underlying = skipTypes(e.sons[1].typ, {tyGenericInst, tyAlias, tyVar, tyRange}) + let underlying = skipTypes(e.sons[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent, tyRange}) if optOverflowCheck notin p.options or underlying.kind in {tyUInt..tyUInt64}: binaryStmt(p, e, d, opr[op]) else: @@ -1793,7 +1819,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) - let ranged = skipTypes(e.sons[1].typ, {tyGenericInst, tyAlias, tyVar}) + let ranged = skipTypes(e.sons[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent}) let res = binaryArithOverflowRaw(p, ranged, a, b, if underlying.kind == tyInt64: fun64[op] else: fun[op]) putIntoDest(p, a, e.sons[1], "($#)($#)" % [ @@ -1861,12 +1887,12 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mEcho: genEcho(p, e[1].skipConv) of mArrToSeq: genArrToSeq(p, e, d) of mNLen..mNError, mSlurp..mQuoteAst: - localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s) + localError(p.config, e.info, strutils.`%`(errXMustBeCompileTime, e.sons[0].sym.name.s)) of mSpawn: - let n = lowerings.wrapProcForSpawn(p.module.module, e, e.typ, nil, nil) + let n = lowerings.wrapProcForSpawn(p.module.g.graph, p.module.module, e, e.typ, nil, nil) expr(p, n, d) of mParallel: - let n = semparallel.liftParallel(p.module.module, e) + let n = semparallel.liftParallel(p.module.g.graph, p.module.module, e) expr(p, n, d) of mDeepCopy: var a, b: TLoc @@ -1878,7 +1904,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: when defined(debugMagics): echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind - internalError(e.info, "genMagicExpr: " & $op) + internalError(p.config, e.info, "genMagicExpr: " & $op) proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = # example: { a..b, c, d, e, f..g } @@ -1894,34 +1920,35 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = if getSize(e.typ) > 8: # big set: useStringh(p.module) - lineF(p, cpsStmts, "memset($1, 0, sizeof($1));$n", [rdLoc(d)]) - for i in countup(0, sonsLen(e) - 1): - if e.sons[i].kind == nkRange: - getTemp(p, getSysType(tyInt), idx) # our counter - initLocExpr(p, e.sons[i].sons[0], a) - initLocExpr(p, e.sons[i].sons[1], b) + lineF(p, cpsStmts, "memset($1, 0, sizeof($2));$n", + [rdLoc(d), getTypeDesc(p.module, e.typ)]) + for it in e.sons: + if it.kind == nkRange: + getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), idx) # our counter + initLocExpr(p, it.sons[0], a) + initLocExpr(p, it.sons[1], b) lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" & "$2[(NU)($1)>>3] |=(1U<<((NU)($1)&7U));$n", [rdLoc(idx), rdLoc(d), rdSetElemLoc(a, e.typ), rdSetElemLoc(b, e.typ)]) else: - initLocExpr(p, e.sons[i], a) + initLocExpr(p, it, a) lineF(p, cpsStmts, "$1[(NU)($2)>>3] |=(1U<<((NU)($2)&7U));$n", [rdLoc(d), rdSetElemLoc(a, e.typ)]) else: # small set var ts = "NU" & $(getSize(e.typ) * 8) lineF(p, cpsStmts, "$1 = 0;$n", [rdLoc(d)]) - for i in countup(0, sonsLen(e) - 1): - if e.sons[i].kind == nkRange: - getTemp(p, getSysType(tyInt), idx) # our counter - initLocExpr(p, e.sons[i].sons[0], a) - initLocExpr(p, e.sons[i].sons[1], b) + for it in e.sons: + if it.kind == nkRange: + getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), idx) # our counter + initLocExpr(p, it.sons[0], a) + initLocExpr(p, it.sons[1], b) lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" & "$2 |=((" & ts & ")(1)<<(($1)%(sizeof(" & ts & ")*8)));$n", [ rdLoc(idx), rdLoc(d), rdSetElemLoc(a, e.typ), rdSetElemLoc(b, e.typ)]) else: - initLocExpr(p, e.sons[i], a) + initLocExpr(p, it, a) lineF(p, cpsStmts, "$1 |=((" & ts & ")(1)<<(($2)%(sizeof(" & ts & ")*8)));$n", [rdLoc(d), rdSetElemLoc(a, e.typ)]) @@ -1957,7 +1984,7 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) = initLocExpr(p, n.sons[0], a) initLocExpr(p, n.sons[1], b) if n.sons[0].skipConv.kind == nkClosure: - internalError(n.info, "closure to closure created") + internalError(p.config, n.info, "closure to closure created") # tasyncawait.nim breaks with this optimization: when false: if d.k != locNone: @@ -1998,7 +2025,7 @@ template genStmtListExprImpl(exprOrStmt) {.dirty.} = let theMacro = it[0].sym add p.s(cpsStmts), initFrameNoDebug(p, frameName, makeCString theMacro.name.s, - theMacro.info.quotedFilename, it.info.line) + quotedFilename(p.config, theMacro.info), it.info.line.int) else: genStmts(p, it) if n.len > 0: exprOrStmt @@ -2021,11 +2048,12 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = var r = rdLoc(a) var nilCheck: Rope = nil var t = skipTypes(a.t, abstractInst) - while t.kind in {tyVar, tyPtr, tyRef}: - if t.kind != tyVar: nilCheck = r - if t.kind != tyVar or not p.module.compileToCpp: + while t.kind in {tyVar, tyLent, tyPtr, tyRef}: + if t.kind notin {tyVar, tyLent}: nilCheck = r + if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp: r = "(*$1)" % [r] t = skipTypes(t.lastSon, abstractInst) + discard getTypeDesc(p.module, t) if not p.module.compileToCpp: while t.kind == tyObject and t.sons[0] != nil: add(r, ".Sup") @@ -2045,6 +2073,7 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = proc downConv(p: BProc, n: PNode, d: var TLoc) = if p.module.compileToCpp: + discard getTypeDesc(p.module, skipTypes(n[0].typ, abstractPtrs)) expr(p, n.sons[0], d) # downcast does C++ for us else: var dest = skipTypes(n.typ, abstractPtrs) @@ -2053,10 +2082,11 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) = while arg.kind == nkObjDownConv: arg = arg.sons[0] var src = skipTypes(arg.typ, abstractPtrs) + discard getTypeDesc(p.module, src) var a: TLoc initLocExpr(p, arg, a) var r = rdLoc(a) - let isRef = skipTypes(arg.typ, abstractInst).kind in {tyRef, tyPtr, tyVar} + let isRef = skipTypes(arg.typ, abstractInst).kind in {tyRef, tyPtr, tyVar, tyLent} if isRef: add(r, "->Sup") else: @@ -2068,7 +2098,7 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) = # (see bug #837). However sometimes using a temporary is not correct: # init(TFigure(my)) # where it is passed to a 'var TFigure'. We test # this by ensuring the destination is also a pointer: - if d.k == locNone and skipTypes(n.typ, abstractInst).kind in {tyRef, tyPtr, tyVar}: + if d.k == locNone and skipTypes(n.typ, abstractInst).kind in {tyRef, tyPtr, tyVar, tyLent}: getTemp(p, n.typ, d) linefmt(p, cpsStmts, "$1 = &$2;$n", rdLoc(d), r) else: @@ -2116,11 +2146,11 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = #if sym.kind == skIterator: # echo renderTree(sym.getBody, {renderIds}) if sfCompileTime in sym.flags: - localError(n.info, "request to generate code for .compileTime proc: " & + localError(p.config, n.info, "request to generate code for .compileTime proc: " & sym.name.s) genProc(p.module, sym) if sym.loc.r == nil or sym.loc.lode == nil: - internalError(n.info, "expr: proc not init " & sym.name.s) + internalError(p.config, n.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) of skConst: if isSimpleConst(sym.typ): @@ -2128,6 +2158,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = else: genComplexConst(p, sym, d) of skEnumField: + # we never reach this case - as of the time of this comment, + # skEnumField is folded to an int in semfold.nim, but this code + # remains for robustness putIntoDest(p, d, n, rope(sym.position)) of skVar, skForVar, skResult, skLet: if {sfGlobal, sfThread} * sym.flags != {}: @@ -2135,10 +2168,10 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = if sym.loc.r == nil or sym.loc.t == nil: #echo "FAILED FOR PRCO ", p.prc.name.s #echo renderTree(p.prc.ast, {renderIds}) - internalError n.info, "expr: var not init " & sym.name.s & "_" & $sym.id + internalError p.config, n.info, "expr: var not init " & sym.name.s & "_" & $sym.id if sfThread in sym.flags: accessThreadLocalVar(p, sym) - if emulatedThreadVars(): + if emulatedThreadVars(p.config): putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.r) else: putLocIntoDest(p, d, sym.loc) @@ -2148,16 +2181,16 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = if sym.loc.r == nil or sym.loc.t == nil: #echo "FAILED FOR PRCO ", p.prc.name.s #echo renderTree(p.prc.ast, {renderIds}) - internalError(n.info, "expr: temp not init " & sym.name.s & "_" & $sym.id) + internalError(p.config, n.info, "expr: temp not init " & sym.name.s & "_" & $sym.id) putLocIntoDest(p, d, sym.loc) of skParam: if sym.loc.r == nil or sym.loc.t == nil: # echo "FAILED FOR PRCO ", p.prc.name.s # debug p.prc.typ.n # echo renderTree(p.prc.ast, {renderIds}) - internalError(n.info, "expr: param not init " & sym.name.s & "_" & $sym.id) + internalError(p.config, n.info, "expr: param not init " & sym.name.s & "_" & $sym.id) putLocIntoDest(p, d, sym.loc) - else: internalError(n.info, "expr(" & $sym.kind & "); unknown symbol") + else: internalError(p.config, n.info, "expr(" & $sym.kind & "); unknown symbol") of nkNilLit: if not isEmptyType(n.typ): putIntoDest(p, d, n, genLiteral(p, n)) @@ -2195,7 +2228,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = genSeqConstr(p, n, d) else: genArrayConstr(p, n, d) - of nkPar: + of nkPar, nkTupleConstr: if isDeepConstExpr(n) and n.len != 0: exprComplexConst(p, n, d) else: @@ -2226,15 +2259,15 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = var sym = n.sons[namePos].sym genProc(p.module, sym) if sym.loc.r == nil or sym.loc.lode == nil: - internalError(n.info, "expr: proc not init " & sym.name.s) + internalError(p.config, n.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) of nkClosure: genClosure(p, n, d) of nkEmpty: discard of nkWhileStmt: genWhileStmt(p, n) of nkVarSection, nkLetSection: genVarStmt(p, n) - of nkConstSection: genConstStmt(p, n) - of nkForStmt: internalError(n.info, "for statement not eliminated") + of nkConstSection: discard # consts generated lazily on use + of nkForStmt: internalError(p.config, n.info, "for statement not eliminated") of nkCaseStmt: genCase(p, n, d) of nkReturnStmt: genReturnStmt(p, n) of nkBreakStmt: genBreakStmt(p, n) @@ -2262,7 +2295,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = initLocExpr(p, ex, a) of nkAsmStmt: genAsmStmt(p, n) of nkTryStmt: - if p.module.compileToCpp and optNoCppExceptions notin gGlobalOptions: + if p.module.compileToCpp and optNoCppExceptions notin p.config.globalOptions: genTryCpp(p, n, d) else: genTry(p, n, d) @@ -2284,8 +2317,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = # are not transformed correctly. We work around this issue (#411) here # by ensuring it's no inner proc (owner is a module): if prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags: - if (not emitLazily(prc)) or - ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or + if ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or (prc.kind == skMethod): # we have not only the header: @@ -2294,8 +2326,8 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkParForStmt: genParForStmt(p, n) of nkState: genState(p, n) of nkGotoState: genGotoState(p, n) - of nkBreakState: genBreakState(p, n) - else: internalError(n.info, "expr(" & $n.kind & "); unknown node kind") + of nkBreakState: genBreakState(p, n, d) + else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind") proc genNamedConstExpr(p: BProc, n: PNode): Rope = if n.kind == nkExprColonExpr: result = genConstExpr(p, n.sons[1]) @@ -2307,7 +2339,7 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope = of tyBool: result = rope"NIM_FALSE" of tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64: result = rope"0" of tyFloat..tyFloat128: result = rope"0.0" - of tyCString, tyString, tyVar, tyPointer, tyPtr, tySequence, tyExpr, + of tyCString, tyString, tyVar, tyLent, tyPointer, tyPtr, tySequence, tyExpr, tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil: result = rope"NIM_NIL" of tyProc: @@ -2331,13 +2363,13 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope = if mapType(t) == ctArray: result = rope"{}" else: result = rope"0" else: - globalError(info, "cannot create null element for: " & $t.kind) + globalError(p.config, info, "cannot create null element for: " & $t.kind) proc getNullValueAux(p: BProc; t: PType; obj, cons: PNode, result: var Rope; count: var int) = case obj.kind of nkRecList: - for i in countup(0, sonsLen(obj) - 1): - getNullValueAux(p, t, obj.sons[i], cons, result, count) + for it in obj.sons: + getNullValueAux(p, t, it, cons, result, count) of nkRecCase: getNullValueAux(p, t, obj.sons[0], cons, result, count) for i in countup(1, sonsLen(obj) - 1): @@ -2357,7 +2389,7 @@ proc getNullValueAux(p: BProc; t: PType; obj, cons: PNode, result: var Rope; cou # not found, produce default value: result.add getDefaultValue(p, field.typ, cons.info) else: - localError(cons.info, "cannot create null element for: " & $obj) + localError(p.config, cons.info, "cannot create null element for: " & $obj) proc getNullValueAuxT(p: BProc; orig, t: PType; obj, cons: PNode, result: var Rope; count: var int) = var base = t.sons[0] @@ -2427,7 +2459,7 @@ proc genConstExpr(p: BProc, n: PNode): Rope = var cs: TBitSet toBitSet(n, cs) result = genRawSetData(cs, int(getSize(n.typ))) - of nkBracket, nkPar, nkClosure: + of nkBracket, nkPar, nkTupleConstr, nkClosure: var t = skipTypes(n.typ, abstractInst) if t.kind == tySequence: result = genConstSeq(p, n, n.typ) diff --git a/compiler/ccgliterals.nim b/compiler/ccgliterals.nim new file mode 100644 index 000000000..cfe71375e --- /dev/null +++ b/compiler/ccgliterals.nim @@ -0,0 +1,91 @@ +# +# +# The Nim Compiler +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This include file contains the logic to produce constant string +## and seq literals. The code here is responsible that +## ``const x = ["a", "b"]`` works without hidden runtime creation code. +## The price is that seqs and strings are not purely a library +## implementation. + +template detectVersion(field, corename) = + if m.g.field == 0: + let core = getCompilerProc(m.g.graph, corename) + if core == nil or core.kind != skConst: + m.g.field = 1 + else: + m.g.field = int ast.getInt(core.ast) + result = m.g.field + +proc detectStrVersion(m: BModule): int = + detectVersion(strVersion, "nimStrVersion") + +proc detectSeqVersion(m: BModule): int = + detectVersion(seqVersion, "nimSeqVersion") + +# ----- Version 1: GC'ed strings and seqs -------------------------------- + +proc genStringLiteralDataOnlyV1(m: BModule, s: string): Rope = + discard cgsym(m, "TGenericSeq") + result = getTempName(m) + addf(m.s[cfsData], "STRING_LITERAL($1, $2, $3);$n", + [result, makeCString(s), rope(len(s))]) + +proc genStringLiteralV1(m: BModule; n: PNode): Rope = + if s.isNil: + result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", []) + else: + let id = nodeTableTestOrSet(m.dataCache, n, m.labels) + if id == m.labels: + # string literal not found in the cache: + result = ropecg(m, "((#NimStringDesc*) &$1)", + [genStringLiteralDataOnlyV1(m, n.strVal)]) + else: + result = ropecg(m, "((#NimStringDesc*) &$1$2)", + [m.tmpBase, rope(id)]) + +# ------ Version 2: destructor based strings and seqs ----------------------- + +proc genStringLiteralDataOnlyV2(m: BModule, s: string): Rope = + result = getTempName(m) + addf(m.s[cfsData], " static const NIM_CHAR $1[$2] = $3;$n", + [result, rope(len(s)+1), makeCString(s)]) + +proc genStringLiteralV2(m: BModule; n: PNode): Rope = + let id = nodeTableTestOrSet(m.dataCache, n, m.labels) + if id == m.labels: + # string literal not found in the cache: + let pureLit = genStringLiteralDataOnlyV2(m, n.strVal) + result = getTempName(m) + addf(m.s[cfsData], "static const #NimStringV2 $1 = {$2, $2, $3};$n", + [result, rope(len(n.strVal)+1), pureLit]) + else: + result = m.tmpBase & rope(id) + +# ------ Version selector --------------------------------------------------- + +proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo): Rope = + case detectStrVersion(m) + of 0, 1: result = genStringLiteralDataOnlyV1(m, s) + of 2: result = genStringLiteralDataOnlyV2(m, s) + else: + localError(m.config, info, "cannot determine how to produce code for string literal") + +proc genStringLiteralFromData(m: BModule; data: Rope; info: TLineInfo): Rope = + result = ropecg(m, "((#NimStringDesc*) &$1)", + [data]) + +proc genNilStringLiteral(m: BModule; info: TLineInfo): Rope = + result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", []) + +proc genStringLiteral(m: BModule; n: PNode): Rope = + case detectStrVersion(m) + of 0, 1: result = genStringLiteralV1(m, n) + of 2: result = genStringLiteralV2(m, n) + else: + localError(m.config, n.info, "cannot determine how to produce code for string literal") diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim index f667be70f..4b4f9c0e6 100644 --- a/compiler/ccgmerge.nim +++ b/compiler/ccgmerge.nim @@ -45,28 +45,28 @@ const ] NimMergeEndMark = "/*\tNIM_merge_END:*/" -proc genSectionStart*(fs: TCFileSection): Rope = - if compilationCachePresent: +proc genSectionStart*(fs: TCFileSection; conf: ConfigRef): Rope = + if compilationCachePresent(conf): result = rope(tnl) add(result, "/*\t") add(result, CFileSectionNames[fs]) add(result, ":*/") add(result, tnl) -proc genSectionEnd*(fs: TCFileSection): Rope = - if compilationCachePresent: +proc genSectionEnd*(fs: TCFileSection; conf: ConfigRef): Rope = + if compilationCachePresent(conf): result = rope(NimMergeEndMark & tnl) -proc genSectionStart*(ps: TCProcSection): Rope = - if compilationCachePresent: +proc genSectionStart*(ps: TCProcSection; conf: ConfigRef): Rope = + if compilationCachePresent(conf): result = rope(tnl) add(result, "/*\t") add(result, CProcSectionNames[ps]) add(result, ":*/") add(result, tnl) -proc genSectionEnd*(ps: TCProcSection): Rope = - if compilationCachePresent: +proc genSectionEnd*(ps: TCProcSection; conf: ConfigRef): Rope = + if compilationCachePresent(conf): result = rope(NimMergeEndMark & tnl) proc writeTypeCache(a: TypeCache, s: var string) = @@ -96,7 +96,7 @@ proc writeIntSet(a: IntSet, s: var string) = s.add('}') proc genMergeInfo*(m: BModule): Rope = - if not compilationCachePresent: return nil + if not compilationCachePresent(m.config): return nil var s = "/*\tNIM_merge_INFO:" s.add(tnl) s.add("typeCache:{") @@ -161,7 +161,7 @@ proc readVerbatimSection(L: var TBaseLexer): Rope = buf = L.buf r.add(tnl) of '\0': - internalError("ccgmerge: expected: " & NimMergeEndMark) + doAssert(false, "ccgmerge: expected: " & NimMergeEndMark) break else: if atEndMark(buf, pos): @@ -179,7 +179,7 @@ proc readKey(L: var TBaseLexer, result: var string) = while buf[pos] in IdentChars: result.add(buf[pos]) inc pos - if buf[pos] != ':': internalError("ccgmerge: ':' expected") + if buf[pos] != ':': doAssert(false, "ccgmerge: ':' expected") L.bufpos = pos + 1 # skip ':' proc newFakeType(id: int): PType = @@ -187,12 +187,12 @@ proc newFakeType(id: int): PType = result.id = id proc readTypeCache(L: var TBaseLexer, result: var TypeCache) = - if ^L.bufpos != '{': internalError("ccgmerge: '{' expected") + if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected") inc L.bufpos while ^L.bufpos != '}': skipWhite(L) var key = decodeStr(L.buf, L.bufpos) - if ^L.bufpos != ':': internalError("ccgmerge: ':' expected") + if ^L.bufpos != ':': doAssert(false, "ccgmerge: ':' expected") inc L.bufpos var value = decodeStr(L.buf, L.bufpos) # XXX implement me @@ -201,7 +201,7 @@ proc readTypeCache(L: var TBaseLexer, result: var TypeCache) = inc L.bufpos proc readIntSet(L: var TBaseLexer, result: var IntSet) = - if ^L.bufpos != '{': internalError("ccgmerge: '{' expected") + if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected") inc L.bufpos while ^L.bufpos != '}': skipWhite(L) @@ -225,7 +225,7 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) = of "labels": m.labels = decodeVInt(L.buf, L.bufpos) of "flags": m.flags = cast[set[CodegenFlag]](decodeVInt(L.buf, L.bufpos) != 0) - else: internalError("ccgmerge: unknown key: " & k) + else: doAssert(false, "ccgmerge: unknown key: " & k) when not defined(nimhygiene): {.pragma: inject.} @@ -275,9 +275,9 @@ proc readMergeSections(cfilename: string, m: var TMergeSections) = if sectionB >= 0 and sectionB <= high(TCProcSection).int: m.p[TCProcSection(sectionB)] = verbatim else: - internalError("ccgmerge: unknown section: " & k) + doAssert(false, "ccgmerge: unknown section: " & k) else: - internalError("ccgmerge: '*/' expected") + doAssert(false, "ccgmerge: '*/' expected") proc mergeRequired*(m: BModule): bool = for i in cfsHeaders..cfsProcs: diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 36816cc2c..91a3add70 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -16,13 +16,17 @@ const # above X strings a hash-switch for strings is generated proc registerGcRoot(p: BProc, v: PSym) = - if gSelectedGC in {gcMarkAndSweep, gcGenerational, gcV2, gcRefc} and + if p.config.selectedGC in {gcMarkAndSweep, gcGenerational, gcV2, gcRefc} and containsGarbageCollectedRef(v.loc.t): # we register a specialized marked proc here; this has the advantage # that it works out of the box for thread local storage then :-) let prc = genTraverseProcForGlobal(p.module, v, v.info) - appcg(p.module, p.module.initProc.procSec(cpsInit), - "#nimRegisterGlobalMarker($1);$n", [prc]) + if sfThread in v.flags: + appcg(p.module, p.module.initProc.procSec(cpsInit), + "#nimRegisterThreadLocalMarker($1);$n", [prc]) + else: + appcg(p.module, p.module.initProc.procSec(cpsInit), + "#nimRegisterGlobalMarker($1);$n", [prc]) proc isAssignedImmediately(n: PNode): bool {.inline.} = if n.kind == nkEmpty: return false @@ -33,15 +37,19 @@ proc isAssignedImmediately(n: PNode): bool {.inline.} = return false result = true +proc inExceptBlockLen(p: BProc): int = + for x in p.nestedTryStmts: + if x.inExcept: result.inc + proc genVarTuple(p: BProc, n: PNode) = var tup, field: TLoc - if n.kind != nkVarTuple: internalError(n.info, "genVarTuple") + if n.kind != nkVarTuple: internalError(p.config, n.info, "genVarTuple") var L = sonsLen(n) # if we have a something that's been captured, use the lowering instead: for i in countup(0, L-3): if n[i].kind != nkSym: - genStmts(p, lowerTupleUnpacking(n, p.prc)) + genStmts(p, lowerTupleUnpacking(p.module.g.graph, n, p.prc)) return genLineDir(p, n) @@ -62,7 +70,7 @@ proc genVarTuple(p: BProc, n: PNode) = if t.kind == tyTuple: field.r = "$1.Field$2" % [rdLoc(tup), rope(i)] else: - if t.n.sons[i].kind != nkSym: internalError(n.info, "genVarTuple") + if t.n.sons[i].kind != nkSym: internalError(p.config, n.info, "genVarTuple") field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n.sons[i].sym, t)] putLocIntoDest(p, v.loc, field) @@ -92,7 +100,7 @@ proc startBlock(p: BProc, start: FormatStr = "{$n", setLen(p.blocks, result + 1) p.blocks[result].id = p.labels p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16 - p.blocks[result].nestedExceptStmts = p.inExceptBlock.int16 + p.blocks[result].nestedExceptStmts = p.inExceptBlockLen.int16 proc assignLabel(b: var TBlock): Rope {.inline.} = b.label = "LA" & b.id.rope @@ -117,7 +125,7 @@ proc endBlock(p: BProc, blockEnd: Rope) = proc endBlock(p: BProc) = let topBlock = p.blocks.len - 1 var blockEnd = if p.blocks[topBlock].label != nil: - rfmt(nil, "} $1: ;$n", p.blocks[topBlock].label) + ropecg(p.module, "} $1: ;$n", p.blocks[topBlock].label) else: ~"}$n" let frameLen = p.blocks[topBlock].frameLen @@ -141,7 +149,7 @@ template preserveBreakIdx(body: untyped): untyped = p.breakIdx = oldBreakIdx proc genState(p: BProc, n: PNode) = - internalAssert n.len == 1 + internalAssert p.config, n.len == 1 let n0 = n[0] if n0.kind == nkIntLit: let idx = n.sons[0].intVal @@ -149,6 +157,39 @@ proc genState(p: BProc, n: PNode) = elif n0.kind == nkStrLit: linefmt(p, cpsStmts, "$1: ;$n", n0.strVal.rope) +proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) = + # Called by return and break stmts. + # Deals with issues faced when jumping out of try/except/finally stmts, + + var stack = newSeq[tuple[n: PNode, inExcept: bool]](0) + + for i in countup(1, howManyTrys): + let tryStmt = p.nestedTryStmts.pop + if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions: + # Pop safe points generated by try + if not tryStmt.inExcept: + linefmt(p, cpsStmts, "#popSafePoint();$n") + + # Pop this try-stmt of the list of nested trys + # so we don't infinite recurse on it in the next step. + stack.add(tryStmt) + + # Find finally-stmt for this try-stmt + # and generate a copy of its sons + var finallyStmt = lastSon(tryStmt.n) + if finallyStmt.kind == nkFinally: + genStmts(p, finallyStmt.sons[0]) + + # push old elements again: + for i in countdown(howManyTrys-1, 0): + p.nestedTryStmts.add(stack[i]) + + if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions: + # Pop exceptions that was handled by the + # except-blocks we are in + for i in countdown(howManyExcepts-1, 0): + linefmt(p, cpsStmts, "#popCurrentException();$n") + proc genGotoState(p: BProc, n: PNode) = # we resist the temptation to translate it into duff's device as it later # will be translated into computed gotos anyway for GCC at least: @@ -159,7 +200,11 @@ proc genGotoState(p: BProc, n: PNode) = initLocExpr(p, n.sons[0], a) lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)]) p.beforeRetNeeded = true - lineF(p, cpsStmts, "case -1: goto BeforeRet_;$n", []) + lineF(p, cpsStmts, "case -1:$n", []) + blockLeaveActions(p, + howManyTrys = p.nestedTryStmts.len, + howManyExcepts = p.inExceptBlockLen) + lineF(p, cpsStmts, " goto BeforeRet_;$n", []) var statesCounter = lastOrd(n.sons[0].typ) if n.len >= 2 and n[1].kind == nkIntLit: statesCounter = n[1].intVal @@ -169,23 +214,21 @@ proc genGotoState(p: BProc, n: PNode) = lineF(p, cpsStmts, "case $2: goto $1$2;$n", [prefix, rope(i)]) lineF(p, cpsStmts, "}$n", []) -proc genBreakState(p: BProc, n: PNode) = +proc genBreakState(p: BProc, n: PNode, d: var TLoc) = var a: TLoc + initLoc(d, locExpr, n, OnUnknown) + if n.sons[0].kind == nkClosure: - # XXX this produces quite inefficient code! initLocExpr(p, n.sons[0].sons[1], a) - lineF(p, cpsStmts, "if (((NI*) $1)[1] < 0) break;$n", [rdLoc(a)]) + d.r = "(((NI*) $1)[1] < 0)" % [rdLoc(a)] else: initLocExpr(p, n.sons[0], a) # the environment is guaranteed to contain the 'state' field at offset 1: - lineF(p, cpsStmts, "if ((((NI*) $1.ClE_0)[1]) < 0) break;$n", [rdLoc(a)]) - # lineF(p, cpsStmts, "if (($1) < 0) break;$n", [rdLoc(a)]) - -proc genVarPrototypeAux(m: BModule, n: PNode) + d.r = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)] proc genGotoVar(p: BProc; value: PNode) = if value.kind notin {nkCharLit..nkUInt64Lit}: - localError(value.info, "'goto' target must be a literal value") + localError(p.config, value.info, "'goto' target must be a literal value") else: lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope]) @@ -217,7 +260,7 @@ proc genSingleVar(p: BProc, a: PNode) = # Alternative construction using default constructor (which may zeromem): # if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc) if sfExportc in v.flags and p.module.g.generatedHeader != nil: - genVarPrototypeAux(p.module.g.generatedHeader, vn) + genVarPrototype(p.module.g.generatedHeader, vn) registerGcRoot(p, v) else: let value = a.sons[2] @@ -239,7 +282,10 @@ proc genSingleVar(p: BProc, a: PNode) = if params != nil: params.add(~", ") assert(sonsLen(typ) == sonsLen(typ.n)) add(params, genOtherArg(p, value, i, typ)) - lineF(p, cpsStmts, "$#($#);$n", [decl, params]) + if params == nil: + lineF(p, cpsStmts, "$#;$n", [decl]) + else: + lineF(p, cpsStmts, "$#($#);$n", [decl, params]) else: initLocExprSingleUse(p, value, tmp) lineF(p, cpsStmts, "$# = $#;$n", [decl, tmp.rdLoc]) @@ -260,28 +306,16 @@ proc genClosureVar(p: BProc, a: PNode) = loadInto(p, a.sons[0], a.sons[2], v) proc genVarStmt(p: BProc, n: PNode) = - for i in countup(0, sonsLen(n) - 1): - var a = n.sons[i] - if a.kind == nkCommentStmt: continue - if a.kind == nkIdentDefs: + for it in n.sons: + if it.kind == nkCommentStmt: continue + if it.kind == nkIdentDefs: # can be a lifted var nowadays ... - if a.sons[0].kind == nkSym: - genSingleVar(p, a) + if it.sons[0].kind == nkSym: + genSingleVar(p, it) else: - genClosureVar(p, a) + genClosureVar(p, it) else: - genVarTuple(p, a) - -proc genConstStmt(p: BProc, t: PNode) = - for i in countup(0, sonsLen(t) - 1): - var it = t.sons[i] - if it.kind == nkCommentStmt: continue - if it.kind != nkConstDef: internalError(t.info, "genConstStmt") - var c = it.sons[0].sym - if c.typ.containsCompileTimeOnly: continue - elif c.typ.kind in ConstantDataTypes and lfNoDecl notin c.loc.flags and - c.ast.len != 0: - if not emitLazily(c): requestConstImpl(p, c) + genVarTuple(p, it) proc genIf(p: BProc, n: PNode, d: var TLoc) = # @@ -302,10 +336,9 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) = getTemp(p, n.typ, d) genLineDir(p, n) let lend = getLabel(p) - for i in countup(0, sonsLen(n) - 1): + for it in n.sons: # bug #4230: avoid false sharing between branches: if d.k == locTemp and isEmptyType(n.typ): d.k = locNone - let it = n.sons[i] if it.len == 2: when newScopeForIf: startBlock(p) initLocExprSingleUse(p, it.sons[0], a) @@ -329,47 +362,9 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) = startBlock(p) expr(p, it.sons[0], d) endBlock(p) - else: internalError(n.info, "genIf()") + else: internalError(p.config, n.info, "genIf()") if sonsLen(n) > 1: fixLabel(p, lend) - -proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) = - # Called by return and break stmts. - # Deals with issues faced when jumping out of try/except/finally stmts, - - var stack: seq[PNode] - newSeq(stack, 0) - - var alreadyPoppedCnt = p.inExceptBlock - for i in countup(1, howManyTrys): - if not p.module.compileToCpp or optNoCppExceptions in gGlobalOptions: - # Pop safe points generated by try - if alreadyPoppedCnt > 0: - dec alreadyPoppedCnt - else: - linefmt(p, cpsStmts, "#popSafePoint();$n") - - # Pop this try-stmt of the list of nested trys - # so we don't infinite recurse on it in the next step. - var tryStmt = p.nestedTryStmts.pop - stack.add(tryStmt) - - # Find finally-stmt for this try-stmt - # and generate a copy of its sons - var finallyStmt = lastSon(tryStmt) - if finallyStmt.kind == nkFinally: - genStmts(p, finallyStmt.sons[0]) - - # push old elements again: - for i in countdown(howManyTrys-1, 0): - p.nestedTryStmts.add(stack[i]) - - if not p.module.compileToCpp or optNoCppExceptions in gGlobalOptions: - # Pop exceptions that was handled by the - # except-blocks we are in - for i in countdown(howManyExcepts-1, 0): - linefmt(p, cpsStmts, "#popCurrentException();$n") - proc genReturnStmt(p: BProc, t: PNode) = if nfPreventCg in t.flags: return p.beforeRetNeeded = true @@ -377,7 +372,7 @@ proc genReturnStmt(p: BProc, t: PNode) = if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0]) blockLeaveActions(p, howManyTrys = p.nestedTryStmts.len, - howManyExcepts = p.inExceptBlock) + howManyExcepts = p.inExceptBlockLen) if (p.finallySafePoints.len > 0): # If we're in a finally block, and we came here by exception # consume it before we return. @@ -391,7 +386,7 @@ proc genGotoForCase(p: BProc; caseStmt: PNode) = 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") + localError(p.config, it.info, "range notation not available for computed goto") return let val = getOrdValue(it.sons[j]) lineF(p, cpsStmts, "NIMSTATE_$#:$n", [val.rope]) @@ -406,19 +401,19 @@ proc genComputedGoto(p: BProc; n: PNode) = let it = n.sons[i] if it.kind == nkCaseStmt: if lastSon(it).kind != nkOfBranch: - localError(it.info, + localError(p.config, it.info, "case statement must be exhaustive for computed goto"); return casePos = i let aSize = lengthOrd(it.sons[0].typ) if aSize > 10_000: - localError(it.info, + localError(p.config, it.info, "case statement has too many cases for computed goto"); return arraySize = aSize.int if firstOrd(it.sons[0].typ) != 0: - localError(it.info, + localError(p.config, it.info, "case statement has to start at 0 for computed goto"); return if casePos < 0: - localError(n.info, "no case statement found for computed goto"); return + localError(p.config, n.info, "no case statement found for computed goto"); return var id = p.labels+1 inc p.labels, arraySize+1 let tmp = "TMP$1_" % [id.rope] @@ -452,7 +447,7 @@ proc genComputedGoto(p: BProc; n: PNode) = 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") + localError(p.config, it.info, "range notation not available for computed goto") return let val = getOrdValue(it.sons[j]) lineF(p, cpsStmts, "TMP$#_:$n", [intLiteral(val+id+1)]) @@ -556,33 +551,38 @@ proc genBreakStmt(p: BProc, t: PNode) = # an unnamed 'break' can only break a loop after 'transf' pass: while idx >= 0 and not p.blocks[idx].isLoop: dec idx if idx < 0 or not p.blocks[idx].isLoop: - internalError(t.info, "no loop to break") + internalError(p.config, t.info, "no loop to break") let label = assignLabel(p.blocks[idx]) blockLeaveActions(p, p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts, - p.inExceptBlock - p.blocks[idx].nestedExceptStmts) + p.inExceptBlockLen - p.blocks[idx].nestedExceptStmts) genLineDir(p, t) lineF(p, cpsStmts, "goto $1;$n", [label]) proc genRaiseStmt(p: BProc, t: PNode) = - if p.inExceptBlock > 0: + if p.module.compileToCpp: + discard cgsym(p.module, "popCurrentExceptionEx") + if p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept: # if the current try stmt have a finally block, # we must execute it before reraising - var finallyBlock = p.nestedTryStmts[p.nestedTryStmts.len - 1].lastSon + var finallyBlock = p.nestedTryStmts[^1].n[^1] if finallyBlock.kind == nkFinally: - genSimpleBlock(p, finallyBlock.sons[0]) - if t.sons[0].kind != nkEmpty: + genSimpleBlock(p, finallyBlock[0]) + if t[0].kind != nkEmpty: var a: TLoc - initLocExpr(p, t.sons[0], a) + initLocExprSingleUse(p, t[0], a) var e = rdLoc(a) - var typ = skipTypes(t.sons[0].typ, abstractPtrs) + var typ = skipTypes(t[0].typ, abstractPtrs) genLineDir(p, t) - lineCg(p, cpsStmts, "#raiseException((#Exception*)$1, $2);$n", - [e, makeCString(typ.sym.name.s)]) + if isImportedException(typ, p.config): + lineF(p, cpsStmts, "throw $1;$n", [e]) + else: + lineCg(p, cpsStmts, "#raiseException((#Exception*)$1, $2);$n", + [e, makeCString(typ.sym.name.s)]) else: genLineDir(p, t) # reraise the last exception: - if p.module.compileToCpp and optNoCppExceptions notin gGlobalOptions: + if p.module.compileToCpp and optNoCppExceptions notin p.config.globalOptions: line(p, cpsStmts, ~"throw;$n") else: linefmt(p, cpsStmts, "#reraiseException();$n") @@ -775,91 +775,75 @@ proc genCase(p: BProc, t: PNode, d: var TLoc) = else: genOrdinalCase(p, t, d) + proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = # code to generate: # - # XXX: There should be a standard dispatch algorithm - # that's used both here and with multi-methods - # # try # { # myDiv(4, 9); - # } catch (NimException& exp) { - # if (isObj(exp, EIO) { - # ... - # } else if (isObj(exp, ESystem) { - # ... - # finallyPart() - # raise; - # } else { - # // general handler - # } - # } - # finallyPart(); + # } catch (NimExceptionType1&) { + # body + # } catch (NimExceptionType2&) { + # finallyPart() + # raise; + # } + # catch(...) { + # general_handler_body + # } + # finallyPart(); + + template genExceptBranchBody(body: PNode) {.dirty.} = + if optStackTrace in p.options: + linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n") + expr(p, body, d) + if not isEmptyType(t.typ) and d.k == locNone: getTemp(p, t.typ, d) genLineDir(p, t) - let exc = getTempName(p.module) - if getCompilerProc("Exception") != nil: - discard cgsym(p.module, "Exception") - else: - discard cgsym(p.module, "E_Base") - add(p.nestedTryStmts, t) + discard cgsym(p.module, "popCurrentExceptionEx") + add(p.nestedTryStmts, (t, false)) startBlock(p, "try {$n") - expr(p, t.sons[0], d) - let length = sonsLen(t) - endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc])) - if optStackTrace in p.options: - linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n") - inc p.inExceptBlock - var i = 1 + expr(p, t[0], d) + endBlock(p) + var catchAllPresent = false - while (i < length) and (t.sons[i].kind == nkExceptBranch): + + p.nestedTryStmts[^1].inExcept = true + for i in 1..<t.len: + if t[i].kind != nkExceptBranch: break + # bug #4230: avoid false sharing between branches: if d.k == locTemp and isEmptyType(t.typ): d.k = locNone - let blen = sonsLen(t.sons[i]) - if i > 1: addf(p.s(cpsStmts), "else ", []) - if blen == 1: + + if t[i].len == 1: # general except section: catchAllPresent = true - startBlock(p) - expr(p, t.sons[i].sons[0], d) - linefmt(p, cpsStmts, "#popCurrentException();$n") + startBlock(p, "catch (...) {$n") + genExceptBranchBody(t[i][0]) endBlock(p) else: - var orExpr: Rope = nil - for j in countup(0, blen - 2): - assert(t.sons[i].sons[j].kind == nkType) - if orExpr != nil: add(orExpr, "||") - appcg(p.module, orExpr, - "#isObj($1.exp->m_type, $2)", - [exc, genTypeInfo(p.module, t[i][j].typ, t[i][j].info)]) - lineF(p, cpsStmts, "if ($1) ", [orExpr]) - startBlock(p) - expr(p, t.sons[i].sons[blen-1], d) - linefmt(p, cpsStmts, "#popCurrentException();$n") - endBlock(p) - inc(i) + for j in 0..t[i].len-2: + if t[i][j].isInfixAs(): + let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:` + fillLoc(exvar.sym.loc, locTemp, exvar, mangleLocalName(p, exvar.sym), OnUnknown) + startBlock(p, "catch ($1& $2) {$n", getTypeDesc(p.module, t[i][j][1].typ), rdLoc(exvar.sym.loc)) + else: + startBlock(p, "catch ($1&) {$n", getTypeDesc(p.module, t[i][j].typ)) + genExceptBranchBody(t[i][^1]) # exception handler body will duplicated for every type + endBlock(p) - # reraise the exception if there was no catch all - # and none of the handlers matched - if not catchAllPresent: - if i > 1: lineF(p, cpsStmts, "else ", []) - startBlock(p) - var finallyBlock = t.lastSon - if finallyBlock.kind == nkFinally: - #expr(p, finallyBlock.sons[0], d) - genStmts(p, finallyBlock.sons[0]) + discard pop(p.nestedTryStmts) + if not catchAllPresent and t[^1].kind == nkFinally: + # finally requires catch all presence + startBlock(p, "catch (...) {$n") + genSimpleBlock(p, t[^1][0]) line(p, cpsStmts, ~"throw;$n") endBlock(p) - lineF(p, cpsStmts, "}$n", []) # end of catch block - dec p.inExceptBlock - - discard pop(p.nestedTryStmts) - if (i < length) and (t.sons[i].kind == nkFinally): - genSimpleBlock(p, t.sons[i].sons[0]) + if t[^1].kind == nkFinally: + genSimpleBlock(p, t[^1][0]) proc genTry(p: BProc, t: PNode, d: var TLoc) = # code to generate: @@ -895,23 +879,20 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = p.module.includeHeader("<setjmp.h>") genLineDir(p, t) var safePoint = getTempName(p.module) - if getCompilerProc("Exception") != nil: - discard cgsym(p.module, "Exception") - else: - discard cgsym(p.module, "E_Base") + discard cgsym(p.module, "Exception") linefmt(p, cpsLocals, "#TSafePoint $1;$n", safePoint) linefmt(p, cpsStmts, "#pushSafePoint(&$1);$n", safePoint) - if isDefined("nimStdSetjmp"): + if isDefined(p.config, "nimStdSetjmp"): linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", safePoint) - elif isDefined("nimSigSetjmp"): + elif isDefined(p.config, "nimSigSetjmp"): linefmt(p, cpsStmts, "$1.status = sigsetjmp($1.context, 0);$n", safePoint) - elif isDefined("nimRawSetjmp"): + elif isDefined(p.config, "nimRawSetjmp"): linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", safePoint) else: linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", safePoint) startBlock(p, "if ($1.status == 0) {$n", [safePoint]) var length = sonsLen(t) - add(p.nestedTryStmts, t) + add(p.nestedTryStmts, (t, false)) expr(p, t.sons[0], d) linefmt(p, cpsStmts, "#popSafePoint();$n") endBlock(p) @@ -919,7 +900,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = linefmt(p, cpsStmts, "#popSafePoint();$n") if optStackTrace in p.options: linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n") - inc p.inExceptBlock + p.nestedTryStmts[^1].inExcept = true var i = 1 while (i < length) and (t.sons[i].kind == nkExceptBranch): # bug #4230: avoid false sharing between branches: @@ -950,7 +931,6 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = linefmt(p, cpsStmts, "#popCurrentException();$n") endBlock(p) inc(i) - dec p.inExceptBlock discard pop(p.nestedTryStmts) endBlock(p) # end of else block if i < length and t.sons[i].kind == nkFinally: @@ -961,19 +941,20 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope = var res = "" - for i in countup(0, sonsLen(t) - 1): - case t.sons[i].kind + for it in t.sons: + case it.kind of nkStrLit..nkTripleStrLit: - res.add(t.sons[i].strVal) + res.add(it.strVal) of nkSym: - var sym = t.sons[i].sym + var sym = it.sym if sym.kind in {skProc, skFunc, skIterator, skMethod}: var a: TLoc - initLocExpr(p, t.sons[i], a) + initLocExpr(p, it, a) res.add($rdLoc(a)) elif sym.kind == skType: res.add($getTypeDesc(p.module, sym.typ)) else: + discard getTypeDesc(p.module, skipTypes(sym.typ, abstractPtrs)) var r = sym.loc.r if r == nil: # if no name has already been given, @@ -982,12 +963,12 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope = sym.loc.r = r # but be consequent! res.add($r) of nkTypeOfExpr: - res.add($getTypeDesc(p.module, t.sons[i].typ)) + res.add($getTypeDesc(p.module, it.typ)) else: + discard getTypeDesc(p.module, skipTypes(it.typ, abstractPtrs)) var a: TLoc - initLocExpr(p, t.sons[i], a) + initLocExpr(p, it, a) res.add($a.rdLoc) - #internalError(t.sons[i].info, "genAsmOrEmitStmt()") if isAsmStmt and hasGnuAsm in CC[cCompiler].props: for x in splitLines(res): @@ -1032,7 +1013,7 @@ proc genEmit(p: BProc, t: PNode) = if p.prc == nil: # top level emit pragma? let section = determineSection(t[1]) - genCLineDir(p.module.s[section], t.info) + genCLineDir(p.module.s[section], t.info, p.config) add(p.module.s[section], s) else: genLineDir(p, t) @@ -1063,8 +1044,7 @@ proc genWatchpoint(p: BProc, n: PNode) = genTypeInfo(p.module, typ, n.info)]) proc genPragma(p: BProc, n: PNode) = - for i in countup(0, sonsLen(n) - 1): - var it = n.sons[i] + for it in n.sons: case whichPragma(it) of wEmit: genEmit(p, it) of wBreakpoint: genBreakPoint(p, it) @@ -1159,4 +1139,4 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = proc genStmts(p: BProc, t: PNode) = var a: TLoc expr(p, t, a) - internalAssert a.k in {locNone, locTemp, locLocalVar} + internalAssert p.config, a.k in {locNone, locTemp, locLocalVar} diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim index 505b69eab..da5c624b7 100644 --- a/compiler/ccgthreadvars.nim +++ b/compiler/ccgthreadvars.nim @@ -12,11 +12,11 @@ # included from cgen.nim -proc emulatedThreadVars(): bool = - result = {optThreads, optTlsEmulation} <= gGlobalOptions +proc emulatedThreadVars(conf: ConfigRef): bool = + result = {optThreads, optTlsEmulation} <= conf.globalOptions proc accessThreadLocalVar(p: BProc, s: PSym) = - if emulatedThreadVars() and not p.threadVarAccessed: + if emulatedThreadVars(p.config) and not p.threadVarAccessed: p.threadVarAccessed = true incl p.module.flags, usesThreadVars addf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV_;$n", []) @@ -37,7 +37,7 @@ var # made to be one. proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) = - if emulatedThreadVars(): + if emulatedThreadVars(m.config): # we gather all thread locals var into a struct; we need to allocate # storage for that somehow, can't use the thread local storage # allocator for it :-( @@ -46,7 +46,7 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) = addf(nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r]) else: if isExtern: add(m.s[cfsVars], "extern ") - if optThreads in gGlobalOptions: add(m.s[cfsVars], "NIM_THREADVAR ") + if optThreads in m.config.globalOptions: add(m.s[cfsVars], "NIM_THREADVAR ") add(m.s[cfsVars], getTypeDesc(m, s.loc.t)) addf(m.s[cfsVars], " $1;$n", [s.loc.r]) @@ -57,7 +57,7 @@ proc generateThreadLocalStorage(m: BModule) = proc generateThreadVarsSize(m: BModule) = if nimtv != nil: - let externc = if gCmd == cmdCompileToCpp or + let externc = if m.config.cmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags: "extern \"C\" " else: "" addf(m.s[cfsProcs], diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index 275c2ddb6..c265064a1 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -17,11 +17,11 @@ type p: BProc visitorFrmt: string -proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType) +proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) proc genCaseRange(p: BProc, branch: PNode) proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) -proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, n: PNode; +proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode; typ: PType) = if n == nil: return case n.kind @@ -29,12 +29,12 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, n: PNode; for i in countup(0, sonsLen(n) - 1): genTraverseProc(c, accessor, n.sons[i], typ) of nkRecCase: - if (n.sons[0].kind != nkSym): internalError(n.info, "genTraverseProc") + if (n.sons[0].kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc") var p = c.p let disc = n.sons[0].sym if disc.loc.r == nil: fillObjectFields(c.p.module, typ) if disc.loc.t == nil: - internalError(n.info, "genTraverseProc()") + internalError(c.p.config, n.info, "genTraverseProc()") lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r]) for i in countup(1, sonsLen(n) - 1): let branch = n.sons[i] @@ -51,9 +51,9 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, n: PNode; if field.typ.kind == tyVoid: return if field.loc.r == nil: fillObjectFields(c.p.module, typ) if field.loc.t == nil: - internalError(n.info, "genTraverseProc()") + internalError(c.p.config, n.info, "genTraverseProc()") genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t) - else: internalError(n.info, "genTraverseProc()") + else: internalError(c.p.config, n.info, "genTraverseProc()") proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} = if not m.compileToCpp: @@ -61,22 +61,23 @@ proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} = else: result = accessor -proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType) = +proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) = if typ == nil: return var p = c.p case typ.kind - of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred: + of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred, + tySink: genTraverseProc(c, accessor, lastSon(typ)) of tyArray: let arraySize = lengthOrd(typ.sons[0]) var i: TLoc - getTemp(p, getSysType(tyInt), i) + getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo(), tyInt), i) let oldCode = p.s(cpsStmts) linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", i.r, arraySize.rope) let oldLen = p.s(cpsStmts).len - genTraverseProc(c, rfmt(nil, "$1[$2]", accessor, i.r), typ.sons[1]) + genTraverseProc(c, ropecg(c.p.module, "$1[$2]", accessor, i.r), typ.sons[1]) if p.s(cpsStmts).len == oldLen: # do not emit dummy long loops for faster debug builds: p.s(cpsStmts) = oldCode @@ -91,20 +92,20 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType) = of tyTuple: let typ = getUniqueType(typ) for i in countup(0, sonsLen(typ) - 1): - genTraverseProc(c, rfmt(nil, "$1.Field$2", accessor, i.rope), typ.sons[i]) + genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", accessor, i.rope), typ.sons[i]) of tyRef, tyString, tySequence: lineCg(p, cpsStmts, c.visitorFrmt, accessor) of tyProc: if typ.callConv == ccClosure: - lineCg(p, cpsStmts, c.visitorFrmt, rfmt(nil, "$1.ClE_0", accessor)) + lineCg(p, cpsStmts, c.visitorFrmt, ropecg(c.p.module, "$1.ClE_0", accessor)) else: discard -proc genTraverseProcSeq(c: var TTraversalClosure, accessor: Rope, typ: PType) = +proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) = var p = c.p assert typ.kind == tySequence var i: TLoc - getTemp(p, getSysType(tyInt), i) + getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo(), tyInt), i) let oldCode = p.s(cpsStmts) lineF(p, cpsStmts, "for ($1 = 0; $1 < $2->$3; $1++) {$n", [i.r, accessor, rope(if c.p.module.compileToCpp: "len" else: "Sup.len")]) @@ -116,18 +117,13 @@ proc genTraverseProcSeq(c: var TTraversalClosure, accessor: Rope, typ: PType) = else: lineF(p, cpsStmts, "}$n", []) -proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash; - reason: TTypeInfoReason): Rope = +proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope = var c: TTraversalClosure var p = newProc(nil, m) result = "Marker_" & getTypeName(m, origTyp, sig) var typ = origTyp.skipTypes(abstractInst) if typ.kind == tyOpt: typ = optLowering(typ) - case reason - of tiNew: c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n" - else: assert false - let header = "static N_NIMCALL(void, $1)(void* p, NI op)" % [result] let t = getTypeDesc(m, typ) @@ -135,6 +131,8 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash; lineF(p, cpsInit, "a = ($1)p;$n", [t]) c.p = p + c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n" + assert typ.kind != tyTypeDesc if typ.kind == tySequence: genTraverseProcSeq(c, "a".rope, typ) @@ -159,7 +157,7 @@ proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope = var sLoc = s.loc.r result = getTempName(m) - if sfThread in s.flags and emulatedThreadVars(): + if sfThread in s.flags and emulatedThreadVars(m.config): accessThreadLocalVar(p, s) sLoc = "NimTV_->" & sLoc diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index c9cd3b125..7b44cddad 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -14,6 +14,8 @@ import sighashes from lowerings import createObj +proc genProcHeader(m: BModule, prc: PSym): Rope + proc isKeyword(w: PIdent): bool = # Nim and C++ share some keywords # it's more efficient to test the whole Nim keywords range @@ -42,38 +44,11 @@ when false: assert p.kind == skPackage result = gDebugInfo.register(p.name.s, m.name.s) -proc idOrSig(m: BModule; s: PSym): Rope = - if s.kind in routineKinds and s.typ != nil: - # signatures for exported routines are reliable enough to - # produce a unique name and this means produced C++ is more stable wrt - # Nim changes: - let sig = hashProc(s) - result = rope($sig) - #let m = if s.typ.callConv != ccInline: findPendingModule(m, s) else: m - let counter = m.sigConflicts.getOrDefault(sig) - #if sigs == "_jckmNePK3i2MFnWwZlp6Lg" and s.name.s == "contains": - # echo "counter ", counter, " ", s.id - if counter != 0: - result.add "_" & rope(counter+1) - # this minor hack is necessary to make tests/collections/thashes compile. - # The inlined hash function's original module is ambiguous so we end up - # generating duplicate names otherwise: - if s.typ.callConv == ccInline: - result.add rope(m.module.name.s) - m.sigConflicts.inc(sig) - else: - let sig = hashNonProc(s) - result = rope($sig) - let counter = m.sigConflicts.getOrDefault(sig) - if counter != 0: - result.add "_" & rope(counter+1) - m.sigConflicts.inc(sig) - proc mangleName(m: BModule; s: PSym): Rope = result = s.loc.r if result == nil: result = s.name.s.mangle.rope - add(result, m.idOrSig(s)) + add(result, idOrSig(s, m.module.name.s, m.sigConflicts)) s.loc.r = result writeMangledName(m.ndi, s) @@ -119,7 +94,7 @@ proc scopeMangledParam(p: BProc; param: PSym) = const irrelevantForBackend = {tyGenericBody, tyGenericInst, tyGenericInvocation, - tyDistinct, tyRange, tyStatic, tyAlias, tyInferred} + tyDistinct, tyRange, tyStatic, tyAlias, tySink, tyInferred} proc typeName(typ: PType): Rope = let typ = typ.skipTypes(irrelevantForBackend) @@ -139,7 +114,7 @@ proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope = t = t.lastSon else: break - let typ = if typ.kind == tyAlias: typ.lastSon else: typ + let typ = if typ.kind in {tyAlias, tySink}: typ.lastSon else: typ if typ.loc.r == nil: typ.loc.r = typ.typeName & $sig else: @@ -147,7 +122,7 @@ proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope = # check consistency: assert($typ.loc.r == $(typ.typeName & $sig)) result = typ.loc.r - if result == nil: internalError("getTypeName: " & $typ.kind) + if result == nil: internalError(m.config, "getTypeName: " & $typ.kind) proc mapSetType(typ: PType): TCTypeKind = case int(getSize(typ)) @@ -167,10 +142,10 @@ proc mapType(typ: PType): TCTypeKind = of tyOpenArray, tyArray, tyVarargs: result = ctArray of tyObject, tyTuple: result = ctStruct of tyUserTypeClasses: - internalAssert typ.isResolvedUserTypeClass + doAssert typ.isResolvedUserTypeClass return mapType(typ.lastSon) of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal, - tyTypeDesc, tyAlias, tyInferred: + tyTypeDesc, tyAlias, tySink, tyInferred: result = mapType(lastSon(typ)) of tyEnum: if firstOrd(typ) < 0: @@ -181,9 +156,9 @@ proc mapType(typ: PType): TCTypeKind = of 2: result = ctUInt16 of 4: result = ctInt32 of 8: result = ctInt64 - else: internalError("mapType") + else: result = ctInt32 of tyRange: result = mapType(typ.sons[0]) - of tyPtr, tyVar, tyRef, tyOptAsRef: + of tyPtr, tyVar, tyLent, tyRef, tyOptAsRef: var base = skipTypes(typ.lastSon, typedescInst) case base.kind of tyOpenArray, tyArray, tyVarargs: result = ctPtrToArray @@ -208,8 +183,8 @@ proc mapType(typ: PType): TCTypeKind = result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt)) of tyStatic: if typ.n != nil: result = mapType(lastSon typ) - else: internalError("mapType") - else: internalError("mapType") + else: doAssert(false, "mapType") + else: doAssert(false, "mapType") proc mapReturnType(typ: PType): TCTypeKind = #if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr @@ -242,7 +217,7 @@ proc isInvalidReturnType(rettype: PType): bool = case mapType(rettype) of ctArray: result = not (skipTypes(rettype, typedescInst).kind in - {tyVar, tyRef, tyPtr}) + {tyVar, tyLent, tyRef, tyPtr}) of ctStruct: let t = skipTypes(rettype, typedescInst) if rettype.isImportedCppType or t.isImportedCppType: return false @@ -264,13 +239,9 @@ proc cacheGetType(tab: TypeCache; sig: SigHash): Rope = result = tab.getOrDefault(sig) proc addAbiCheck(m: BModule, t: PType, name: Rope) = - if isDefined("checkabi"): + if isDefined(m.config, "checkabi"): addf(m.s[cfsTypeInfo], "NIM_CHECK_SIZE($1, $2);$n", [name, rope(getSize(t))]) -proc getTempName(m: BModule): Rope = - result = m.tmpBase & rope(m.labels) - inc m.labels - proc ccgIntroducedPtr(s: PSym): bool = var pt = skipTypes(s.typ, typedescInst) assert skResult != s.kind @@ -316,8 +287,13 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope = of tyPointer: result = typeNameOrLiteral(m, typ, "void*") of tyString: - discard cgsym(m, "NimStringDesc") - result = typeNameOrLiteral(m, typ, "NimStringDesc*") + case detectStrVersion(m) + of 2: + discard cgsym(m, "string") + result = typeNameOrLiteral(m, typ, "NimStringV2") + else: + discard cgsym(m, "NimStringDesc") + result = typeNameOrLiteral(m, typ, "NimStringDesc*") of tyCString: result = typeNameOrLiteral(m, typ, "NCSTRING") of tyBool: result = typeNameOrLiteral(m, typ, "NIM_BOOL") of tyChar: result = typeNameOrLiteral(m, typ, "NIM_CHAR") @@ -327,8 +303,8 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope = of tyDistinct, tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ.sons[0]) of tyStatic: if typ.n != nil: result = getSimpleTypeDesc(m, lastSon typ) - else: internalError("tyStatic for getSimpleTypeDesc") - of tyGenericInst, tyAlias: + else: internalError(m.config, "tyStatic for getSimpleTypeDesc") + of tyGenericInst, tyAlias, tySink: result = getSimpleTypeDesc(m, lastSon typ) else: result = nil @@ -348,7 +324,7 @@ proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope = if result == nil: result = cacheGetType(m.typeCache, sig) proc structOrUnion(t: PType): Rope = - let t = t.skipTypes({tyAlias}) + let t = t.skipTypes({tyAlias, tySink}) (if tfUnion in t.flags: rope("union") else: rope("struct")) proc getForwardStructFormat(m: BModule): string = @@ -368,8 +344,10 @@ proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope = if not isImportedType(concrete): addf(m.s[cfsForwardTypes], getForwardStructFormat(m), [structOrUnion(typ), result]) + else: + pushType(m, concrete) doAssert m.forwTypeCache[sig] == result - else: internalError("getTypeForward(" & $typ.kind & ')') + else: internalError(m.config, "getTypeForward(" & $typ.kind & ')') proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope = ## like getTypeDescAux but creates only a *weak* dependency. In other words @@ -396,7 +374,7 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope = result = getTypeDescAux(m, t, check) proc paramStorageLoc(param: PSym): TStorageLoc = - if param.typ.skipTypes({tyVar, tyTypeDesc}).kind notin { + if param.typ.skipTypes({tyVar, tyLent, tyTypeDesc}).kind notin { tyArray, tyOpenArray, tyVarargs}: result = OnStack else: @@ -411,7 +389,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, else: rettype = getTypeDescAux(m, t.sons[0], check) for i in countup(1, sonsLen(t.n) - 1): - if t.n.sons[i].kind != nkSym: internalError(t.n.info, "genProcParams") + if t.n.sons[i].kind != nkSym: internalError(m.config, t.n.info, "genProcParams") var param = t.n.sons[i].sym if isCompileTimeOnly(param.typ): continue if params != nil: add(params, ~", ") @@ -430,11 +408,11 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, add(params, param.loc.r) # declare the len field for open arrays: var arr = param.typ - if arr.kind == tyVar: arr = arr.sons[0] + if arr.kind in {tyVar, tyLent}: arr = arr.lastSon var j = 0 while arr.kind in {tyOpenArray, tyVarargs}: # this fixes the 'sort' bug: - if param.typ.kind == tyVar: param.loc.storage = OnUnknown + if param.typ.kind in {tyVar, tyLent}: param.loc.storage = OnUnknown # need to pass hidden parameter: addf(params, ", NI $1Len_$2", [param.loc.r, j.rope]) inc(j) @@ -464,7 +442,7 @@ proc mangleRecFieldName(m: BModule; field: PSym, rectype: PType): Rope = result = field.loc.r else: result = rope(mangleField(m, field.name)) - if result == nil: internalError(field.info, "mangleRecFieldName") + if result == nil: internalError(m.config, field.info, "mangleRecFieldName") proc genRecordFieldsAux(m: BModule, n: PNode, accessExpr: Rope, rectype: PType, @@ -475,7 +453,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode, for i in countup(0, sonsLen(n) - 1): add(result, genRecordFieldsAux(m, n.sons[i], accessExpr, rectype, check)) of nkRecCase: - if n.sons[0].kind != nkSym: internalError(n.info, "genRecordFieldsAux") + if n.sons[0].kind != nkSym: internalError(m.config, n.info, "genRecordFieldsAux") add(result, genRecordFieldsAux(m, n.sons[0], accessExpr, rectype, check)) let uname = rope(mangle(n.sons[0].sym.name.s) & 'U') let ae = if accessExpr != nil: "$1.$2" % [accessExpr, uname] @@ -496,14 +474,14 @@ proc genRecordFieldsAux(m: BModule, n: PNode, if hasAttribute in CC[cCompiler].props: add(unionBody, "struct __attribute__((__packed__)){" ) else: - addf(unionBody, "#pragma pack(1)$nstruct{", []) + addf(unionBody, "#pragma pack(push, 1)$nstruct{", []) add(unionBody, a) addf(unionBody, "} $1;$n", [sname]) if tfPacked in rectype.flags and hasAttribute notin CC[cCompiler].props: addf(unionBody, "#pragma pack(pop)$n", []) else: add(unionBody, genRecordFieldsAux(m, k, ae, rectype, check)) - else: internalError("genRecordFieldsAux(record case branch)") + else: internalError(m.config, "genRecordFieldsAux(record case branch)") if unionBody != nil: addf(result, "union{$n$1} $2;$n", [unionBody, uname]) of nkSym: @@ -531,7 +509,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode, # don't use fieldType here because we need the # tyGenericInst for C++ template support addf(result, "$1 $2;$n", [getTypeDescAux(m, field.loc.t, check), sname]) - else: internalError(n.info, "genRecordFieldsAux()") + else: internalError(m.config, n.info, "genRecordFieldsAux()") proc getRecordFields(m: BModule, typ: PType, check: var IntSet): Rope = result = genRecordFieldsAux(m, typ.n, nil, typ, check) @@ -551,7 +529,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope, if hasAttribute in CC[cCompiler].props: result = structOrUnion(typ) & " __attribute__((__packed__))" else: - result = "#pragma pack(1)" & tnl & structOrUnion(typ) + result = "#pragma pack(push, 1)" & tnl & structOrUnion(typ) else: result = structOrUnion(typ) @@ -569,6 +547,16 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope, elif m.compileToCpp: appcg(m, result, " : public $1 {$n", [getTypeDescAux(m, typ.sons[0].skipTypes(skipPtrs), check)]) + if typ.isException: + appcg(m, result, "virtual void raise() {throw *this;}$n") # required for polymorphic exceptions + if typ.sym.magic == mException: + # Add cleanup destructor to Exception base class + appcg(m, result, "~$1() {if(this->raise_id) popCurrentExceptionEx(this->raise_id);}$n", [name]) + # hack: forward declare popCurrentExceptionEx() on top of type description, + # proper request to generate popCurrentExceptionEx not possible for 2 reasons: + # generated function will be below declared Exception type and circular dependency + # between Exception and popCurrentExceptionEx function + result = genProcHeader(m, magicsys.getCompilerProc(m.g.graph, "popCurrentExceptionEx")) & ";" & rnl & result hasField = true else: appcg(m, result, " {$n $1 Sup;$n", @@ -615,8 +603,10 @@ proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool = return false proc resolveStarsInCppType(typ: PType, idx, stars: int): PType = - # XXX: we should catch this earlier and report it as a semantic error - if idx >= typ.len: internalError "invalid apostrophe type parameter index" + # Make sure the index refers to one of the generic params of the type. + # XXX: we should catch this earlier and report it as a semantic error. + if idx >= typ.len: + doAssert false, "invalid apostrophe type parameter index" result = typ.sons[idx] for i in 1..stars: @@ -629,7 +619,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = var t = origTyp.skipTypes(irrelevantForBackend) if containsOrIncl(check, t.id): if not (isImportedCppType(origTyp) or isImportedCppType(t)): - internalError("cannot generate C type for: " & typeToString(origTyp)) + internalError(m.config, "cannot generate C type for: " & typeToString(origTyp)) # XXX: this BUG is hard to fix -> we need to introduce helper structs, # but determining when this needs to be done is hard. We should split # C type generation into an analysis and a code generation phase somehow. @@ -641,7 +631,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = excl(check, t.id) return case t.kind - of tyRef, tyOptAsRef, tyPtr, tyVar: + of tyRef, tyOptAsRef, tyPtr, tyVar, tyLent: var star = if t.kind == tyVar and tfVarIsPtr notin origTyp.flags and compileToCpp(m): "&" else: "*" var et = origTyp.skipTypes(abstractInst).lastSon @@ -661,7 +651,6 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = let name = getTypeForward(m, et, hashType et) result = name & star m.typeCache[sig] = result - pushType(m, et) of tySequence: # no restriction! We have a forward declaration for structs let name = getTypeForward(m, et, hashType et) @@ -708,7 +697,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = of 2: addf(m.s[cfsTypes], "typedef NU16 $1;$n", [result]) of 4: addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result]) of 8: addf(m.s[cfsTypes], "typedef NI64 $1;$n", [result]) - else: internalError(t.sym.info, "getTypeDescAux: enum") + else: internalError(m.config, t.sym.info, "getTypeDescAux: enum") when false: let owner = hashOwner(t.sym) if not gDebugInfo.hasEnum(t.sym.name.s, t.sym.info.line, owner): @@ -818,6 +807,9 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = let typeInSlot = resolveStarsInCppType(origTyp, idx + 1, stars) if typeInSlot == nil or typeInSlot.kind == tyVoid: result.add(~"void") + elif typeInSlot.kind == tyStatic: + internalAssert m.config, typeInSlot.n != nil + result.add typeInSlot.n.renderTree else: result.add getTypeDescAux(m, typeInSlot, check) else: @@ -834,6 +826,13 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = # always call for sideeffects: assert t.kind != tyTuple discard getRecordDesc(m, t, result, check) + # The resulting type will include commas and these won't play well + # with the C macros for defining procs such as N_NIMCALL. We must + # create a typedef for the type and use it in the proc signature: + let typedefName = ~"TY" & $sig + addf(m.s[cfsTypes], "typedef $1 $2;$n", [result, typedefName]) + m.typeCache[sig] = typedefName + result = typedefName else: when false: if t.sym != nil and t.sym.name.s == "KeyValuePair": @@ -872,11 +871,11 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = of 1, 2, 4, 8: addf(m.s[cfsTypes], "typedef NU$2 $1;$n", [result, rope(s*8)]) else: addf(m.s[cfsTypes], "typedef NU8 $1[$2];$n", [result, rope(getSize(t))]) - of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, + of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink, tyUserTypeClass, tyUserTypeClassInst, tyInferred: result = getTypeDescAux(m, lastSon(t), check) else: - internalError("getTypeDescAux(" & $t.kind & ')') + internalError(m.config, "getTypeDescAux(" & $t.kind & ')') result = nil # fixes bug #145: excl(check, t.id) @@ -916,7 +915,7 @@ template cgDeclFrmt*(s: PSym): string = s.constraint.strVal proc genProcHeader(m: BModule, prc: PSym): Rope = var rettype, params: Rope - genCLineDir(result, prc.info) + genCLineDir(result, prc.info, m.config) # using static is needed for inline procs if lfExportLib in prc.loc.flags: if isHeaderFile in m.flags: @@ -969,8 +968,9 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; if flags != 0: addf(m.s[cfsTypeInit3], "$1.flags = $2;$n", [name, rope(flags)]) discard cgsym(m, "TNimType") - if isDefined("nimTypeNames"): - var typename = typeToString(origType, preferName) + if isDefined(m.config, "nimTypeNames"): + var typename = typeToString(if origType.typeInst != nil: origType.typeInst + else: origType, preferName) if typename == "ref object" and origType.skipTypes(skipPtrs).sym != nil: typename = "anon ref object from " & $origType.skipTypes(skipPtrs).sym.info addf(m.s[cfsTypeInit3], "$1.name = $2;$n", @@ -1000,7 +1000,7 @@ proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope = while lookupInRecord(objtype.n, d.name) == nil: objtype = objtype.sons[0] if objtype.sym == nil: - internalError(d.info, "anonymous obj with discriminator") + internalError(m.config, d.info, "anonymous obj with discriminator") result = "NimDT_$1_$2" % [rope($hashType(objtype)), rope(d.name.s.mangle)] proc discriminatorTableDecl(m: BModule, objtype: PType, d: PSym): Rope = @@ -1034,7 +1034,7 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope; assert L > 0 if field.loc.r == nil: fillObjectFields(m, typ) if field.loc.t == nil: - internalError(n.info, "genObjectFields") + internalError(m.config, n.info, "genObjectFields") addf(m.s[cfsTypeInit3], "$1.kind = 3;$n" & "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" & "$1.name = $5;$n" & "$1.sons = &$6[0];$n" & @@ -1050,7 +1050,7 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope; case b.kind of nkOfBranch: if sonsLen(b) < 2: - internalError(b.info, "genObjectFields; nkOfBranch broken") + internalError(m.config, b.info, "genObjectFields; nkOfBranch broken") for j in countup(0, sonsLen(b) - 2): if b.sons[j].kind == nkRange: var x = int(getOrdValue(b.sons[j].sons[0])) @@ -1064,23 +1064,23 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope; of nkElse: addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, rope(L), tmp2]) - else: internalError(n.info, "genObjectFields(nkRecCase)") + else: internalError(m.config, n.info, "genObjectFields(nkRecCase)") of nkSym: var field = n.sym if field.bitsize == 0: if field.loc.r == nil: fillObjectFields(m, typ) if field.loc.t == nil: - internalError(n.info, "genObjectFields") + internalError(m.config, n.info, "genObjectFields") addf(m.s[cfsTypeInit3], "$1.kind = 1;$n" & "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" & "$1.name = $5;$n", [expr, getTypeDesc(m, origType), field.loc.r, genTypeInfo(m, field.typ, info), makeCString(field.name.s)]) - else: internalError(n.info, "genObjectFields") + else: internalError(m.config, n.info, "genObjectFields") proc genObjectInfo(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo) = if typ.kind == tyObject: if incompleteType(typ): - localError(info, "request for RTTI generation for incomplete object: " & + localError(m.config, info, "request for RTTI generation for incomplete object: " & typeToString(typ)) genTypeInfoAux(m, typ, origType, name, info) else: @@ -1171,19 +1171,15 @@ proc genSetInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) = proc genArrayInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) = genTypeInfoAuxBase(m, typ, typ, name, genTypeInfo(m, typ.sons[1], info), info) -proc fakeClosureType(owner: PSym): PType = +proc fakeClosureType(m: BModule; owner: PSym): PType = # we generate the same RTTI as for a tuple[pointer, ref tuple[]] result = newType(tyTuple, owner) result.rawAddSon(newType(tyPointer, owner)) var r = newType(tyRef, owner) - let obj = createObj(owner, owner.info, final=false) + let obj = createObj(m.g.graph, owner, owner.info, final=false) r.rawAddSon(obj) result.rawAddSon(r) -type - TTypeInfoReason = enum ## for what do we need the type info? - tiNew, ## for 'new' - include ccgtrav proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) = @@ -1227,24 +1223,24 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope = m.g.typeInfoMarker[sig] = result case t.kind of tyEmpty, tyVoid: result = rope"0" - of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar: + of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar, tyLent: genTypeInfoAuxBase(m, t, t, result, rope"0", info) of tyStatic: if t.n != nil: result = genTypeInfo(m, lastSon t, info) - else: internalError("genTypeInfo(" & $t.kind & ')') + else: internalError(m.config, "genTypeInfo(" & $t.kind & ')') of tyUserTypeClasses: - internalAssert t.isResolvedUserTypeClass + internalAssert m.config, t.isResolvedUserTypeClass return genTypeInfo(m, t.lastSon, info) of tyProc: if t.callConv != ccClosure: genTypeInfoAuxBase(m, t, t, result, rope"0", info) else: - let x = fakeClosureType(t.owner) + let x = fakeClosureType(m, t.owner) genTupleInfo(m, x, x, result, info) of tySequence, tyRef, tyOptAsRef: genTypeInfoAux(m, t, t, result, info) - if gSelectedGC >= gcMarkAndSweep: - let markerProc = genTraverseProc(m, origType, sig, tiNew) + if m.config.selectedGC >= gcMarkAndSweep: + let markerProc = genTraverseProc(m, origType, sig) addf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [result, markerProc]) of tyPtr, tyRange: genTypeInfoAux(m, t, t, result, info) of tyArray: genArrayInfo(m, t, result, info) @@ -1257,7 +1253,7 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope = # BUGFIX: use consistently RTTI without proper field names; otherwise # results are not deterministic! genTupleInfo(m, t, origType, result, info) - else: internalError("genTypeInfo(" & $t.kind & ')') + else: internalError(m.config, "genTypeInfo(" & $t.kind & ')') if t.deepCopy != nil: genDeepCopyProc(m, t.deepCopy, result) elif origType.deepCopy != nil: diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index b1a268c9e..a6080a808 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -53,7 +53,7 @@ proc hashString*(s: string): BiggestInt = result = a var - gTypeTable: array[TTypeKind, TIdTable] + gTypeTable: array[TTypeKind, TIdTable] # XXX globals here gCanonicalTypes: array[TTypeKind, PType] proc initTypeTables() = @@ -110,13 +110,13 @@ proc getUniqueType*(key: PType): PType = of tyDistinct: if key.deepCopy != nil: result = key else: result = getUniqueType(lastSon(key)) - of tyGenericInst, tyOrdinal, tyStatic, tyAlias, tyInferred: + of tyGenericInst, tyOrdinal, tyStatic, tyAlias, tySink, tyInferred: result = getUniqueType(lastSon(key)) #let obj = lastSon(key) #if obj.sym != nil and obj.sym.name.s == "TOption": # echo "for ", typeToString(key), " I returned " # debug result - of tyPtr, tyRef, tyVar: + of tyPtr, tyRef, tyVar, tyLent: 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 @@ -211,8 +211,4 @@ proc mangle*(name: string): string = if requiresUnderscore: result.add "_" -proc emitLazily*(s: PSym): bool {.inline.} = - result = optDeadCodeElim in gGlobalOptions or - sfDeadCodeElim in getModule(s).flags - initTypeTables() diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 630426cfd..6a16474c0 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -11,7 +11,7 @@ import ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets, - nversion, nimsets, msgs, securehash, bitsets, idents, types, + nversion, nimsets, msgs, std / sha1, bitsets, idents, types, ccgutils, os, ropes, math, passes, rodread, wordrecg, treetab, cgmeth, condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, lowerings, semparallel, tables, sets, ndi @@ -19,6 +19,7 @@ import import strutils except `%` # collides with ropes.`%` from modulegraphs import ModuleGraph +from configuration import warnGcMem, errXMustBeCompileTime, hintDependency, errGenerated import dynlib when not declared(dynlib.libCandidates): @@ -92,6 +93,7 @@ proc useHeader(m: BModule, sym: PSym) = proc cgsym(m: BModule, name: string): Rope proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope = + assert m != nil var i = 0 var length = len(frmt) result = nil @@ -115,15 +117,15 @@ proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope = if i >= length or not (frmt[i] in {'0'..'9'}): break num = j if j > high(args) + 1: - internalError("ropes: invalid format string $" & $j) + internalError(m.config, "ropes: invalid format string $" & $j) add(result, args[j-1]) of 'n': - if optLineDir notin gOptions: add(result, rnl) + if optLineDir notin m.config.options: add(result, rnl) inc(i) of 'N': add(result, rnl) inc(i) - else: internalError("ropes: invalid format string $" & frmt[i]) + else: internalError(m.config, "ropes: invalid format string $" & frmt[i]) elif frmt[i] == '#' and frmt[i+1] in IdentStartChars: inc(i) var j = i @@ -145,15 +147,10 @@ proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope = if i - 1 >= start: add(result, substr(frmt, start, i - 1)) -template rfmt(m: BModule, fmt: string, args: varargs[Rope]): untyped = - ropecg(m, fmt, args) - -var indent = "\t".rope - proc indentLine(p: BProc, r: Rope): Rope = result = r for i in countup(0, p.blocks.len-1): - prepend(result, indent) + prepend(result, "\t".rope) proc appcg(m: BModule, c: var Rope, frmt: FormatStr, args: varargs[Rope]) = @@ -189,14 +186,14 @@ proc safeLineNm(info: TLineInfo): int = result = toLinenumber(info) if result < 0: result = 0 # negative numbers are not allowed in #line -proc genCLineDir(r: var Rope, filename: string, line: int) = +proc genCLineDir(r: var Rope, filename: string, line: int; conf: ConfigRef) = assert line >= 0 - if optLineDir in gOptions: + if optLineDir in conf.options: addf(r, "$N#line $2 $1$N", [rope(makeSingleLineCString(filename)), rope(line)]) -proc genCLineDir(r: var Rope, info: TLineInfo) = - genCLineDir(r, info.toFullPath, info.safeLineNm) +proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) = + genCLineDir(r, info.toFullPath, info.safeLineNm, conf) proc freshLineInfo(p: BProc; info: TLineInfo): bool = if p.lastLineInfo.line != info.line or @@ -213,9 +210,9 @@ proc genLineDir(p: BProc, t: PNode) = tt = tt.sons[1] let line = tt.info.safeLineNm - if optEmbedOrigSrc in gGlobalOptions: - add(p.s(cpsStmts), ~"//" & tt.info.sourceLine & rnl) - genCLineDir(p.s(cpsStmts), tt.info.toFullPath, line) + if optEmbedOrigSrc in p.config.globalOptions: + add(p.s(cpsStmts), ~"//" & sourceLine(p.config, tt.info) & rnl) + genCLineDir(p.s(cpsStmts), tt.info.toFullPath, line, p.config) if ({optStackTrace, optEndb} * p.options == {optStackTrace, optEndb}) and (p.prc == nil or sfPure notin p.prc.flags): if freshLineInfo(p, tt.info): @@ -223,22 +220,27 @@ proc genLineDir(p: BProc, t: PNode) = line.rope, makeCString(toFilename(tt.info))) elif ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and - (p.prc == nil or sfPure notin p.prc.flags) and tt.info.fileIndex >= 0: + (p.prc == nil or sfPure notin p.prc.flags) and tt.info.fileIndex != InvalidFileIDX: if freshLineInfo(p, tt.info): linefmt(p, cpsStmts, "nimln_($1, $2);$n", - line.rope, tt.info.quotedFilename) + line.rope, quotedFilename(p.config, tt.info)) proc postStmtActions(p: BProc) {.inline.} = add(p.s(cpsStmts), p.module.injectStmt) proc accessThreadLocalVar(p: BProc, s: PSym) -proc emulatedThreadVars(): bool {.inline.} +proc emulatedThreadVars(conf: ConfigRef): bool {.inline.} proc genProc(m: BModule, prc: PSym) template compileToCpp(m: BModule): untyped = - gCmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags + m.config.cmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags -include "ccgtypes.nim" +proc getTempName(m: BModule): Rope = + result = m.tmpBase & rope(m.labels) + inc m.labels + +include ccgliterals +include ccgtypes # ------------------------------ Manager of temporaries ------------------ @@ -260,6 +262,11 @@ proc rdCharLoc(a: TLoc): Rope = proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc, takeAddr: bool) = + if p.module.compileToCpp and t.isException and not isDefined(p.config, "noCppExceptions"): + # init vtable in Exception object for polymorphic exceptions + includeHeader(p.module, "<new>") + linefmt(p, section, "new ($1) $2;$n", rdLoc(a), getTypeDesc(p.module, t)) + case analyseObjectWithTypeField(t) of frNone: discard @@ -312,8 +319,10 @@ proc resetLoc(p: BProc, loc: var TLoc) = genObjectInit(p, cpsStmts, loc.t, loc, true) else: useStringh(p.module) + # array passed as argument decayed into pointer, bug #7332 + # so we use getTypeDesc here rather than rdLoc(loc) linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n", - addrLoc(loc), rdLoc(loc)) + addrLoc(loc), getTypeDesc(p.module, loc.t)) # XXX: We can be extra clever here and call memset only # on the bytes following the m_type field? genObjectInit(p, cpsStmts, loc.t, loc, true) @@ -330,7 +339,7 @@ proc constructLoc(p: BProc, loc: TLoc, isTemp = false) = if not isImportedCppType(typ): useStringh(p.module) linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n", - addrLoc(loc), rdLoc(loc)) + addrLoc(loc), getTypeDesc(p.module, typ)) genObjectInit(p, cpsStmts, loc.t, loc, true) proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) = @@ -361,7 +370,7 @@ proc getIntTemp(p: BProc, result: var TLoc) = linefmt(p, cpsLocals, "NI $1;$n", result.r) result.k = locTemp result.storage = OnStack - result.lode = lodeTyp getSysType(tyInt) + result.lode = lodeTyp getSysType(p.module.g.graph, unknownLineInfo(), tyInt) result.flags = {} proc initGCFrame(p: BProc): Rope = @@ -405,7 +414,7 @@ proc assignLocalVar(p: BProc, n: PNode) = #assert(s.loc.k == locNone) # not yet assigned # this need not be fulfilled for inline procs; they are regenerated # for each module that uses them! - let nl = if optLineDir in gOptions: "" else: tnl + let nl = if optLineDir in p.config.options: "" else: tnl let decl = localVarDecl(p, n) & ";" & nl line(p, cpsLocals, decl) localDebugInfo(p, n.sym) @@ -499,24 +508,24 @@ proc initFrame(p: BProc, procname, filename: Rope): Rope = discard cgsym(p.module, "nimFrame") if p.maxFrameLen > 0: discard cgsym(p.module, "VarSlot") - result = rfmt(nil, "\tnimfrs_($1, $2, $3, $4);$n", + result = ropecg(p.module, "\tnimfrs_($1, $2, $3, $4);$n", procname, filename, p.maxFrameLen.rope, p.blocks[0].frameLen.rope) else: - result = rfmt(nil, "\tnimfr_($1, $2);$n", procname, filename) + result = ropecg(p.module, "\tnimfr_($1, $2);$n", procname, filename) proc initFrameNoDebug(p: BProc; frame, procname, filename: Rope; line: int): Rope = discard cgsym(p.module, "nimFrame") addf(p.blocks[0].sections[cpsLocals], "TFrame $1;$n", [frame]) - result = rfmt(nil, "\t$1.procname = $2; $1.filename = $3; " & + result = ropecg(p.module, "\t$1.procname = $2; $1.filename = $3; " & " $1.line = $4; $1.len = -1; nimFrame(&$1);$n", frame, procname, filename, rope(line)) proc deinitFrameNoDebug(p: BProc; frame: Rope): Rope = - result = rfmt(p.module, "\t#popFrameOfAddr(&$1);$n", frame) + result = ropecg(p.module, "\t#popFrameOfAddr(&$1);$n", frame) proc deinitFrame(p: BProc): Rope = - result = rfmt(p.module, "\t#popFrame();$n") + result = ropecg(p.module, "\t#popFrame();$n") include ccgexprs @@ -539,16 +548,18 @@ proc loadDynamicLib(m: BModule, lib: PLib) = if lib.path.kind in {nkStrLit..nkTripleStrLit}: var s: TStringSeq = @[] libCandidates(lib.path.strVal, s) - rawMessage(hintDependency, lib.path.strVal) + rawMessage(m.config, hintDependency, lib.path.strVal) var loadlib: Rope = nil for i in countup(0, high(s)): inc(m.labels) if i > 0: add(loadlib, "||") - appcg(m, loadlib, "($1 = #nimLoadLibrary((#NimStringDesc*) &$2))$n", - [tmp, getStrLit(m, s[i])]) + let n = newStrNode(nkStrLit, s[i]) + n.info = lib.path.info + appcg(m, loadlib, "($1 = #nimLoadLibrary($2))$n", + [tmp, genStringLiteral(m, n)]) appcg(m, m.s[cfsDynLibInit], - "if (!($1)) #nimLoadLibraryError((#NimStringDesc*) &$2);$n", - [loadlib, getStrLit(m, lib.path.strVal)]) + "if (!($1)) #nimLoadLibraryError($2);$n", + [loadlib, genStringLiteral(m, lib.path)]) else: var p = newProc(nil, m) p.options = p.options - {optStackTrace, optEndb} @@ -561,7 +572,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) = "if (!($1 = #nimLoadLibrary($2))) #nimLoadLibraryError($2);$n", [tmp, rdLoc(dest)]) - if lib.name == nil: internalError("loadDynamicLib") + if lib.name == nil: internalError(m.config, "loadDynamicLib") proc mangleDynLibProc(sym: PSym): Rope = if sfCompilerProc in sym.flags: @@ -592,14 +603,14 @@ proc symInDynamicLib(m: BModule, sym: PSym) = [tmp, getTypeDesc(m, sym.typ), params, makeCString($extname)] var last = lastSon(n) if last.kind == nkHiddenStdConv: last = last.sons[1] - internalAssert(last.kind == nkStrLit) + internalAssert(m.config, last.kind == nkStrLit) let idx = last.strVal if idx.len == 0: add(m.initProc.s(cpsStmts), load) elif idx.len == 1 and idx[0] in {'0'..'9'}: add(m.extensionLoaders[idx[0]], load) else: - internalError(sym.info, "wrong index: " & idx) + internalError(m.config, sym.info, "wrong index: " & idx) else: appcg(m, m.s[cfsDynLibInit], "\t$1 = ($2) #nimGetProcAddr($3, $4);$n", @@ -625,18 +636,18 @@ proc symInDynamicLibPartial(m: BModule, sym: PSym) = sym.typ.sym = nil # generate a new name proc cgsym(m: BModule, name: string): Rope = - let sym = magicsys.getCompilerProc(name) + let sym = magicsys.getCompilerProc(m.g.graph, name) if sym != nil: case sym.kind of skProc, skFunc, skMethod, skConverter, skIterator: genProc(m, sym) of skVar, skResult, skLet: genVarPrototype(m, newSymNode sym) of skType: discard getTypeDesc(m, sym.typ) - else: internalError("cgsym: " & name & ": " & $sym.kind) + else: internalError(m.config, "cgsym: " & name & ": " & $sym.kind) else: # we used to exclude the system module from this check, but for DLL # generation support this sloppyness leads to hard to detect bugs, so # we're picky here for the system module too: - rawMessage(errSystemNeeds, name) + rawMessage(m.config, errGenerated, "system module needs: " & name) result = sym.loc.r proc generateHeaders(m: BModule) = @@ -662,12 +673,18 @@ proc generateHeaders(m: BModule) = add(m.s[cfsHeaders], "#undef powerpc" & tnl) add(m.s[cfsHeaders], "#undef unix" & tnl) +proc openNamespaceNim(): Rope = + result.add("namespace Nim {" & tnl) + +proc closeNamespaceNim(): Rope = + result.add("}" & tnl) + proc closureSetup(p: BProc, prc: PSym) = if tfCapturesEnv notin prc.typ.flags: return # prc.ast[paramsPos].last contains the type we're after: var ls = lastSon(prc.ast[paramsPos]) if ls.kind != nkSym: - internalError(prc.info, "closure generation failed") + internalError(p.config, prc.info, "closure generation failed") var env = ls.sym #echo "created environment: ", env.id, " for ", prc.name.s assignLocalVar(p, ls) @@ -707,7 +724,7 @@ proc genProcAux(m: BModule, prc: PSym) = assert(prc.ast != nil) if sfPure notin prc.flags and prc.typ.sons[0] != nil: if resultPos >= prc.ast.len: - internalError(prc.info, "proc has no result symbol") + internalError(m.config, prc.info, "proc has no result symbol") let resNode = prc.ast.sons[resultPos] let res = resNode.sym # get result symbol if not isInvalidReturnType(prc.typ.sons[0]): @@ -722,10 +739,11 @@ proc genProcAux(m: BModule, prc: PSym) = assignLocalVar(p, resNode) assert(res.loc.r != nil) initLocalVar(p, res, immediateAsgn=false) - returnStmt = rfmt(nil, "\treturn $1;$n", rdLoc(res.loc)) + returnStmt = ropecg(p.module, "\treturn $1;$n", rdLoc(res.loc)) else: fillResult(resNode) assignParam(p, res) + if sfNoInit notin prc.flags: resetLoc(p, res.loc) if skipTypes(res.typ, abstractInst).kind == tyArray: #incl(res.loc.flags, lfIndirect) res.loc.storage = OnUnknown @@ -743,15 +761,15 @@ proc genProcAux(m: BModule, prc: PSym) = if sfPure in prc.flags: if hasDeclspec in extccomp.CC[extccomp.cCompiler].props: header = "__declspec(naked) " & header - generatedProc = rfmt(nil, "$N$1 {$n$2$3$4}$N$N", + generatedProc = ropecg(p.module, "$N$1 {$n$2$3$4}$N$N", header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)) else: - generatedProc = rfmt(nil, "$N$1 {$N", header) + generatedProc = ropecg(p.module, "$N$1 {$N", header) add(generatedProc, initGCFrame(p)) if optStackTrace in prc.options: add(generatedProc, p.s(cpsLocals)) var procname = makeCString(prc.name.s) - add(generatedProc, initFrame(p, procname, prc.info.quotedFilename)) + add(generatedProc, initFrame(p, procname, quotedFilename(p.config, prc.info))) else: add(generatedProc, p.s(cpsLocals)) if optProfiler in prc.options: @@ -770,10 +788,10 @@ proc genProcAux(m: BModule, prc: PSym) = proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} = result = (sfCompileToCpp in m.module.flags and sfCompileToCpp notin sym.getModule().flags and - gCmd != cmdCompileToCpp) or ( + m.config.cmd != cmdCompileToCpp) or ( sym.flags * {sfImportc, sfInfixCall, sfCompilerProc} == {sfImportc} and sym.magic == mNone and - gCmd == cmdCompileToCpp) + m.config.cmd == cmdCompileToCpp) proc genProcPrototype(m: BModule, sym: PSym) = useHeader(m, sym) @@ -781,7 +799,7 @@ proc genProcPrototype(m: BModule, sym: PSym) = if lfDynamicLib in sym.loc.flags: if getModule(sym).id != m.module.id and not containsOrIncl(m.declaredThings, sym.id): - add(m.s[cfsVars], rfmt(nil, "extern $1 $2;$n", + add(m.s[cfsVars], ropecg(m, "extern $1 $2;$n", getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym))) elif not containsOrIncl(m.declaredProtos, sym.id): var header = genProcHeader(m, sym) @@ -793,7 +811,7 @@ proc genProcPrototype(m: BModule, sym: PSym) = header.add(" __attribute__((naked))") if sfNoReturn in sym.flags and hasAttribute in CC[cCompiler].props: header.add(" __attribute__((noreturn))") - add(m.s[cfsProcHeaders], rfmt(nil, "$1;$n", header)) + add(m.s[cfsProcHeaders], ropecg(m, "$1;$n", header)) proc genProcNoForward(m: BModule, prc: PSym) = if lfImportCompilerProc in prc.loc.flags: @@ -879,7 +897,7 @@ proc genProc(m: BModule, prc: PSym) = if not containsOrIncl(m.g.generatedHeader.declaredThings, prc.id): genProcAux(m.g.generatedHeader, prc) -proc genVarPrototypeAux(m: BModule, n: PNode) = +proc genVarPrototype(m: BModule, n: PNode) = #assert(sfGlobal in sym.flags) let sym = n.sym useHeader(m, sym) @@ -899,17 +917,14 @@ proc genVarPrototypeAux(m: BModule, n: PNode) = if sfVolatile in sym.flags: add(m.s[cfsVars], " volatile") addf(m.s[cfsVars], " $1;$n", [sym.loc.r]) -proc genVarPrototype(m: BModule, n: PNode) = - genVarPrototypeAux(m, n) - -proc addIntTypes(result: var Rope) {.inline.} = +proc addIntTypes(result: var Rope; conf: ConfigRef) {.inline.} = addf(result, "#define NIM_NEW_MANGLING_RULES" & tnl & "#define NIM_INTBITS $1" & tnl, [ platform.CPU[targetCPU].intSize.rope]) + if optUseNimNamespace in conf.globalOptions: result.add("#define USE_NIM_NAMESPACE" & tnl) -proc getCopyright(cfile: Cfile): Rope = - const copyrightYear = "2017" - if optCompileOnly in gGlobalOptions: +proc getCopyright(conf: ConfigRef; cfile: Cfile): Rope = + if optCompileOnly in conf.globalOptions: result = ("/* Generated by Nim Compiler v$1 */$N" & "/* (c) " & copyrightYear & " Andreas Rumpf */$N" & "/* The generated code is subject to the original license. */$N") % @@ -924,11 +939,11 @@ proc getCopyright(cfile: Cfile): Rope = rope(platform.OS[targetOS].name), rope(platform.CPU[targetCPU].name), rope(extccomp.CC[extccomp.cCompiler].name), - rope(getCompileCFileCmd(cfile))] + rope(getCompileCFileCmd(conf, cfile))] -proc getFileHeader(cfile: Cfile): Rope = - result = getCopyright(cfile) - addIntTypes(result) +proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope = + result = getCopyright(conf, cfile) + addIntTypes(result, conf) proc genFilenames(m: BModule): Rope = discard cgsym(m, "dbgRegisterFilename") @@ -1033,8 +1048,8 @@ proc genMainProc(m: BModule) = var nimMain, otherMain: FormatStr if platform.targetOS == osWindows and - gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}: - if optGenGuiApp in gGlobalOptions: + m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}: + if optGenGuiApp in m.config.globalOptions: nimMain = WinNimMain otherMain = WinCMain else: @@ -1044,7 +1059,7 @@ proc genMainProc(m: BModule) = elif platform.targetOS == osGenode: nimMain = GenodeNimMain otherMain = ComponentConstruct - elif optGenDynLib in gGlobalOptions: + elif optGenDynLib in m.config.globalOptions: nimMain = PosixNimDllMain otherMain = PosixCDllMain elif platform.targetOS == osStandalone: @@ -1054,16 +1069,16 @@ proc genMainProc(m: BModule) = nimMain = PosixNimMain otherMain = PosixCMain if m.g.breakpoints != nil: discard cgsym(m, "dbgRegisterBreakpoint") - if optEndb in gOptions: + if optEndb in m.config.options: m.g.breakpoints.add(m.genFilenames) let initStackBottomCall = - if platform.targetOS == osStandalone or gSelectedGC == gcNone: "".rope + if platform.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N") inc(m.labels) appcg(m, m.s[cfsProcs], PreMainBody, [ m.g.mainDatInit, m.g.breakpoints, m.g.otherModsInit, - if emulatedThreadVars() and platform.targetOS != osStandalone: + if emulatedThreadVars(m.config) and platform.targetOS != osStandalone: ropecg(m, "\t#initThreadVarsEmulation();$N") else: "".rope, @@ -1071,8 +1086,12 @@ proc genMainProc(m: BModule) = appcg(m, m.s[cfsProcs], nimMain, [m.g.mainModInit, initStackBottomCall, rope(m.labels)]) - if optNoMain notin gGlobalOptions: + if optNoMain notin m.config.globalOptions: + if optUseNimNamespace in m.config.globalOptions: + m.s[cfsProcs].add closeNamespaceNim() & "using namespace Nim;" & tnl + appcg(m, m.s[cfsProcs], otherMain, []) + if optUseNimNamespace in m.config.globalOptions: m.s[cfsProcs].add openNamespaceNim() proc getSomeInitName(m: PSym, suffix: string): Rope = assert m.kind == skModule @@ -1080,7 +1099,7 @@ proc getSomeInitName(m: PSym, suffix: string): Rope = if {sfSystemModule, sfMainModule} * m.flags == {}: result = m.owner.name.s.mangle.rope result.add "_" - result.add m.name.s + result.add m.name.s.mangle result.add suffix proc getInitName(m: PSym): Rope = @@ -1096,8 +1115,8 @@ proc registerModuleToMain(g: BModuleList; m: PSym) = var init = m.getInitName datInit = m.getDatInitName - addf(g.mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", [init]) - addf(g.mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", [datInit]) + addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init]) + addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit]) if sfSystemModule notin m.flags: addf(g.mainDatInit, "\t$1();$N", [datInit]) let initCall = "\t$1();$N" % [init] @@ -1108,7 +1127,7 @@ proc registerModuleToMain(g: BModuleList; m: PSym) = proc genInitCode(m: BModule) = var initname = getInitName(m.module) - var prc = "NIM_EXTERNC N_NOINLINE(void, $1)(void) {$N" % [initname] + var prc = "N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N" % [initname] if m.typeNodes > 0: appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n", [m.typeNodesName, rope(m.typeNodes)]) @@ -1118,11 +1137,11 @@ proc genInitCode(m: BModule) = add(prc, initGCFrame(m.initProc)) - add(prc, genSectionStart(cpsLocals)) + add(prc, genSectionStart(cpsLocals, m.config)) add(prc, m.preInitProc.s(cpsLocals)) add(prc, m.initProc.s(cpsLocals)) add(prc, m.postInitProc.s(cpsLocals)) - add(prc, genSectionEnd(cpsLocals)) + add(prc, genSectionEnd(cpsLocals, m.config)) if optStackTrace in m.initProc.options and frameDeclared notin m.flags: # BUT: the generated init code might depend on a current frame, so @@ -1130,33 +1149,33 @@ proc genInitCode(m: BModule) = incl m.flags, frameDeclared if preventStackTrace notin m.flags: var procname = makeCString(m.module.name.s) - add(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename)) + add(prc, initFrame(m.initProc, procname, quotedFilename(m.config, m.module.info))) else: add(prc, ~"\tTFrame FR_; FR_.len = 0;$N") - add(prc, genSectionStart(cpsInit)) + add(prc, genSectionStart(cpsInit, m.config)) add(prc, m.preInitProc.s(cpsInit)) add(prc, m.initProc.s(cpsInit)) add(prc, m.postInitProc.s(cpsInit)) - add(prc, genSectionEnd(cpsInit)) + add(prc, genSectionEnd(cpsInit, m.config)) - add(prc, genSectionStart(cpsStmts)) + add(prc, genSectionStart(cpsStmts, m.config)) add(prc, m.preInitProc.s(cpsStmts)) add(prc, m.initProc.s(cpsStmts)) add(prc, m.postInitProc.s(cpsStmts)) - add(prc, genSectionEnd(cpsStmts)) + add(prc, genSectionEnd(cpsStmts, m.config)) if optStackTrace in m.initProc.options and preventStackTrace notin m.flags: add(prc, deinitFrame(m.initProc)) add(prc, deinitGCFrame(m.initProc)) addf(prc, "}$N$N", []) - prc.addf("NIM_EXTERNC N_NOINLINE(void, $1)(void) {$N", + prc.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N", [getDatInitName(m.module)]) for i in cfsTypeInit1..cfsDynLibInit: - add(prc, genSectionStart(i)) + add(prc, genSectionStart(i, m.config)) add(prc, m.s[i]) - add(prc, genSectionEnd(i)) + add(prc, genSectionEnd(i, m.config)) addf(prc, "}$N$N", []) # we cannot simply add the init proc to ``m.s[cfsProcs]`` anymore because @@ -1171,16 +1190,19 @@ proc genInitCode(m: BModule) = add(m.s[cfsInitProc], ex) proc genModule(m: BModule, cfile: Cfile): Rope = - result = getFileHeader(cfile) + result = getFileHeader(m.config, cfile) result.add(genMergeInfo(m)) generateThreadLocalStorage(m) generateHeaders(m) for i in countup(cfsHeaders, cfsProcs): - add(result, genSectionStart(i)) + add(result, genSectionStart(i, m.config)) add(result, m.s[i]) - add(result, genSectionEnd(i)) + add(result, genSectionEnd(i, m.config)) + if optUseNimNamespace in m.config.globalOptions and i == cfsHeaders: + result.add openNamespaceNim() add(result, m.s[cfsInitProc]) + if optUseNimNamespace in m.config.globalOptions: result.add closeNamespaceNim() proc newPreInitProc(m: BModule): BProc = result = newProc(nil, m) @@ -1193,10 +1215,12 @@ proc newPostInitProc(m: BModule): BProc = result.labels = 200_000 proc initProcOptions(m: BModule): TOptions = - if sfSystemModule in m.module.flags: gOptions-{optStackTrace} else: gOptions + let opts = m.config.options + if sfSystemModule in m.module.flags: opts-{optStackTrace} else: opts proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule = new(result) + result.g = g result.tmpBase = rope("TM" & $hashOwner(module) & "_") result.headerFiles = @[] result.declaredThings = initIntSet() @@ -1217,14 +1241,13 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule = result.forwardedProcs = @[] result.typeNodesName = getTempName(result) result.nimTypesName = getTempName(result) - result.g = g # no line tracing for the init sections of the system module so that we # don't generate a TFrame which can confuse the stack botton initialization: if sfSystemModule in module.flags: incl result.flags, preventStackTrace excl(result.preInitProc.options, optStackTrace) excl(result.postInitProc.options, optStackTrace) - let ndiName = if optCDebug in gGlobalOptions: changeFileExt(completeCFilePath(filename), "ndi") + let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCFilePath(g.config, filename), "ndi") else: "" open(result.ndi, ndiName) @@ -1277,7 +1300,7 @@ proc resetCgenModules*(g: BModuleList) = for m in cgenModules(g): resetModule(m) proc rawNewModule(g: BModuleList; module: PSym): BModule = - result = rawNewModule(g, module, module.position.int32.toFullPath) + result = rawNewModule(g, module, module.position.FileIndex.toFullPath) proc newModule(g: BModuleList; module: PSym): BModule = # we should create only one cgen module for each module sym @@ -1285,22 +1308,19 @@ proc newModule(g: BModuleList; module: PSym): BModule = growCache g.modules, module.position g.modules[module.position] = result - if (optDeadCodeElim in gGlobalOptions): - if (sfDeadCodeElim in module.flags): - internalError("added pending module twice: " & module.filename) - -template injectG(config) {.dirty.} = +template injectG() {.dirty.} = if graph.backend == nil: - graph.backend = newModuleList(config) + graph.backend = newModuleList(graph) let g = BModuleList(graph.backend) proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = - injectG(graph.config) + injectG() result = newModule(g, module) - if optGenIndex in gGlobalOptions and g.generatedHeader == nil: - let f = if graph.config.headerFile.len > 0: graph.config.headerFile else: gProjectFull + if optGenIndex in graph.config.globalOptions and g.generatedHeader == nil: + let f = if graph.config.headerFile.len > 0: graph.config.headerFile + else: graph.config.projectFull g.generatedHeader = rawNewModule(g, module, - changeFileExt(completeCFilePath(f), hExt)) + changeFileExt(completeCFilePath(graph.config, f), hExt)) incl g.generatedHeader.flags, isHeaderFile proc writeHeader(m: BModule) = @@ -1311,41 +1331,44 @@ proc writeHeader(m: BModule) = var guard = "__$1__" % [m.filename.splitFile.name.rope] result.addf("#ifndef $1$n#define $1$n", [guard]) - addIntTypes(result) + addIntTypes(result, m.config) generateHeaders(m) generateThreadLocalStorage(m) for i in countup(cfsHeaders, cfsProcs): - add(result, genSectionStart(i)) + add(result, genSectionStart(i, m.config)) add(result, m.s[i]) - add(result, genSectionEnd(i)) + add(result, genSectionEnd(i, m.config)) + if optUseNimNamespace in m.config.globalOptions and i == cfsHeaders: result.add openNamespaceNim() add(result, m.s[cfsInitProc]) - if optGenDynLib in gGlobalOptions: + if optGenDynLib in m.config.globalOptions: result.add("N_LIB_IMPORT ") result.addf("N_CDECL(void, NimMain)(void);$n", []) + if optUseNimNamespace in m.config.globalOptions: result.add closeNamespaceNim() result.addf("#endif /* $1 */$n", [guard]) writeRope(result, m.filename) proc getCFile(m: BModule): string = let ext = if m.compileToCpp: ".cpp" - elif gCmd == cmdCompileToOC or sfCompileToObjC in m.module.flags: ".m" + elif m.config.cmd == cmdCompileToOC or sfCompileToObjC in m.module.flags: ".m" else: ".c" - result = changeFileExt(completeCFilePath(m.cfilename.withPackageName), ext) + result = changeFileExt(completeCFilePath(m.config, withPackageName(m.config, m.cfilename)), ext) proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext = - injectG(graph.config) + injectG() var m = newModule(g, module) readMergeInfo(getCFile(m), m) result = m proc myProcess(b: PPassContext, n: PNode): PNode = result = n - if b == nil or passes.skipCodegen(n): return + if b == nil: return var m = BModule(b) + if passes.skipCodegen(m.config, n): return m.initProc.options = initProcOptions(m) - softRnl = if optLineDir in gOptions: noRnl else: rnl + softRnl = if optLineDir in m.config.options: noRnl else: rnl genStmts(m.initProc, n) proc finishModule(m: BModule) = @@ -1355,18 +1378,18 @@ proc finishModule(m: BModule) = # a ``for`` loop here var prc = m.forwardedProcs[i] if sfForward in prc.flags: - internalError(prc.info, "still forwarded: " & prc.name.s) + internalError(m.config, prc.info, "still forwarded: " & prc.name.s) genProcNoForward(m, prc) inc(i) assert(m.g.forwardedProcsCounter >= i) dec(m.g.forwardedProcsCounter, i) setLen(m.forwardedProcs, 0) -proc shouldRecompile(code: Rope, cfile: Cfile): bool = +proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool = result = true - if optForceFullMake notin gGlobalOptions: + if optForceFullMake notin m.config.globalOptions: if not equalsFile(code, cfile.cname): - if isDefined("nimdiff"): + if isDefined(m.config, "nimdiff"): if fileExists(cfile.cname): copyFile(cfile.cname, cfile.cname & ".backup") echo "diff ", cfile.cname, ".backup ", cfile.cname @@ -1389,7 +1412,7 @@ proc writeModule(m: BModule, pending: bool) = # generate code for the init statements of the module: let cfile = getCFile(m) - if m.rd == nil or optForceFullMake in gGlobalOptions: + if m.rd == nil or optForceFullMake in m.config.globalOptions: genInitCode(m) finishTypeDescriptions(m) if sfMainModule in m.module.flags: @@ -1397,35 +1420,35 @@ proc writeModule(m: BModule, pending: bool) = add(m.s[cfsProcHeaders], m.g.mainModProcs) generateThreadVarsSize(m) - var cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {}) + var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {}) var code = genModule(m, cf) when hasTinyCBackend: - if gCmd == cmdRun: + if conf.cmd == cmdRun: tccgen.compileCCode($code) return - if not shouldRecompile(code, cf): cf.flags = {CfileFlag.Cached} - addFileToCompile(cf) + if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached} + addFileToCompile(m.config, cf) elif pending and mergeRequired(m) and sfMainModule notin m.module.flags: - let cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {}) + let cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {}) mergeFiles(cfile, m) genInitCode(m) finishTypeDescriptions(m) var code = genModule(m, cf) writeRope(code, cfile) - addFileToCompile(cf) + addFileToCompile(m.config, cf) else: # Consider: first compilation compiles ``system.nim`` and produces # ``system.c`` but then compilation fails due to an error. This means # that ``system.o`` is missing, so we need to call the C compiler for it: - var cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {}) + var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {}) if not existsFile(cf.obj): cf.flags = {CfileFlag.Cached} - addFileToCompile(cf) + addFileToCompile(m.config, cf) close(m.ndi) proc updateCachedModule(m: BModule) = let cfile = getCFile(m) - var cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {}) + var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {}) if mergeRequired(m) and sfMainModule notin m.module.flags: mergeFiles(cfile, m) @@ -1436,12 +1459,13 @@ proc updateCachedModule(m: BModule) = writeRope(code, cfile) else: cf.flags = {CfileFlag.Cached} - addFileToCompile(cf) + addFileToCompile(m.config, cf) proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = result = n - if b == nil or passes.skipCodegen(n): return + if b == nil: return var m = BModule(b) + if passes.skipCodegen(m.config, n): return # if the module is cached, we don't regenerate the main proc # nor the dispatchers? But if the dispatchers changed? # XXX emit the dispatchers into its own .c file? @@ -1475,7 +1499,7 @@ proc cgenWriteModules*(backend: RootRef, config: ConfigRef) = m.updateCachedModule else: m.writeModule(pending=true) - writeMapping(g.mapping) + writeMapping(config, g.mapping) if g.generatedHeader != nil: writeHeader(g.generatedHeader) const cgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 0f8fa760e..ce3fc2f90 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -14,6 +14,7 @@ import tables, ndi from msgs import TLineInfo +from modulegraphs import ModuleGraph type TLabel* = Rope # for the C generator a label is just a rope @@ -70,11 +71,10 @@ type threadVarAccessed*: bool # true if the proc already accessed some threadvar lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements currLineInfo*: TLineInfo # AST codegen will make this superfluous - nestedTryStmts*: seq[PNode] # in how many nested try statements we are - # (the vars must be volatile then) - inExceptBlock*: int # are we currently inside an except block? - # leaving such scopes by raise or by return must - # execute any applicable finally blocks + nestedTryStmts*: seq[tuple[n: PNode, inExcept: bool]] + # in how many nested try statements we are + # (the vars must be volatile then) + # bool is true when are in the except part of a try block finallySafePoints*: seq[Rope] # For correctly cleaning up exceptions when # using return in finally statements labels*: Natural # for generating unique labels in the C proc @@ -117,6 +117,8 @@ type breakpoints*: Rope # later the breakpoints are inserted into the main proc typeInfoMarker*: TypeCache config*: ConfigRef + graph*: ModuleGraph + strVersion*, seqVersion*: int # version of the string/seq implementation to use TCGen = object of TPassContext # represents a C source file s*: TCFileSections # sections of the C file @@ -148,6 +150,9 @@ type g*: BModuleList ndi*: NdiFile +template config*(m: BModule): ConfigRef = m.g.config +template config*(p: BProc): ConfigRef = p.module.g.config + proc includeHeader*(this: BModule; header: string) = if not this.headerFiles.contains header: this.headerFiles.add header @@ -165,14 +170,15 @@ proc newProc*(prc: PSym, module: BModule): BProc = result.prc = prc result.module = module if prc != nil: result.options = prc.options - else: result.options = gOptions + else: result.options = module.config.options newSeq(result.blocks, 1) result.nestedTryStmts = @[] result.finallySafePoints = @[] result.sigConflicts = initCountTable[string]() -proc newModuleList*(config: ConfigRef): BModuleList = - BModuleList(modules: @[], typeInfoMarker: initTable[SigHash, Rope](), config: config) +proc newModuleList*(g: ModuleGraph): BModuleList = + BModuleList(modules: @[], typeInfoMarker: initTable[SigHash, Rope](), config: g.config, + graph: g) iterator cgenModules*(g: BModuleList): BModule = for i in 0..high(g.modules): diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index 6f7d9f489..1d72952e2 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -11,9 +11,9 @@ import intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys, - sempass2, strutils, modulegraphs + sempass2, strutils, modulegraphs, configuration -proc genConv(n: PNode, d: PType, downcast: bool): PNode = +proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode = var dest = skipTypes(d, abstractPtrs) var source = skipTypes(n.typ, abstractPtrs) if (source.kind == tyObject) and (dest.kind == tyObject): @@ -24,12 +24,12 @@ proc genConv(n: PNode, d: PType, downcast: bool): PNode = elif diff < 0: result = newNodeIT(nkObjUpConv, n.info, d) addSon(result, n) - if downcast: internalError(n.info, "cgmeth.genConv: no upcast allowed") + if downcast: internalError(conf, n.info, "cgmeth.genConv: no upcast allowed") elif diff > 0: result = newNodeIT(nkObjDownConv, n.info, d) addSon(result, n) if not downcast: - internalError(n.info, "cgmeth.genConv: no downcast allowed") + internalError(conf, n.info, "cgmeth.genConv: no downcast allowed") else: result = n else: @@ -42,7 +42,7 @@ proc getDispatcher*(s: PSym): PSym = let disp = dispn.sym if sfDispatcher in disp.flags: result = disp -proc methodCall*(n: PNode): PNode = +proc methodCall*(n: PNode; conf: ConfigRef): PNode = result = n # replace ordinary method by dispatcher method: let disp = getDispatcher(result.sons[0].sym) @@ -50,9 +50,9 @@ proc methodCall*(n: PNode): PNode = result.sons[0].sym = disp # change the arguments to up/downcasts to fit the dispatcher's parameters: for i in countup(1, sonsLen(result)-1): - result.sons[i] = genConv(result.sons[i], disp.typ.sons[i], true) + result.sons[i] = genConv(result.sons[i], disp.typ.sons[i], true, conf) else: - localError(n.info, "'" & $result.sons[0] & "' lacks a dispatcher") + localError(conf, n.info, "'" & $result.sons[0] & "' lacks a dispatcher") type MethodResult = enum No, Invalid, Yes @@ -68,7 +68,7 @@ proc sameMethodBucket(a, b: PSym): MethodResult = while true: aa = skipTypes(aa, {tyGenericInst, tyAlias}) bb = skipTypes(bb, {tyGenericInst, tyAlias}) - if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef}: + if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef, tyLent}: aa = aa.lastSon bb = bb.lastSon else: @@ -130,7 +130,7 @@ proc createDispatcher(s: PSym): PSym = attachDispatcher(disp, newSymNode(disp)) return disp -proc fixupDispatcher(meth, disp: PSym) = +proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) = # We may have constructed the dispatcher from a method prototype # and need to augment the incomplete dispatcher with information # from later definitions, particularly the resultPos slot. Also, @@ -149,7 +149,7 @@ proc fixupDispatcher(meth, disp: PSym) = disp.typ.lockLevel = meth.typ.lockLevel elif meth.typ.lockLevel != UnspecifiedLockLevel and meth.typ.lockLevel != disp.typ.lockLevel: - message(meth.info, warnLockLevel, + message(conf, meth.info, warnLockLevel, "method has lock level $1, but another method has $2" % [$meth.typ.lockLevel, $disp.typ.lockLevel]) # XXX The following code silences a duplicate warning in @@ -166,13 +166,13 @@ proc methodDef*(g: ModuleGraph; s: PSym, fromCache: bool) = of Yes: add(g.methods[i].methods, s) attachDispatcher(s, lastSon(disp.ast)) - fixupDispatcher(s, disp) + fixupDispatcher(s, disp, g.config) #echo "fixup ", disp.name.s, " ", disp.id - when useEffectSystem: checkMethodEffects(disp, s) + when useEffectSystem: checkMethodEffects(g, disp, s) if {sfBase, sfFromGeneric} * s.flags == {sfBase} and g.methods[i].methods[0] != s: # already exists due to forwarding definition? - localError(s.info, "method is not a base") + localError(g.config, s.info, "method is not a base") return of No: discard of Invalid: @@ -183,10 +183,10 @@ proc methodDef*(g: ModuleGraph; s: PSym, fromCache: bool) = #if fromCache: # internalError(s.info, "no method dispatcher found") if witness != nil: - localError(s.info, "invalid declaration order; cannot attach '" & s.name.s & + localError(g.config, s.info, "invalid declaration order; cannot attach '" & s.name.s & "' to method defined here: " & $witness.info) elif sfBase notin s.flags: - message(s.info, warnUseBase) + message(g.config, s.info, warnUseBase) proc relevantCol(methods: TSymSeq, col: int): bool = # returns true iff the position is relevant @@ -225,32 +225,33 @@ proc sortBucket(a: var TSymSeq, relevantCols: IntSet) = a[j] = v if h == 1: break -proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym = +proc genDispatcher(g: ModuleGraph; methods: TSymSeq, relevantCols: IntSet): PSym = var base = lastSon(methods[0].ast).sym result = base var paramLen = sonsLen(base.typ) var nilchecks = newNodeI(nkStmtList, base.info) var disp = newNodeI(nkIfStmt, base.info) - var ands = getSysSym("and") - var iss = getSysSym("of") + var ands = getSysSym(g, unknownLineInfo(), "and") + var iss = getSysSym(g, unknownLineInfo(), "of") + let boolType = getSysType(g, unknownLineInfo(), tyBool) for col in countup(1, paramLen - 1): if contains(relevantCols, col): let param = base.typ.n.sons[col].sym if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}: addSon(nilchecks, newTree(nkCall, - newSymNode(getCompilerProc"chckNilDisp"), newSymNode(param))) + newSymNode(getCompilerProc(g, "chckNilDisp")), newSymNode(param))) for meth in countup(0, high(methods)): var curr = methods[meth] # generate condition: var cond: PNode = nil for col in countup(1, paramLen - 1): if contains(relevantCols, col): - var isn = newNodeIT(nkCall, base.info, getSysType(tyBool)) + var isn = newNodeIT(nkCall, base.info, boolType) addSon(isn, newSymNode(iss)) let param = base.typ.n.sons[col].sym addSon(isn, newSymNode(param)) addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col])) if cond != nil: - var a = newNodeIT(nkCall, base.info, getSysType(tyBool)) + var a = newNodeIT(nkCall, base.info, boolType) addSon(a, newSymNode(ands)) addSon(a, cond) addSon(a, isn) @@ -262,7 +263,7 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym = addSon(call, newSymNode(curr)) for col in countup(1, paramLen - 1): addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym), - curr.typ.sons[col], false)) + curr.typ.sons[col], false, g.config)) var ret: PNode if retTyp != nil: var a = newNodeI(nkFastAsgn, base.info) @@ -290,4 +291,4 @@ proc generateMethodDispatchers*(g: ModuleGraph): PNode = if relevantCol(g.methods[bucket].methods, col): incl(relevantCols, col) sortBucket(g.methods[bucket].methods, relevantCols) addSon(result, - newSymNode(genDispatcher(g.methods[bucket].methods, relevantCols))) + newSymNode(genDispatcher(g, g.methods[bucket].methods, relevantCols))) diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim new file mode 100644 index 000000000..86b63e34b --- /dev/null +++ b/compiler/closureiters.nim @@ -0,0 +1,1287 @@ +# +# +# The Nim Compiler +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# This file implements closure iterator transformations. +# The main idea is to split the closure iterator body to top level statements. +# The body is split by yield statement. +# +# Example: +# while a > 0: +# echo "hi" +# yield a +# dec a +# +# Should be transformed to: +# STATE0: +# if a > 0: +# echo "hi" +# :state = 1 # Next state +# return a # yield +# else: +# :state = 2 # Next state +# break :stateLoop # Proceed to the next state +# STATE1: +# dec a +# :state = 0 # Next state +# break :stateLoop # Proceed to the next state +# STATE2: +# :state = -1 # End of execution + +# The transformation should play well with lambdalifting, however depending +# on situation, it can be called either before or after lambdalifting +# transformation. As such we behave slightly differently, when accessing +# iterator state, or using temp variables. If lambdalifting did not happen, +# we just create local variables, so that they will be lifted further on. +# Otherwise, we utilize existing env, created by lambdalifting. + +# Lambdalifting treats :state variable specially, it should always end up +# as the first field in env. Currently C codegen depends on this behavior. + +# One special subtransformation is nkStmtListExpr lowering. +# Example: +# template foo(): int = +# yield 1 +# 2 +# +# iterator it(): int {.closure.} = +# if foo() == 2: +# yield 3 +# +# If a nkStmtListExpr has yield inside, it has first to be lowered to: +# yield 1 +# :tmpSlLower = 2 +# if :tmpSlLower == 2: +# yield 3 + +# nkTryStmt Transformations: +# If the iter has an nkTryStmt with a yield inside +# - the closure iter is promoted to have exceptions (ctx.hasExceptions = true) +# - exception table is created. This is a const array, where +# `abs(exceptionTable[i])` is a state idx to which we should jump from state +# `i` should exception be raised in state `i`. For all states in `try` block +# the target state is `except` block. For all states in `except` block +# the target state is `finally` block. For all other states there is no +# target state (0, as the first block can never be neither except nor finally). +# `exceptionTable[i]` is < 0 if `abs(exceptionTable[i])` is except block, +# and > 0, for finally block. +# - local variable :curExc is created +# - the iter body is wrapped into a +# try: +# closureIterSetupExc(:curExc) +# ...body... +# catch: +# :state = exceptionTable[:state] +# if :state == 0: raise # No state that could handle exception +# :unrollFinally = :state > 0 # Target state is finally +# if :state < 0: +# :state = -:state +# :curExc = getCurrentException() +# +# nkReturnStmt within a try/except/finally now has to behave differently as we +# want the nearest finally block to be executed before the return, thus it is +# transformed to: +# :tmpResult = returnValue (if return doesn't have a value, this is skipped) +# :unrollFinally = true +# goto nearestFinally (or -1 if not exists) +# +# Every finally block calls closureIterEndFinally() upon its successful +# completion. +# +# Example: +# +# try: +# yield 0 +# raise ... +# except: +# yield 1 +# return 3 +# finally: +# yield 2 +# +# Is transformed to (yields are left in place for example simplicity, +# in reality the code is subdivided even more, as described above): +# +# STATE0: # Try +# yield 0 +# raise ... +# :state = 2 # What would happen should we not raise +# break :stateLoop +# STATE1: # Except +# yield 1 +# :tmpResult = 3 # Return +# :unrollFinally = true # Return +# :state = 2 # Goto Finally +# break :stateLoop +# :state = 2 # What would happen should we not return +# break :stateLoop +# STATE2: # Finally +# yield 2 +# if :unrollFinally: # This node is created by `newEndFinallyNode` +# if :curExc.isNil: +# return :tmpResult +# else: +# raise +# state = -1 # Goto next state. In this case we just exit +# break :stateLoop + +import + intsets, strutils, options, ast, astalgo, trees, treetab, msgs, idents, + renderer, types, magicsys, rodread, lowerings, lambdalifting, modulegraphs + +type + Ctx = object + g: ModuleGraph + fn: PSym + stateVarSym: PSym # :state variable. nil if env already introduced by lambdalifting + tmpResultSym: PSym # Used when we return, but finally has to interfere + unrollFinallySym: PSym # Indicates that we're unrolling finally states (either exception happened or premature return) + curExcSym: PSym # Current exception + + states: seq[PNode] # The resulting states. Every state is an nkState node. + blockLevel: int # Temp used to transform break and continue stmts + stateLoopLabel: PSym # Label to break on, when jumping between states. + exitStateIdx: int # index of the last state + tempVarId: int # unique name counter + tempVars: PNode # Temp var decls, nkVarSection + exceptionTable: seq[int] # For state `i` jump to state `exceptionTable[i]` if exception is raised + hasExceptions: bool # Does closure have yield in try? + curExcHandlingState: int # Negative for except, positive for finally + nearestFinally: int # Index of the nearest finally block. For try/except it + # is their finally. For finally it is parent finally. Otherwise -1 + +proc newStateAccess(ctx: var Ctx): PNode = + if ctx.stateVarSym.isNil: + result = rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)), + getStateField(ctx.g, ctx.fn), ctx.fn.info) + else: + result = newSymNode(ctx.stateVarSym) + +proc newStateAssgn(ctx: var Ctx, toValue: PNode): PNode = + # Creates state assignment: + # :state = toValue + newTree(nkAsgn, ctx.newStateAccess(), toValue) + +proc newStateAssgn(ctx: var Ctx, stateNo: int = -2): PNode = + # Creates state assignment: + # :state = stateNo + ctx.newStateAssgn(newIntTypeNode(nkIntLit, stateNo, ctx.g.getSysType(TLineInfo(), tyInt))) + +proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym = + result = newSym(skVar, getIdent(name), ctx.fn, ctx.fn.info) + result.typ = typ + assert(not typ.isNil) + + if not ctx.stateVarSym.isNil: + # We haven't gone through labmda lifting yet, so just create a local var, + # it will be lifted later + if ctx.tempVars.isNil: + ctx.tempVars = newNodeI(nkVarSection, ctx.fn.info) + addVar(ctx.tempVars, newSymNode(result)) + else: + let envParam = getEnvParam(ctx.fn) + # let obj = envParam.typ.lastSon + result = addUniqueField(envParam.typ.lastSon, result) + +proc newEnvVarAccess(ctx: Ctx, s: PSym): PNode = + if ctx.stateVarSym.isNil: + result = rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)), s, ctx.fn.info) + else: + result = newSymNode(s) + +proc newTmpResultAccess(ctx: var Ctx): PNode = + if ctx.tmpResultSym.isNil: + ctx.tmpResultSym = ctx.newEnvVar(":tmpResult", ctx.fn.typ[0]) + ctx.newEnvVarAccess(ctx.tmpResultSym) + +proc newUnrollFinallyAccess(ctx: var Ctx, info: TLineInfo): PNode = + if ctx.unrollFinallySym.isNil: + ctx.unrollFinallySym = ctx.newEnvVar(":unrollFinally", ctx.g.getSysType(info, tyBool)) + ctx.newEnvVarAccess(ctx.unrollFinallySym) + +proc newCurExcAccess(ctx: var Ctx): PNode = + if ctx.curExcSym.isNil: + ctx.curExcSym = ctx.newEnvVar(":curExc", ctx.g.callCodegenProc("getCurrentException", emptyNode).typ) + ctx.newEnvVarAccess(ctx.curExcSym) + +proc newState(ctx: var Ctx, n, gotoOut: PNode): int = + # Creates a new state, adds it to the context fills out `gotoOut` so that it + # will goto this state. + # Returns index of the newly created state + + result = ctx.states.len + let resLit = ctx.g.newIntLit(n.info, result) + let s = newNodeI(nkState, n.info) + s.add(resLit) + s.add(n) + ctx.states.add(s) + ctx.exceptionTable.add(ctx.curExcHandlingState) + + if not gotoOut.isNil: + assert(gotoOut.len == 0) + gotoOut.add(ctx.g.newIntLit(gotoOut.info, result)) + +proc toStmtList(n: PNode): PNode = + result = n + if result.kind notin {nkStmtList, nkStmtListExpr}: + result = newNodeI(nkStmtList, n.info) + result.add(n) + +proc addGotoOut(n: PNode, gotoOut: PNode): PNode = + # Make sure `n` is a stmtlist, and ends with `gotoOut` + result = toStmtList(n) + if result.len != 0 and result.sons[^1].kind != nkGotoState: + result.add(gotoOut) + +proc newTempVar(ctx: var Ctx, typ: PType): PSym = + result = ctx.newEnvVar(":tmpSlLower" & $ctx.tempVarId, typ) + inc ctx.tempVarId + +proc hasYields(n: PNode): bool = + # TODO: This is very inefficient. It traverses the node, looking for nkYieldStmt. + case n.kind + of nkYieldStmt: + result = true + of nkCharLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkStrLit..nkTripleStrLit, + nkSym, nkIdent, procDefs, nkTemplateDef: + discard + else: + for c in n: + if c.hasYields: + result = true + break + +proc transformBreaksAndContinuesInWhile(ctx: var Ctx, n: PNode, before, after: PNode): PNode = + result = n + case n.kind + of nkCharLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkStrLit..nkTripleStrLit, + nkSym, nkIdent, procDefs, nkTemplateDef: + discard + of nkWhileStmt: discard # Do not recurse into nested whiles + of nkContinueStmt: + result = before + of nkBlockStmt: + inc ctx.blockLevel + result[1] = ctx.transformBreaksAndContinuesInWhile(result[1], before, after) + dec ctx.blockLevel + of nkBreakStmt: + if ctx.blockLevel == 0: + result = after + else: + for i in 0 ..< n.len: + n[i] = ctx.transformBreaksAndContinuesInWhile(n[i], before, after) + +proc transformBreaksInBlock(ctx: var Ctx, n: PNode, label, after: PNode): PNode = + result = n + case n.kind + of nkCharLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkStrLit..nkTripleStrLit, + nkSym, nkIdent, procDefs, nkTemplateDef: + discard + of nkBlockStmt, nkWhileStmt: + inc ctx.blockLevel + result[1] = ctx.transformBreaksInBlock(result[1], label, after) + dec ctx.blockLevel + of nkBreakStmt: + if n[0].kind == nkEmpty: + if ctx.blockLevel == 0: + result = after + else: + if label.kind == nkSym and n[0].sym == label.sym: + result = after + else: + for i in 0 ..< n.len: + n[i] = ctx.transformBreaksInBlock(n[i], label, after) + +proc newNullifyCurExc(ctx: var Ctx, info: TLineInfo): PNode = + # :curEcx = nil + let curExc = ctx.newCurExcAccess() + curExc.info = info + let nilnode = newNode(nkNilLit) + nilnode.typ = curExc.typ + result = newTree(nkAsgn, curExc, nilnode) + +proc newOr(g: ModuleGraph, a, b: PNode): PNode {.inline.} = + result = newTree(nkCall, newSymNode(g.getSysMagic(a.info, "or", mOr)), a, b) + result.typ = g.getSysType(a.info, tyBool) + result.info = a.info + +proc collectExceptState(ctx: var Ctx, n: PNode): PNode {.inline.} = + var ifStmt = newNodeI(nkIfStmt, n.info) + let g = ctx.g + for c in n: + if c.kind == nkExceptBranch: + var ifBranch: PNode + + if c.len > 1: + var cond: PNode + for i in 0 .. c.len - 2: + assert(c[i].kind == nkType) + let nextCond = newTree(nkCall, + newSymNode(g.getSysMagic(c.info, "of", mOf)), + g.callCodegenProc("getCurrentException", emptyNode), + c[i]) + nextCond.typ = ctx.g.getSysType(c.info, tyBool) + nextCond.info = c.info + + if cond.isNil: + cond = nextCond + else: + cond = g.newOr(cond, nextCond) + + ifBranch = newNodeI(nkElifBranch, c.info) + ifBranch.add(cond) + else: + if ifStmt.len == 0: + ifStmt = newNodeI(nkStmtList, c.info) + ifBranch = newNodeI(nkStmtList, c.info) + else: + ifBranch = newNodeI(nkElse, c.info) + + ifBranch.add(c[^1]) + ifStmt.add(ifBranch) + + if ifStmt.len != 0: + result = newTree(nkStmtList, ctx.newNullifyCurExc(n.info), ifStmt) + else: + result = emptyNode + +proc addElseToExcept(ctx: var Ctx, n: PNode) = + if n.kind == nkStmtList and n[1].kind == nkIfStmt and n[1][^1].kind != nkElse: + # Not all cases are covered + let branchBody = newNodeI(nkStmtList, n.info) + + block: # :unrollFinally = true + branchBody.add(newTree(nkAsgn, + ctx.newUnrollFinallyAccess(n.info), + newIntTypeNode(nkIntLit, 1, ctx.g.getSysType(n.info, tyBool)))) + + block: # :curExc = getCurrentException() + branchBody.add(newTree(nkAsgn, + ctx.newCurExcAccess(), + ctx.g.callCodegenProc("getCurrentException", emptyNode))) + + block: # goto nearestFinally + branchBody.add(newTree(nkGotoState, ctx.g.newIntLit(n.info, ctx.nearestFinally))) + + let elseBranch = newTree(nkElse, branchBody) + n[1].add(elseBranch) + +proc getFinallyNode(n: PNode): PNode = + result = n[^1] + if result.kind == nkFinally: + result = result[0] + else: + result = emptyNode + +proc hasYieldsInExpressions(n: PNode): bool = + case n.kind + of nkCharLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkStrLit..nkTripleStrLit, + nkSym, nkIdent, procDefs, nkTemplateDef: + discard + of nkStmtListExpr: + if isEmptyType(n.typ): + for c in n: + if c.hasYieldsInExpressions: + return true + else: + result = n.hasYields + else: + for c in n: + if c.hasYieldsInExpressions: + return true + +proc exprToStmtList(n: PNode): tuple[s, res: PNode] = + assert(n.kind == nkStmtListExpr) + + var parent = n + var lastSon = n[^1] + + while lastSon.kind == nkStmtListExpr: + parent = lastSon + lastSon = lastSon[^1] + + result.s = newNodeI(nkStmtList, n.info) + result.s.sons = parent.sons + result.s.sons.setLen(result.s.sons.len - 1) # delete last son + result.res = lastSon + +proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode = + result = newTree(nkFastAsgn, ctx.newEnvVarAccess(s), v) + result.info = v.info + +proc addExprAssgn(ctx: Ctx, output, input: PNode, sym: PSym) = + if input.kind == nkStmtListExpr: + let (st, res) = exprToStmtList(input) + output.add(st) + output.add(ctx.newEnvVarAsgn(sym, res)) + else: + output.add(ctx.newEnvVarAsgn(sym, input)) + +proc convertExprBodyToAsgn(ctx: Ctx, exprBody: PNode, res: PSym): PNode = + result = newNodeI(nkStmtList, exprBody.info) + ctx.addExprAssgn(result, exprBody, res) + +proc newNotCall(g: ModuleGraph; e: PNode): PNode = + result = newTree(nkCall, newSymNode(g.getSysMagic(e.info, "not", mNot), e.info), e) + result.typ = g.getSysType(e.info, tyBool) + +proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = + result = n + case n.kind + of nkCharLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkStrLit..nkTripleStrLit, + nkSym, nkIdent, procDefs, nkTemplateDef: + discard + + of nkYieldStmt: + var ns = false + for i in 0 ..< n.len: + n[i] = ctx.lowerStmtListExprs(n[i], ns) + + if ns: + assert(n[0].kind == nkStmtListExpr) + result = newNodeI(nkStmtList, n.info) + let (st, ex) = exprToStmtList(n[0]) + result.add(st) + n[0] = ex + result.add(n) + + needsSplit = true + + of nkPar, nkObjConstr, nkTupleConstr, nkBracket: + var ns = false + for i in 0 ..< n.len: + n[i] = ctx.lowerStmtListExprs(n[i], ns) + + if ns: + needsSplit = true + + result = newNodeI(nkStmtListExpr, n.info) + if n.typ.isNil: internalError(ctx.g.config, "lowerStmtListExprs: constr typ.isNil") + result.typ = n.typ + + for i in 0 ..< n.len: + if n[i].kind == nkStmtListExpr: + let (st, ex) = exprToStmtList(n[i]) + result.add(st) + n[i] = ex + result.add(n) + + of nkIfStmt, nkIfExpr: + var ns = false + for i in 0 ..< n.len: + n[i] = ctx.lowerStmtListExprs(n[i], ns) + + if ns: + needsSplit = true + var tmp: PSym + var s: PNode + let isExpr = not isEmptyType(n.typ) + if isExpr: + tmp = ctx.newTempVar(n.typ) + result = newNodeI(nkStmtListExpr, n.info) + result.typ = n.typ + else: + result = newNodeI(nkStmtList, n.info) + + var curS = result + + for branch in n: + case branch.kind + of nkElseExpr, nkElse: + if isExpr: + let branchBody = newNodeI(nkStmtList, branch.info) + ctx.addExprAssgn(branchBody, branch[0], tmp) + let newBranch = newTree(nkElse, branchBody) + curS.add(newBranch) + else: + curS.add(branch) + + of nkElifExpr, nkElifBranch: + var newBranch: PNode + if branch[0].kind == nkStmtListExpr: + let (st, res) = exprToStmtList(branch[0]) + let elseBody = newTree(nkStmtList, st) + + newBranch = newTree(nkElifBranch, res, branch[1]) + + let newIf = newTree(nkIfStmt, newBranch) + elseBody.add(newIf) + if curS.kind == nkIfStmt: + let newElse = newNodeI(nkElse, branch.info) + newElse.add(elseBody) + curS.add(newElse) + else: + curS.add(elseBody) + curS = newIf + else: + newBranch = branch + if curS.kind == nkIfStmt: + curS.add(newBranch) + else: + let newIf = newTree(nkIfStmt, newBranch) + curS.add(newIf) + curS = newIf + + if isExpr: + let branchBody = newNodeI(nkStmtList, branch[1].info) + ctx.addExprAssgn(branchBody, branch[1], tmp) + newBranch[1] = branchBody + + else: + internalError(ctx.g.config, "lowerStmtListExpr(nkIf): " & $branch.kind) + + if isExpr: result.add(ctx.newEnvVarAccess(tmp)) + + of nkTryStmt: + var ns = false + for i in 0 ..< n.len: + n[i] = ctx.lowerStmtListExprs(n[i], ns) + + if ns: + needsSplit = true + let isExpr = not isEmptyType(n.typ) + + if isExpr: + result = newNodeI(nkStmtListExpr, n.info) + result.typ = n.typ + let tmp = ctx.newTempVar(n.typ) + + n[0] = ctx.convertExprBodyToAsgn(n[0], tmp) + for i in 1 ..< n.len: + let branch = n[i] + case branch.kind + of nkExceptBranch: + if branch[0].kind == nkType: + branch[1] = ctx.convertExprBodyToAsgn(branch[1], tmp) + else: + branch[0] = ctx.convertExprBodyToAsgn(branch[0], tmp) + of nkFinally: + discard + else: + internalError(ctx.g.config, "lowerStmtListExpr(nkTryStmt): " & $branch.kind) + result.add(n) + result.add(ctx.newEnvVarAccess(tmp)) + + of nkCaseStmt: + var ns = false + for i in 0 ..< n.len: + n[i] = ctx.lowerStmtListExprs(n[i], ns) + + if ns: + needsSplit = true + + let isExpr = not isEmptyType(n.typ) + + if isExpr: + let tmp = ctx.newTempVar(n.typ) + result = newNodeI(nkStmtListExpr, n.info) + result.typ = n.typ + + if n[0].kind == nkStmtListExpr: + let (st, ex) = exprToStmtList(n[0]) + result.add(st) + n[0] = ex + + for i in 1 ..< n.len: + let branch = n[i] + case branch.kind + of nkOfBranch: + branch[1] = ctx.convertExprBodyToAsgn(branch[1], tmp) + of nkElse: + branch[0] = ctx.convertExprBodyToAsgn(branch[0], tmp) + else: + internalError(ctx.g.config, "lowerStmtListExpr(nkCaseStmt): " & $branch.kind) + result.add(n) + result.add(ctx.newEnvVarAccess(tmp)) + + of nkCallKinds: + var ns = false + for i in 0 ..< n.len: + n[i] = ctx.lowerStmtListExprs(n[i], ns) + + if ns: + needsSplit = true + let isExpr = not isEmptyType(n.typ) + + if isExpr: + result = newNodeI(nkStmtListExpr, n.info) + result.typ = n.typ + else: + result = newNodeI(nkStmtList, n.info) + + if n[0].kind == nkSym and n[0].sym.magic in {mAnd, mOr}: # `and`/`or` short cirquiting + var cond = n[1] + if cond.kind == nkStmtListExpr: + let (st, ex) = exprToStmtList(cond) + result.add(st) + cond = ex + + let tmp = ctx.newTempVar(cond.typ) + result.add(ctx.newEnvVarAsgn(tmp, cond)) + + var check = ctx.newEnvVarAccess(tmp) + if n[0].sym.magic == mOr: + check = ctx.g.newNotCall(check) + + cond = n[2] + let ifBody = newNodeI(nkStmtList, cond.info) + if cond.kind == nkStmtListExpr: + let (st, ex) = exprToStmtList(cond) + ifBody.add(st) + cond = ex + ifBody.add(ctx.newEnvVarAsgn(tmp, cond)) + + let ifBranch = newTree(nkElifBranch, check, ifBody) + let ifNode = newTree(nkIfStmt, ifBranch) + result.add(ifNode) + result.add(ctx.newEnvVarAccess(tmp)) + else: + for i in 0 ..< n.len: + if n[i].kind == nkStmtListExpr: + let (st, ex) = exprToStmtList(n[i]) + result.add(st) + n[i] = ex + + if n[i].kind in nkCallKinds: # XXX: This should better be some sort of side effect tracking + let tmp = ctx.newTempVar(n[i].typ) + result.add(ctx.newEnvVarAsgn(tmp, n[i])) + n[i] = ctx.newEnvVarAccess(tmp) + + result.add(n) + + of nkVarSection, nkLetSection: + result = newNodeI(nkStmtList, n.info) + for c in n: + let varSect = newNodeI(n.kind, n.info) + varSect.add(c) + var ns = false + c[^1] = ctx.lowerStmtListExprs(c[^1], ns) + if ns: + needsSplit = true + assert(c[^1].kind == nkStmtListExpr) + let (st, ex) = exprToStmtList(c[^1]) + result.add(st) + c[^1] = ex + result.add(varSect) + + of nkDiscardStmt, nkReturnStmt, nkRaiseStmt: + var ns = false + for i in 0 ..< n.len: + n[i] = ctx.lowerStmtListExprs(n[i], ns) + + if ns: + needsSplit = true + result = newNodeI(nkStmtList, n.info) + let (st, ex) = exprToStmtList(n[0]) + result.add(st) + n[0] = ex + result.add(n) + + of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv: + var ns = false + for i in 0 ..< n.len: + n[i] = ctx.lowerStmtListExprs(n[i], ns) + + if ns: + needsSplit = true + result = newNodeI(nkStmtListExpr, n.info) + result.typ = n.typ + let (st, ex) = exprToStmtList(n[1]) + result.add(st) + n[1] = ex + result.add(n) + + of nkAsgn, nkFastAsgn: + var ns = false + for i in 0 ..< n.len: + n[i] = ctx.lowerStmtListExprs(n[i], ns) + + if ns: + needsSplit = true + result = newNodeI(nkStmtList, n.info) + if n[0].kind == nkStmtListExpr: + let (st, ex) = exprToStmtList(n[0]) + result.add(st) + n[0] = ex + + if n[1].kind == nkStmtListExpr: + let (st, ex) = exprToStmtList(n[1]) + result.add(st) + n[1] = ex + + result.add(n) + + of nkWhileStmt: + var ns = false + + var condNeedsSplit = false + n[0] = ctx.lowerStmtListExprs(n[0], condNeedsSplit) + var bodyNeedsSplit = false + n[1] = ctx.lowerStmtListExprs(n[1], bodyNeedsSplit) + + if condNeedsSplit or bodyNeedsSplit: + needsSplit = true + + if condNeedsSplit: + let (st, ex) = exprToStmtList(n[0]) + let brk = newTree(nkBreakStmt, emptyNode) + let branch = newTree(nkElifBranch, ctx.g.newNotCall(ex), brk) + let check = newTree(nkIfStmt, branch) + let newBody = newTree(nkStmtList, st, check, n[1]) + + n[0] = newSymNode(ctx.g.getSysSym(n[0].info, "true")) + n[1] = newBody + else: + for i in 0 ..< n.len: + n[i] = ctx.lowerStmtListExprs(n[i], needsSplit) + +proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode = + # Generate the following code: + # if :unrollFinally: + # if :curExc.isNil: + # return :tmpResult + # else: + # raise + let curExc = ctx.newCurExcAccess() + let nilnode = newNode(nkNilLit) + nilnode.typ = curExc.typ + let cmp = newTree(nkCall, newSymNode(ctx.g.getSysMagic(info, "==", mEqRef), info), curExc, nilnode) + cmp.typ = ctx.g.getSysType(info, tyBool) + + let asgn = newTree(nkFastAsgn, + newSymNode(getClosureIterResult(ctx.fn), info), + ctx.newTmpResultAccess()) + + let retStmt = newTree(nkReturnStmt, asgn) + let branch = newTree(nkElifBranch, cmp, retStmt) + + # The C++ backend requires `getCurrentException` here. + let raiseStmt = newTree(nkRaiseStmt, ctx.g.callCodegenProc("getCurrentException", emptyNode)) + raiseStmt.info = info + let elseBranch = newTree(nkElse, raiseStmt) + + let ifBody = newTree(nkIfStmt, branch, elseBranch) + let elifBranch = newTree(nkElifBranch, ctx.newUnrollFinallyAccess(info), ifBody) + elifBranch.info = info + result = newTree(nkIfStmt, elifBranch) + +proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode = + result = n + # TODO: This is very inefficient. It traverses the node, looking for nkYieldStmt. + case n.kind + of nkReturnStmt: + # We're somewhere in try, transform to finally unrolling + assert(ctx.nearestFinally != 0) + + result = newNodeI(nkStmtList, n.info) + + block: # :unrollFinally = true + let asgn = newNodeI(nkAsgn, n.info) + asgn.add(ctx.newUnrollFinallyAccess(n.info)) + asgn.add(newIntTypeNode(nkIntLit, 1, ctx.g.getSysType(n.info, tyBool))) + result.add(asgn) + + if n[0].kind != nkEmpty: + let asgnTmpResult = newNodeI(nkAsgn, n.info) + asgnTmpResult.add(ctx.newTmpResultAccess()) + asgnTmpResult.add(n[0]) + result.add(asgnTmpResult) + + result.add(ctx.newNullifyCurExc(n.info)) + + let goto = newTree(nkGotoState, ctx.g.newIntLit(n.info, ctx.nearestFinally)) + result.add(goto) + + of nkCharLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkStrLit..nkTripleStrLit, + nkSym, nkIdent, procDefs, nkTemplateDef: + discard + else: + for i in 0 ..< n.len: + n[i] = ctx.transformReturnsInTry(n[i]) + +proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode = + result = n + case n.kind: + of nkCharLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkStrLit..nkTripleStrLit, + nkSym, nkIdent, procDefs, nkTemplateDef: + discard + + of nkStmtList, nkStmtListExpr: + assert(isEmptyType(n.typ), "nkStmtListExpr not lowered") + + result = addGotoOut(result, gotoOut) + for i in 0 ..< n.len: + if n[i].hasYieldsInExpressions: + # Lower nkStmtListExpr nodes inside `n[i]` first + var ns = false + n[i] = ctx.lowerStmtListExprs(n[i], ns) + + if n[i].hasYields: + # Create a new split + let go = newNodeI(nkGotoState, n[i].info) + n[i] = ctx.transformClosureIteratorBody(n[i], go) + + let s = newNodeI(nkStmtList, n[i + 1].info) + for j in i + 1 ..< n.len: + s.add(n[j]) + + n.sons.setLen(i + 1) + discard ctx.newState(s, go) + if ctx.transformClosureIteratorBody(s, gotoOut) != s: + internalError(ctx.g.config, "transformClosureIteratorBody != s") + break + + of nkYieldStmt: + result = newNodeI(nkStmtList, n.info) + result.add(n) + result.add(gotoOut) + + of nkElse, nkElseExpr: + result[0] = addGotoOut(result[0], gotoOut) + result[0] = ctx.transformClosureIteratorBody(result[0], gotoOut) + + of nkElifBranch, nkElifExpr, nkOfBranch: + result[1] = addGotoOut(result[1], gotoOut) + result[1] = ctx.transformClosureIteratorBody(result[1], gotoOut) + + of nkIfStmt, nkCaseStmt: + for i in 0 ..< n.len: + n[i] = ctx.transformClosureIteratorBody(n[i], gotoOut) + if n[^1].kind != nkElse: + # We don't have an else branch, but every possible branch has to end with + # gotoOut, so add else here. + let elseBranch = newTree(nkElse, gotoOut) + n.add(elseBranch) + + of nkWhileStmt: + # while e: + # s + # -> + # BEGIN_STATE: + # if e: + # s + # goto BEGIN_STATE + # else: + # goto OUT + + result = newNodeI(nkGotoState, n.info) + + let s = newNodeI(nkStmtList, n.info) + discard ctx.newState(s, result) + let ifNode = newNodeI(nkIfStmt, n.info) + let elifBranch = newNodeI(nkElifBranch, n.info) + elifBranch.add(n[0]) + + var body = addGotoOut(n[1], result) + + body = ctx.transformBreaksAndContinuesInWhile(body, result, gotoOut) + body = ctx.transformClosureIteratorBody(body, result) + + elifBranch.add(body) + ifNode.add(elifBranch) + + let elseBranch = newTree(nkElse, gotoOut) + ifNode.add(elseBranch) + s.add(ifNode) + + of nkBlockStmt: + result[1] = addGotoOut(result[1], gotoOut) + result[1] = ctx.transformBreaksInBlock(result[1], result[0], gotoOut) + result[1] = ctx.transformClosureIteratorBody(result[1], gotoOut) + + of nkTryStmt: + # See explanation above about how this works + ctx.hasExceptions = true + + result = newNodeI(nkGotoState, n.info) + var tryBody = toStmtList(n[0]) + var exceptBody = ctx.collectExceptState(n) + var finallyBody = newTree(nkStmtList, getFinallyNode(n)) + finallyBody = ctx.transformReturnsInTry(finallyBody) + finallyBody.add(ctx.newEndFinallyNode(finallyBody.info)) + + # The following index calculation is based on the knowledge how state + # indexes are assigned + let tryIdx = ctx.states.len + var exceptIdx, finallyIdx: int + if exceptBody.kind != nkEmpty: + exceptIdx = -(tryIdx + 1) + finallyIdx = tryIdx + 2 + else: + exceptIdx = tryIdx + 1 + finallyIdx = tryIdx + 1 + + let outToFinally = newNodeI(nkGotoState, finallyBody.info) + + block: # Create initial states. + let oldExcHandlingState = ctx.curExcHandlingState + ctx.curExcHandlingState = exceptIdx + let realTryIdx = ctx.newState(tryBody, result) + assert(realTryIdx == tryIdx) + + if exceptBody.kind != nkEmpty: + ctx.curExcHandlingState = finallyIdx + let realExceptIdx = ctx.newState(exceptBody, nil) + assert(realExceptIdx == -exceptIdx) + + ctx.curExcHandlingState = oldExcHandlingState + let realFinallyIdx = ctx.newState(finallyBody, outToFinally) + assert(realFinallyIdx == finallyIdx) + + block: # Subdivide the states + let oldNearestFinally = ctx.nearestFinally + ctx.nearestFinally = finallyIdx + + let oldExcHandlingState = ctx.curExcHandlingState + + ctx.curExcHandlingState = exceptIdx + + if ctx.transformReturnsInTry(tryBody) != tryBody: + internalError(ctx.g.config, "transformReturnsInTry != tryBody") + if ctx.transformClosureIteratorBody(tryBody, outToFinally) != tryBody: + internalError(ctx.g.config, "transformClosureIteratorBody != tryBody") + + ctx.curExcHandlingState = finallyIdx + ctx.addElseToExcept(exceptBody) + if ctx.transformReturnsInTry(exceptBody) != exceptBody: + internalError(ctx.g.config, "transformReturnsInTry != exceptBody") + if ctx.transformClosureIteratorBody(exceptBody, outToFinally) != exceptBody: + internalError(ctx.g.config, "transformClosureIteratorBody != exceptBody") + + ctx.curExcHandlingState = oldExcHandlingState + ctx.nearestFinally = oldNearestFinally + if ctx.transformClosureIteratorBody(finallyBody, gotoOut) != finallyBody: + internalError(ctx.g.config, "transformClosureIteratorBody != finallyBody") + + of nkGotoState, nkForStmt: + internalError(ctx.g.config, "closure iter " & $n.kind) + + else: + for i in 0 ..< n.len: + n[i] = ctx.transformClosureIteratorBody(n[i], gotoOut) + +proc stateFromGotoState(n: PNode): int = + assert(n.kind == nkGotoState) + result = n[0].intVal.int + +proc tranformStateAssignments(ctx: var Ctx, n: PNode): PNode = + # This transforms 3 patterns: + ########################## 1 + # yield e + # goto STATE + # -> + # :state = STATE + # return e + ########################## 2 + # goto STATE + # -> + # :state = STATE + # break :stateLoop + ########################## 3 + # return e + # -> + # :state = -1 + # return e + # + result = n + case n.kind + of nkStmtList, nkStmtListExpr: + if n.len != 0 and n[0].kind == nkYieldStmt: + assert(n.len == 2) + assert(n[1].kind == nkGotoState) + + result = newNodeI(nkStmtList, n.info) + result.add(ctx.newStateAssgn(stateFromGotoState(n[1]))) + + var retStmt = newNodeI(nkReturnStmt, n.info) + if n[0].sons[0].kind != nkEmpty: + var a = newNodeI(nkAsgn, n[0].sons[0].info) + var retVal = n[0].sons[0] #liftCapturedVars(n.sons[0], owner, d, c) + addSon(a, newSymNode(getClosureIterResult(ctx.fn))) + addSon(a, retVal) + retStmt.add(a) + else: + retStmt.add(emptyNode) + + result.add(retStmt) + else: + for i in 0 ..< n.len: + n[i] = ctx.tranformStateAssignments(n[i]) + + of nkCharLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkStrLit..nkTripleStrLit, + nkSym, nkIdent, procDefs, nkTemplateDef: + discard + + of nkReturnStmt: + result = newNodeI(nkStmtList, n.info) + result.add(ctx.newStateAssgn(-1)) + result.add(n) + + of nkGotoState: + result = newNodeI(nkStmtList, n.info) + result.add(ctx.newStateAssgn(stateFromGotoState(n))) + + let breakState = newNodeI(nkBreakStmt, n.info) + breakState.add(newSymNode(ctx.stateLoopLabel)) + result.add(breakState) + + else: + for i in 0 ..< n.len: + n[i] = ctx.tranformStateAssignments(n[i]) + +proc skipStmtList(n: PNode): PNode = + result = n + while result.kind in {nkStmtList}: + if result.len == 0: return emptyNode + result = result[0] + +proc skipEmptyStates(ctx: Ctx, stateIdx: int): int = + # Returns first non-empty state idx for `stateIdx`. Returns `stateIdx` if + # it is not empty + var maxJumps = ctx.states.len # maxJumps used only for debugging purposes. + var stateIdx = stateIdx + while true: + let label = stateIdx + if label == ctx.exitStateIdx: break + var newLabel = label + if label == -1: + newLabel = ctx.exitStateIdx + else: + let fs = ctx.states[label][1].skipStmtList() + if fs.kind == nkGotoState: + newLabel = fs[0].intVal.int + if label == newLabel: break + stateIdx = newLabel + dec maxJumps + if maxJumps == 0: + assert(false, "Internal error") + + result = ctx.states[stateIdx][0].intVal.int + +proc skipThroughEmptyStates(ctx: var Ctx, n: PNode): PNode = + result = n + case n.kind + of nkCharLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkStrLit..nkTripleStrLit, + nkSym, nkIdent, procDefs, nkTemplateDef: + discard + of nkGotoState: + result = copyTree(n) + result[0].intVal = ctx.skipEmptyStates(result[0].intVal.int) + else: + for i in 0 ..< n.len: + n[i] = ctx.skipThroughEmptyStates(n[i]) + +proc newArrayType(g: ModuleGraph; n: int, t: PType, owner: PSym): PType = + result = newType(tyArray, owner) + + let rng = newType(tyRange, owner) + rng.n = newTree(nkRange, g.newIntLit(owner.info, 0), g.newIntLit(owner.info, n)) + rng.rawAddSon(t) + + result.rawAddSon(rng) + result.rawAddSon(t) + +proc createExceptionTable(ctx: var Ctx): PNode {.inline.} = + result = newNodeI(nkBracket, ctx.fn.info) + result.typ = ctx.g.newArrayType(ctx.exceptionTable.len, ctx.g.getSysType(ctx.fn.info, tyInt16), ctx.fn) + + for i in ctx.exceptionTable: + let elem = newIntNode(nkIntLit, i) + elem.typ = ctx.g.getSysType(ctx.fn.info, tyInt16) + result.add(elem) + +proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} = + # Generates the code: + # :state = exceptionTable[:state] + # if :state == 0: raise + # :unrollFinally = :state > 0 + # if :state < 0: + # :state = -:state + # :curExc = getCurrentException() + + result = newNodeI(nkStmtList, info) + + let intTyp = ctx.g.getSysType(info, tyInt) + let boolTyp = ctx.g.getSysType(info, tyBool) + + # :state = exceptionTable[:state] + block: + + # exceptionTable[:state] + let getNextState = newTree(nkBracketExpr, + ctx.createExceptionTable(), + ctx.newStateAccess()) + getNextState.typ = intTyp + + # :state = exceptionTable[:state] + result.add(ctx.newStateAssgn(getNextState)) + + # if :state == 0: raise + block: + let cond = newTree(nkCall, + ctx.g.getSysMagic(info, "==", mEqI).newSymNode(), + ctx.newStateAccess(), + newIntTypeNode(nkIntLit, 0, intTyp)) + cond.typ = boolTyp + + let raiseStmt = newTree(nkRaiseStmt, emptyNode) + let ifBranch = newTree(nkElifBranch, cond, raiseStmt) + let ifStmt = newTree(nkIfStmt, ifBranch) + result.add(ifStmt) + + # :unrollFinally = :state > 0 + block: + let cond = newTree(nkCall, + ctx.g.getSysMagic(info, "<", mLtI).newSymNode, + newIntTypeNode(nkIntLit, 0, intTyp), + ctx.newStateAccess()) + cond.typ = boolTyp + + let asgn = newTree(nkAsgn, ctx.newUnrollFinallyAccess(info), cond) + result.add(asgn) + + # if :state < 0: :state = -:state + block: + let cond = newTree(nkCall, + ctx.g.getSysMagic(info, "<", mLtI).newSymNode, + ctx.newStateAccess(), + newIntTypeNode(nkIntLit, 0, intTyp)) + cond.typ = boolTyp + + let negateState = newTree(nkCall, + ctx.g.getSysMagic(info, "-", mUnaryMinusI).newSymNode, + ctx.newStateAccess()) + negateState.typ = intTyp + + let ifBranch = newTree(nkElifBranch, cond, ctx.newStateAssgn(negateState)) + let ifStmt = newTree(nkIfStmt, ifBranch) + result.add(ifStmt) + + # :curExc = getCurrentException() + block: + result.add(newTree(nkAsgn, + ctx.newCurExcAccess(), + ctx.g.callCodegenProc("getCurrentException", emptyNode))) + +proc wrapIntoTryExcept(ctx: var Ctx, n: PNode): PNode {.inline.} = + let setupExc = newTree(nkCall, + newSymNode(ctx.g.getCompilerProc("closureIterSetupExc")), + ctx.newCurExcAccess()) + + let tryBody = newTree(nkStmtList, setupExc, n) + let exceptBranch = newTree(nkExceptBranch, ctx.newCatchBody(ctx.fn.info)) + + result = newTree(nkTryStmt, tryBody, exceptBranch) + +proc wrapIntoStateLoop(ctx: var Ctx, n: PNode): PNode = + # while true: + # block :stateLoop: + # gotoState :state + # body # Might get wrapped in try-except + let loopBody = newNodeI(nkStmtList, n.info) + result = newTree(nkWhileStmt, newSymNode(ctx.g.getSysSym(n.info, "true")), loopBody) + result.info = n.info + + if not ctx.stateVarSym.isNil: + let varSect = newNodeI(nkVarSection, n.info) + addVar(varSect, newSymNode(ctx.stateVarSym)) + loopBody.add(varSect) + + if not ctx.tempVars.isNil: + loopBody.add(ctx.tempVars) + + let blockStmt = newNodeI(nkBlockStmt, n.info) + blockStmt.add(newSymNode(ctx.stateLoopLabel)) + + let gs = newNodeI(nkGotoState, n.info) + gs.add(ctx.newStateAccess()) + gs.add(ctx.g.newIntLit(n.info, ctx.states.len - 1)) + + var blockBody = newTree(nkStmtList, gs, n) + if ctx.hasExceptions: + blockBody = ctx.wrapIntoTryExcept(blockBody) + + blockStmt.add(blockBody) + loopBody.add(blockStmt) + +proc deleteEmptyStates(ctx: var Ctx) = + let goOut = newTree(nkGotoState, ctx.g.newIntLit(TLineInfo(), -1)) + ctx.exitStateIdx = ctx.newState(goOut, nil) + + # Apply new state indexes and mark unused states with -1 + var iValid = 0 + for i, s in ctx.states: + let body = s[1].skipStmtList() + if body.kind == nkGotoState and i != ctx.states.len - 1 and i != 0: + # This is an empty state. Mark with -1. + s[0].intVal = -1 + else: + s[0].intVal = iValid + inc iValid + + for i, s in ctx.states: + let body = s[1].skipStmtList() + if body.kind != nkGotoState or i == 0: + discard ctx.skipThroughEmptyStates(s) + let excHandlState = ctx.exceptionTable[i] + if excHandlState < 0: + ctx.exceptionTable[i] = -ctx.skipEmptyStates(-excHandlState) + elif excHandlState != 0: + ctx.exceptionTable[i] = ctx.skipEmptyStates(excHandlState) + + var i = 0 + while i < ctx.states.len - 1: + let fs = ctx.states[i][1].skipStmtList() + if fs.kind == nkGotoState and i != 0: + ctx.states.delete(i) + ctx.exceptionTable.delete(i) + else: + inc i + +proc transformClosureIterator*(g: ModuleGraph; fn: PSym, n: PNode): PNode = + var ctx: Ctx + ctx.g = g + ctx.fn = fn + + if getEnvParam(fn).isNil: + # Lambda lifting was not done yet. Use temporary :state sym, which + # be handled specially by lambda lifting. Local temp vars (if needed) + # should folllow the same logic. + ctx.stateVarSym = newSym(skVar, getIdent(":state"), fn, fn.info) + ctx.stateVarSym.typ = g.createClosureIterStateType(fn) + + ctx.stateLoopLabel = newSym(skLabel, getIdent(":stateLoop"), fn, fn.info) + let n = n.toStmtList + + discard ctx.newState(n, nil) + let gotoOut = newTree(nkGotoState, g.newIntLit(n.info, -1)) + + # Splitting transformation + discard ctx.transformClosureIteratorBody(n, gotoOut) + + # Optimize empty states away + ctx.deleteEmptyStates() + + # Make new body by concating the list of states + result = newNodeI(nkStmtList, n.info) + for s in ctx.states: + assert(s.len == 2) + let body = s[1] + s.sons.del(1) + result.add(s) + result.add(body) + + result = ctx.tranformStateAssignments(result) + result = ctx.wrapIntoStateLoop(result) + + # echo "TRANSFORM TO STATES: " + # echo renderTree(result) + + # echo "exception table:" + # for i, e in ctx.exceptionTable: + # echo i, " -> ", e diff --git a/compiler/commands.nim b/compiler/commands.nim index 2d9f76959..09f63f0f5 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -18,7 +18,6 @@ template bootSwitch(name, expr, userString) = bootSwitch(usedRelease, defined(release), "-d:release") bootSwitch(usedGnuReadline, defined(useLinenoise), "-d:useLinenoise") -bootSwitch(usedNoCaas, defined(noCaas), "-d:noCaas") bootSwitch(usedBoehm, defined(boehmgc), "--gc:boehm") bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep") bootSwitch(usedGenerational, defined(gcgenerational), "--gc:generational") @@ -27,90 +26,100 @@ bootSwitch(usedNoGC, defined(nogc), "--gc:none") import os, msgs, options, nversion, condsyms, strutils, extccomp, platform, - wordrecg, parseutils, nimblecmd, idents, parseopt, sequtils + wordrecg, parseutils, nimblecmd, idents, parseopt, sequtils, configuration # but some have deps to imported modules. Yay. bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc") -bootSwitch(usedAvoidTimeMachine, noTimeMachine, "-d:avoidTimeMachine") bootSwitch(usedNativeStacktrace, defined(nativeStackTrace) and nativeStackTraceSupported, "-d:nativeStackTrace") bootSwitch(usedFFI, hasFFI, "-d:useFFI") - -proc writeCommandLineUsage*() - type TCmdLinePass* = enum passCmd1, # first pass over the command line passCmd2, # second pass over the command line passPP # preprocessor called processCommand() -proc processCommand*(switch: string, pass: TCmdLinePass) -proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; - config: ConfigRef = nil) - -# implementation - const HelpMessage = "Nim Compiler Version $1 [$2: $3]\n" & - "Copyright (c) 2006-2017 by Andreas Rumpf\n" + "Compiled at $4\n" & + "Copyright (c) 2006-" & copyrightYear & " by Andreas Rumpf\n" const Usage = slurp"../doc/basicopt.txt".replace("//", "") - AdvancedUsage = slurp"../doc/advopt.txt".replace("//", "") + FeatureDesc = block: + var x = "" + for f in low(Feature)..high(Feature): + if x.len > 0: x.add "|" + x.add $f + x + AdvancedUsage = slurp"../doc/advopt.txt".replace("//", "") % FeatureDesc proc getCommandLineDesc(): string = result = (HelpMessage % [VersionAsString, platform.OS[platform.hostOS].name, - CPU[platform.hostCPU].name]) & Usage + CPU[platform.hostCPU].name, CompileDate]) & + Usage -proc helpOnError(pass: TCmdLinePass) = +proc helpOnError(conf: ConfigRef; pass: TCmdLinePass) = if pass == passCmd1: - msgWriteln(getCommandLineDesc(), {msgStdout}) + msgWriteln(conf, getCommandLineDesc(), {msgStdout}) msgQuit(0) -proc writeAdvancedUsage(pass: TCmdLinePass) = +proc writeAdvancedUsage(conf: ConfigRef; pass: TCmdLinePass) = if pass == passCmd1: - msgWriteln(`%`(HelpMessage, [VersionAsString, + msgWriteln(conf, (HelpMessage % [VersionAsString, platform.OS[platform.hostOS].name, - CPU[platform.hostCPU].name]) & AdvancedUsage, + CPU[platform.hostCPU].name, CompileDate]) & + AdvancedUsage, {msgStdout}) msgQuit(0) -proc writeVersionInfo(pass: TCmdLinePass) = +proc writeFullhelp(conf: ConfigRef; pass: TCmdLinePass) = if pass == passCmd1: - msgWriteln(`%`(HelpMessage, [VersionAsString, + msgWriteln(conf, `%`(HelpMessage, [VersionAsString, platform.OS[platform.hostOS].name, - CPU[platform.hostCPU].name]), + CPU[platform.hostCPU].name, CompileDate]) & + Usage & AdvancedUsage, + {msgStdout}) + msgQuit(0) + +proc writeVersionInfo(conf: ConfigRef; pass: TCmdLinePass) = + if pass == passCmd1: + msgWriteln(conf, `%`(HelpMessage, [VersionAsString, + platform.OS[platform.hostOS].name, + CPU[platform.hostCPU].name, CompileDate]), {msgStdout}) const gitHash = gorge("git log -n 1 --format=%H").strip when gitHash.len == 40: - msgWriteln("git hash: " & gitHash, {msgStdout}) + msgWriteln(conf, "git hash: " & gitHash, {msgStdout}) - msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine & - usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas & + msgWriteln(conf, "active boot switches:" & usedRelease & + usedTinyC & usedGnuReadline & usedNativeStacktrace & usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedGoGC & usedNoGC, {msgStdout}) msgQuit(0) -var - helpWritten: bool - -proc writeCommandLineUsage() = +proc writeCommandLineUsage*(conf: ConfigRef; helpWritten: var bool) = if not helpWritten: - msgWriteln(getCommandLineDesc(), {msgStdout}) + msgWriteln(conf, getCommandLineDesc(), {msgStdout}) helpWritten = true proc addPrefix(switch: string): string = if len(switch) == 1: result = "-" & switch else: result = "--" & switch -proc invalidCmdLineOption(pass: TCmdLinePass, switch: string, info: TLineInfo) = - if switch == " ": localError(info, errInvalidCmdLineOption, "-") - else: localError(info, errInvalidCmdLineOption, addPrefix(switch)) +const + errInvalidCmdLineOption = "invalid command line option: '$1'" + errOnOrOffExpectedButXFound = "'on' or 'off' expected, but '$1' found" + errOnOffOrListExpectedButXFound = "'on', 'off' or 'list' expected, but '$1' found" -proc splitSwitch(switch: string, cmd, arg: var string, pass: TCmdLinePass, +proc invalidCmdLineOption(conf: ConfigRef; pass: TCmdLinePass, switch: string, info: TLineInfo) = + if switch == " ": localError(conf, info, errInvalidCmdLineOption % "-") + else: localError(conf, info, errInvalidCmdLineOption % addPrefix(switch)) + +proc splitSwitch(conf: ConfigRef; switch: string, cmd, arg: var string, pass: TCmdLinePass, info: TLineInfo) = cmd = "" var i = 0 @@ -123,46 +132,41 @@ proc splitSwitch(switch: string, cmd, arg: var string, pass: TCmdLinePass, inc(i) if i >= len(switch): arg = "" elif switch[i] in {':', '=', '['}: arg = substr(switch, i + 1) - else: invalidCmdLineOption(pass, switch, info) + else: invalidCmdLineOption(conf, pass, switch, info) -proc processOnOffSwitch(op: TOptions, arg: string, pass: TCmdLinePass, +proc processOnOffSwitch(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass, info: TLineInfo) = case arg.normalize - of "on": gOptions = gOptions + op - of "off": gOptions = gOptions - op - else: localError(info, errOnOrOffExpectedButXFound, arg) + of "on": conf.options = conf.options + op + of "off": conf.options = conf.options - op + else: localError(conf, info, errOnOrOffExpectedButXFound % arg) -proc processOnOffSwitchOrList(op: TOptions, arg: string, pass: TCmdLinePass, +proc processOnOffSwitchOrList(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass, info: TLineInfo): bool = result = false case arg.normalize - of "on": gOptions = gOptions + op - of "off": gOptions = gOptions - op - else: - if arg == "list": - result = true - else: - localError(info, errOnOffOrListExpectedButXFound, arg) + of "on": conf.options = conf.options + op + of "off": conf.options = conf.options - op + of "list": result = true + else: localError(conf, info, errOnOffOrListExpectedButXFound % arg) -proc processOnOffSwitchG(op: TGlobalOptions, arg: string, pass: TCmdLinePass, +proc processOnOffSwitchG(conf: ConfigRef; op: TGlobalOptions, arg: string, pass: TCmdLinePass, info: TLineInfo) = case arg.normalize - of "on": gGlobalOptions = gGlobalOptions + op - of "off": gGlobalOptions = gGlobalOptions - op - else: localError(info, errOnOrOffExpectedButXFound, arg) - -proc expectArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = - if arg == "": localError(info, errCmdLineArgExpected, addPrefix(switch)) + of "on": conf.globalOptions = conf.globalOptions + op + of "off": conf.globalOptions = conf.globalOptions - op + else: localError(conf, info, errOnOrOffExpectedButXFound % arg) -proc expectNoArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = - if arg != "": localError(info, errCmdLineNoArgExpected, addPrefix(switch)) +proc expectArg(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = + if arg == "": + localError(conf, info, "argument for command line option expected: '$1'" % addPrefix(switch)) -var - enableNotes: TNoteKinds - disableNotes: TNoteKinds +proc expectNoArg(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = + if arg != "": + localError(conf, info, "invalid argument for command line option: '$1'" % addPrefix(switch)) proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass, - info: TLineInfo; orig: string) = + info: TLineInfo; orig: string; conf: ConfigRef) = var id = "" # arg = "X]:on|off" var i = 0 var n = hintMin @@ -170,122 +174,127 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass, add(id, arg[i]) inc(i) if i < len(arg) and (arg[i] == ']'): inc(i) - else: invalidCmdLineOption(pass, orig, info) + else: invalidCmdLineOption(conf, pass, orig, info) if i < len(arg) and (arg[i] in {':', '='}): inc(i) - else: invalidCmdLineOption(pass, orig, info) + else: invalidCmdLineOption(conf, pass, orig, info) if state == wHint: - var x = findStr(msgs.HintsToStr, id) + let x = findStr(configuration.HintsToStr, id) if x >= 0: n = TNoteKind(x + ord(hintMin)) - else: localError(info, "unknown hint: " & id) + else: localError(conf, info, "unknown hint: " & id) else: - var x = findStr(msgs.WarningsToStr, id) + let x = findStr(configuration.WarningsToStr, id) if x >= 0: n = TNoteKind(x + ord(warnMin)) - else: localError(info, "unknown warning: " & id) + else: localError(conf, info, "unknown warning: " & id) case substr(arg, i).normalize of "on": - incl(gNotes, n) - incl(gMainPackageNotes, n) - incl(enableNotes, n) + incl(conf.notes, n) + incl(conf.mainPackageNotes, n) + incl(conf.enableNotes, n) of "off": - excl(gNotes, n) - excl(gMainPackageNotes, n) - incl(disableNotes, n) - excl(ForeignPackageNotes, n) - else: localError(info, errOnOrOffExpectedButXFound, arg) - -proc processCompile(filename: string) = - var found = findFile(filename) + excl(conf.notes, n) + excl(conf.mainPackageNotes, n) + incl(conf.disableNotes, n) + excl(conf.foreignPackageNotes, n) + else: localError(conf, info, errOnOrOffExpectedButXFound % arg) + +proc processCompile(conf: ConfigRef; filename: string) = + var found = findFile(conf, filename) if found == "": found = filename - extccomp.addExternalFileToCompile(found) + extccomp.addExternalFileToCompile(conf, found) + +const + errNoneBoehmRefcExpectedButXFound = "'none', 'boehm' or 'refc' expected, but '$1' found" + errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found" + errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found" -proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool = +proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo): bool = case switch.normalize of "gc": case arg.normalize - of "boehm": result = gSelectedGC == gcBoehm - of "refc": result = gSelectedGC == gcRefc - of "v2": result = gSelectedGC == gcV2 - of "markandsweep": result = gSelectedGC == gcMarkAndSweep - of "generational": result = gSelectedGC == gcGenerational - of "go": result = gSelectedGC == gcGo - of "none": result = gSelectedGC == gcNone - of "stack", "regions": result = gSelectedGC == gcRegions - else: localError(info, errNoneBoehmRefcExpectedButXFound, arg) + of "boehm": result = conf.selectedGC == gcBoehm + of "refc": result = conf.selectedGC == gcRefc + of "v2": result = conf.selectedGC == gcV2 + of "markandsweep": result = conf.selectedGC == gcMarkAndSweep + of "generational": result = conf.selectedGC == gcGenerational + of "go": result = conf.selectedGC == gcGo + of "none": result = conf.selectedGC == gcNone + of "stack", "regions": result = conf.selectedGC == gcRegions + else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg) of "opt": case arg.normalize - of "speed": result = contains(gOptions, optOptimizeSpeed) - of "size": result = contains(gOptions, optOptimizeSize) - of "none": result = gOptions * {optOptimizeSpeed, optOptimizeSize} == {} - else: localError(info, errNoneSpeedOrSizeExpectedButXFound, arg) - of "verbosity": result = $gVerbosity == arg + of "speed": result = contains(conf.options, optOptimizeSpeed) + of "size": result = contains(conf.options, optOptimizeSize) + of "none": result = conf.options * {optOptimizeSpeed, optOptimizeSize} == {} + else: localError(conf, info, errNoneSpeedOrSizeExpectedButXFound % arg) + of "verbosity": result = $conf.verbosity == arg of "app": case arg.normalize - of "gui": result = contains(gGlobalOptions, optGenGuiApp) - of "console": result = not contains(gGlobalOptions, optGenGuiApp) - of "lib": result = contains(gGlobalOptions, optGenDynLib) and - not contains(gGlobalOptions, optGenGuiApp) - of "staticlib": result = contains(gGlobalOptions, optGenStaticLib) and - not contains(gGlobalOptions, optGenGuiApp) - else: localError(info, errGuiConsoleOrLibExpectedButXFound, arg) + of "gui": result = contains(conf.globalOptions, optGenGuiApp) + of "console": result = not contains(conf.globalOptions, optGenGuiApp) + of "lib": result = contains(conf.globalOptions, optGenDynLib) and + not contains(conf.globalOptions, optGenGuiApp) + of "staticlib": result = contains(conf.globalOptions, optGenStaticLib) and + not contains(conf.globalOptions, optGenGuiApp) + else: localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg) of "dynliboverride": - result = isDynlibOverride(arg) - else: invalidCmdLineOption(passCmd1, switch, info) + result = isDynlibOverride(conf, arg) + else: invalidCmdLineOption(conf, passCmd1, switch, info) -proc testCompileOption*(switch: string, info: TLineInfo): bool = +proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool = case switch.normalize - of "debuginfo": result = contains(gGlobalOptions, optCDebug) - of "compileonly", "c": result = contains(gGlobalOptions, optCompileOnly) - of "nolinking": result = contains(gGlobalOptions, optNoLinking) - of "nomain": result = contains(gGlobalOptions, optNoMain) - of "forcebuild", "f": result = contains(gGlobalOptions, optForceFullMake) - of "warnings", "w": result = contains(gOptions, optWarns) - of "hints": result = contains(gOptions, optHints) - of "threadanalysis": result = contains(gGlobalOptions, optThreadAnalysis) - of "stacktrace": result = contains(gOptions, optStackTrace) - of "linetrace": result = contains(gOptions, optLineTrace) - of "debugger": result = contains(gOptions, optEndb) - of "profiler": result = contains(gOptions, optProfiler) - of "memtracker": result = contains(gOptions, optMemTracker) - of "checks", "x": result = gOptions * ChecksOptions == ChecksOptions + of "debuginfo": result = contains(conf.globalOptions, optCDebug) + of "compileonly", "c": result = contains(conf.globalOptions, optCompileOnly) + of "nolinking": result = contains(conf.globalOptions, optNoLinking) + of "nomain": result = contains(conf.globalOptions, optNoMain) + of "forcebuild", "f": result = contains(conf.globalOptions, optForceFullMake) + of "warnings", "w": result = contains(conf.options, optWarns) + of "hints": result = contains(conf.options, optHints) + of "threadanalysis": result = contains(conf.globalOptions, optThreadAnalysis) + of "stacktrace": result = contains(conf.options, optStackTrace) + of "linetrace": result = contains(conf.options, optLineTrace) + of "debugger": result = contains(conf.options, optEndb) + of "profiler": result = contains(conf.options, optProfiler) + of "memtracker": result = contains(conf.options, optMemTracker) + of "checks", "x": result = conf.options * ChecksOptions == ChecksOptions of "floatchecks": - result = gOptions * {optNaNCheck, optInfCheck} == {optNaNCheck, optInfCheck} - of "infchecks": result = contains(gOptions, optInfCheck) - of "nanchecks": result = contains(gOptions, optNaNCheck) - of "nilchecks": result = contains(gOptions, optNilCheck) - of "objchecks": result = contains(gOptions, optObjCheck) - of "fieldchecks": result = contains(gOptions, optFieldCheck) - of "rangechecks": result = contains(gOptions, optRangeCheck) - of "boundchecks": result = contains(gOptions, optBoundsCheck) - of "overflowchecks": result = contains(gOptions, optOverflowCheck) - of "linedir": result = contains(gOptions, optLineDir) - of "assertions", "a": result = contains(gOptions, optAssert) - of "deadcodeelim": result = contains(gGlobalOptions, optDeadCodeElim) - of "run", "r": result = contains(gGlobalOptions, optRun) - of "symbolfiles": result = gSymbolFiles != disabledSf - of "genscript": result = contains(gGlobalOptions, optGenScript) - of "threads": result = contains(gGlobalOptions, optThreads) - of "taintmode": result = contains(gGlobalOptions, optTaintMode) - of "tlsemulation": result = contains(gGlobalOptions, optTlsEmulation) - of "implicitstatic": result = contains(gOptions, optImplicitStatic) - of "patterns": result = contains(gOptions, optPatterns) - of "experimental": result = gExperimentalMode - of "excessivestacktrace": result = contains(gGlobalOptions, optExcessiveStackTrace) - else: invalidCmdLineOption(passCmd1, switch, info) - -proc processPath(path: string, info: TLineInfo, + result = conf.options * {optNaNCheck, optInfCheck} == {optNaNCheck, optInfCheck} + of "infchecks": result = contains(conf.options, optInfCheck) + of "nanchecks": result = contains(conf.options, optNaNCheck) + of "nilchecks": result = contains(conf.options, optNilCheck) + of "objchecks": result = contains(conf.options, optObjCheck) + of "fieldchecks": result = contains(conf.options, optFieldCheck) + of "rangechecks": result = contains(conf.options, optRangeCheck) + of "boundchecks": result = contains(conf.options, optBoundsCheck) + of "overflowchecks": result = contains(conf.options, optOverflowCheck) + of "movechecks": result = contains(conf.options, optMoveCheck) + of "linedir": result = contains(conf.options, optLineDir) + of "assertions", "a": result = contains(conf.options, optAssert) + of "run", "r": result = contains(conf.globalOptions, optRun) + of "symbolfiles": result = conf.symbolFiles != disabledSf + of "genscript": result = contains(conf.globalOptions, optGenScript) + of "threads": result = contains(conf.globalOptions, optThreads) + of "taintmode": result = contains(conf.globalOptions, optTaintMode) + of "tlsemulation": result = contains(conf.globalOptions, optTlsEmulation) + of "implicitstatic": result = contains(conf.options, optImplicitStatic) + of "patterns": result = contains(conf.options, optPatterns) + of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace) + else: invalidCmdLineOption(conf, passCmd1, switch, info) + +proc processPath(conf: ConfigRef; path: string, info: TLineInfo, notRelativeToProj = false): string = - let p = if notRelativeToProj or os.isAbsolute(path) or - '$' in path: + let p = if os.isAbsolute(path) or '$' in path: path + elif notRelativeToProj: + getCurrentDir() / path else: - options.gProjectPath / path + conf.projectPath / path try: - result = pathSubs(p, info.toFullPath().splitFile().dir) + result = pathSubs(conf, p, info.toFullPath().splitFile().dir) except ValueError: - localError(info, "invalid path: " & p) + localError(conf, info, "invalid path: " & p) result = p -proc processCfgPath(path: string, info: TLineInfo): string = +proc processCfgPath(conf: ConfigRef; path: string, info: TLineInfo): string = let path = if path[0] == '"': strutils.unescape(path) else: path let basedir = info.toFullPath().splitFile().dir let p = if os.isAbsolute(path) or '$' in path: @@ -293,435 +302,466 @@ proc processCfgPath(path: string, info: TLineInfo): string = else: basedir / path try: - result = pathSubs(p, basedir) + result = pathSubs(conf, p, basedir) except ValueError: - localError(info, "invalid path: " & p) + localError(conf, info, "invalid path: " & p) result = p -proc trackDirty(arg: string, info: TLineInfo) = +const + errInvalidNumber = "$1 is not a valid number" + +proc trackDirty(conf: ConfigRef; arg: string, info: TLineInfo) = var a = arg.split(',') - if a.len != 4: localError(info, errTokenExpected, - "DIRTY_BUFFER,ORIGINAL_FILE,LINE,COLUMN") + if a.len != 4: localError(conf, info, + "DIRTY_BUFFER,ORIGINAL_FILE,LINE,COLUMN expected") var line, column: int if parseUtils.parseInt(a[2], line) <= 0: - localError(info, errInvalidNumber, a[1]) + localError(conf, info, errInvalidNumber % a[1]) if parseUtils.parseInt(a[3], column) <= 0: - localError(info, errInvalidNumber, a[2]) + localError(conf, info, errInvalidNumber % a[2]) - let dirtyOriginalIdx = a[1].fileInfoIdx - if dirtyOriginalIdx >= 0: + let dirtyOriginalIdx = fileInfoIdx(conf, a[1]) + if dirtyOriginalIdx.int32 >= 0: msgs.setDirtyFile(dirtyOriginalIdx, a[0]) gTrackPos = newLineInfo(dirtyOriginalIdx, line, column) -proc track(arg: string, info: TLineInfo) = +proc track(conf: ConfigRef; arg: string, info: TLineInfo) = var a = arg.split(',') - if a.len != 3: localError(info, errTokenExpected, "FILE,LINE,COLUMN") + if a.len != 3: localError(conf, info, "FILE,LINE,COLUMN expected") var line, column: int if parseUtils.parseInt(a[1], line) <= 0: - localError(info, errInvalidNumber, a[1]) + localError(conf, info, errInvalidNumber % a[1]) if parseUtils.parseInt(a[2], column) <= 0: - localError(info, errInvalidNumber, a[2]) - gTrackPos = newLineInfo(a[0], line, column) + localError(conf, info, errInvalidNumber % a[2]) + gTrackPos = newLineInfo(conf, a[0], line, column) -proc dynlibOverride(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = +proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = if pass in {passCmd2, passPP}: - expectArg(switch, arg, pass, info) - options.inclDynlibOverride(arg) + expectArg(conf, switch, arg, pass, info) + options.inclDynlibOverride(conf, arg) -proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; - config: ConfigRef = nil) = +proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; + conf: ConfigRef) = var theOS: TSystemOS cpu: TSystemCPU key, val: string case switch.normalize of "path", "p": - expectArg(switch, arg, pass, info) - addPath(if pass == passPP: processCfgPath(arg, info) else: processPath(arg, info), info) + expectArg(conf, switch, arg, pass, info) + addPath(conf, if pass == passPP: processCfgPath(conf, arg, info) else: processPath(conf, arg, info), info) of "nimblepath", "babelpath": # keep the old name for compat - if pass in {passCmd2, passPP} and not options.gNoNimblePath: - expectArg(switch, arg, pass, info) - var path = processPath(arg, info, notRelativeToProj=true) + if pass in {passCmd2, passPP} and optNoNimblePath notin conf.globalOptions: + expectArg(conf, switch, arg, pass, info) + var path = processPath(conf, arg, info, notRelativeToProj=true) let nimbleDir = getEnv("NIMBLE_DIR") if nimbleDir.len > 0 and pass == passPP: path = nimbleDir / "pkgs" - nimblePath(path, info) + nimblePath(conf, path, info) of "nonimblepath", "nobabelpath": - expectNoArg(switch, arg, pass, info) - disableNimblePath() + expectNoArg(conf, switch, arg, pass, info) + disableNimblePath(conf) of "excludepath": - expectArg(switch, arg, pass, info) - let path = processPath(arg, info) + expectArg(conf, switch, arg, pass, info) + let path = processPath(conf, arg, info) - options.searchPaths.keepItIf( cmpPaths(it, path) != 0 ) - options.lazyPaths.keepItIf( cmpPaths(it, path) != 0 ) + conf.searchPaths.keepItIf(cmpPaths(it, path) != 0) + conf.lazyPaths.keepItIf(cmpPaths(it, path) != 0) if (len(path) > 0) and (path[len(path) - 1] == DirSep): let strippedPath = path[0 .. (len(path) - 2)] - options.searchPaths.keepItIf( cmpPaths(it, strippedPath) != 0 ) - options.lazyPaths.keepItIf( cmpPaths(it, strippedPath) != 0 ) + conf.searchPaths.keepItIf(cmpPaths(it, strippedPath) != 0) + conf.lazyPaths.keepItIf(cmpPaths(it, strippedPath) != 0) of "nimcache": - expectArg(switch, arg, pass, info) - options.nimcacheDir = processPath(arg, info, true) + expectArg(conf, switch, arg, pass, info) + conf.nimcacheDir = processPath(conf, arg, info, true) of "out", "o": - expectArg(switch, arg, pass, info) - options.outFile = arg + expectArg(conf, switch, arg, pass, info) + conf.outFile = arg of "docseesrcurl": - expectArg(switch, arg, pass, info) - options.docSeeSrcUrl = arg + expectArg(conf, switch, arg, pass, info) + conf.docSeeSrcUrl = arg of "mainmodule", "m": discard "allow for backwards compatibility, but don't do anything" of "define", "d": - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) if {':', '='} in arg: - splitSwitch(arg, key, val, pass, info) - defineSymbol(key, val) + splitSwitch(conf, arg, key, val, pass, info) + defineSymbol(conf.symbols, key, val) else: - defineSymbol(arg) + defineSymbol(conf.symbols, arg) of "undef", "u": - expectArg(switch, arg, pass, info) - undefSymbol(arg) + expectArg(conf, switch, arg, pass, info) + undefSymbol(conf.symbols, arg) of "symbol": - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) # deprecated, do nothing of "compile": - expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: processCompile(arg) + expectArg(conf, switch, arg, pass, info) + if pass in {passCmd2, passPP}: processCompile(conf, arg) of "link": - expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: addExternalFileToLink(arg) + expectArg(conf, switch, arg, pass, info) + if pass in {passCmd2, passPP}: addExternalFileToLink(conf, arg) of "debuginfo": - expectNoArg(switch, arg, pass, info) - incl(gGlobalOptions, optCDebug) + expectNoArg(conf, switch, arg, pass, info) + incl(conf.globalOptions, optCDebug) of "embedsrc": - expectNoArg(switch, arg, pass, info) - incl(gGlobalOptions, optEmbedOrigSrc) + expectNoArg(conf, switch, arg, pass, info) + incl(conf.globalOptions, optEmbedOrigSrc) of "compileonly", "c": - expectNoArg(switch, arg, pass, info) - incl(gGlobalOptions, optCompileOnly) + expectNoArg(conf, switch, arg, pass, info) + incl(conf.globalOptions, optCompileOnly) of "nolinking": - expectNoArg(switch, arg, pass, info) - incl(gGlobalOptions, optNoLinking) + expectNoArg(conf, switch, arg, pass, info) + incl(conf.globalOptions, optNoLinking) of "nomain": - expectNoArg(switch, arg, pass, info) - incl(gGlobalOptions, optNoMain) + expectNoArg(conf, switch, arg, pass, info) + incl(conf.globalOptions, optNoMain) of "forcebuild", "f": - expectNoArg(switch, arg, pass, info) - incl(gGlobalOptions, optForceFullMake) + expectNoArg(conf, switch, arg, pass, info) + incl(conf.globalOptions, optForceFullMake) of "project": - expectNoArg(switch, arg, pass, info) - gWholeProject = true + expectNoArg(conf, switch, arg, pass, info) + incl conf.globalOptions, optWholeProject of "gc": - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) case arg.normalize of "boehm": - gSelectedGC = gcBoehm - defineSymbol("boehmgc") + conf.selectedGC = gcBoehm + defineSymbol(conf.symbols, "boehmgc") of "refc": - gSelectedGC = gcRefc + conf.selectedGC = gcRefc of "v2": - gSelectedGC = gcV2 + conf.selectedGC = gcV2 of "markandsweep": - gSelectedGC = gcMarkAndSweep - defineSymbol("gcmarkandsweep") + conf.selectedGC = gcMarkAndSweep + defineSymbol(conf.symbols, "gcmarkandsweep") of "generational": - gSelectedGC = gcGenerational - defineSymbol("gcgenerational") + conf.selectedGC = gcGenerational + defineSymbol(conf.symbols, "gcgenerational") of "go": - gSelectedGC = gcGo - defineSymbol("gogc") + conf.selectedGC = gcGo + defineSymbol(conf.symbols, "gogc") of "none": - gSelectedGC = gcNone - defineSymbol("nogc") + conf.selectedGC = gcNone + defineSymbol(conf.symbols, "nogc") of "stack", "regions": - gSelectedGC= gcRegions - defineSymbol("gcregions") - else: localError(info, errNoneBoehmRefcExpectedButXFound, arg) + conf.selectedGC= gcRegions + defineSymbol(conf.symbols, "gcregions") + else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg) of "warnings", "w": - if processOnOffSwitchOrList({optWarns}, arg, pass, info): listWarnings() - of "warning": processSpecificNote(arg, wWarning, pass, info, switch) - of "hint": processSpecificNote(arg, wHint, pass, info, switch) + if processOnOffSwitchOrList(conf, {optWarns}, arg, pass, info): listWarnings(conf) + of "warning": processSpecificNote(arg, wWarning, pass, info, switch, conf) + of "hint": processSpecificNote(arg, wHint, pass, info, switch, conf) of "hints": - if processOnOffSwitchOrList({optHints}, arg, pass, info): listHints() - of "threadanalysis": processOnOffSwitchG({optThreadAnalysis}, arg, pass, info) - of "stacktrace": processOnOffSwitch({optStackTrace}, arg, pass, info) - of "excessivestacktrace": processOnOffSwitchG({optExcessiveStackTrace}, arg, pass, info) - of "linetrace": processOnOffSwitch({optLineTrace}, arg, pass, info) + if processOnOffSwitchOrList(conf, {optHints}, arg, pass, info): listHints(conf) + of "threadanalysis": processOnOffSwitchG(conf, {optThreadAnalysis}, arg, pass, info) + of "stacktrace": processOnOffSwitch(conf, {optStackTrace}, arg, pass, info) + of "excessivestacktrace": processOnOffSwitchG(conf, {optExcessiveStackTrace}, arg, pass, info) + of "linetrace": processOnOffSwitch(conf, {optLineTrace}, arg, pass, info) of "debugger": case arg.normalize of "on", "endb": - gOptions.incl optEndb - defineSymbol("endb") + conf.options.incl optEndb + defineSymbol(conf.symbols, "endb") of "off": - gOptions.excl optEndb - undefSymbol("endb") + conf.options.excl optEndb + undefSymbol(conf.symbols, "endb") of "native", "gdb": - incl(gGlobalOptions, optCDebug) - gOptions = gOptions + {optLineDir} - {optEndb} - defineSymbol("nimTypeNames", nil) # type names are used in gdb pretty printing - undefSymbol("endb") + incl(conf.globalOptions, optCDebug) + conf.options = conf.options + {optLineDir} - {optEndb} + defineSymbol(conf.symbols, "nimTypeNames") # type names are used in gdb pretty printing + undefSymbol(conf.symbols, "endb") else: - localError(info, "expected endb|gdb but found " & arg) + localError(conf, info, "expected endb|gdb but found " & arg) of "profiler": - processOnOffSwitch({optProfiler}, arg, pass, info) - if optProfiler in gOptions: defineSymbol("profiler") - else: undefSymbol("profiler") + processOnOffSwitch(conf, {optProfiler}, arg, pass, info) + if optProfiler in conf.options: defineSymbol(conf.symbols, "profiler") + else: undefSymbol(conf.symbols, "profiler") of "memtracker": - processOnOffSwitch({optMemTracker}, arg, pass, info) - if optMemTracker in gOptions: defineSymbol("memtracker") - else: undefSymbol("memtracker") - of "checks", "x": processOnOffSwitch(ChecksOptions, arg, pass, info) + processOnOffSwitch(conf, {optMemTracker}, arg, pass, info) + if optMemTracker in conf.options: defineSymbol(conf.symbols, "memtracker") + else: undefSymbol(conf.symbols, "memtracker") + of "hotcodereloading": + processOnOffSwitch(conf, {optHotCodeReloading}, arg, pass, info) + if optHotCodeReloading in conf.options: defineSymbol(conf.symbols, "hotcodereloading") + else: undefSymbol(conf.symbols, "hotcodereloading") + of "oldnewlines": + case arg.normalize + of "on": + conf.oldNewlines = true + defineSymbol(conf.symbols, "nimOldNewlines") + of "off": + conf.oldNewlines = false + undefSymbol(conf.symbols, "nimOldNewlines") + else: + localError(conf, info, errOnOrOffExpectedButXFound % arg) + of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info) + of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info) of "floatchecks": - processOnOffSwitch({optNaNCheck, optInfCheck}, arg, pass, info) - of "infchecks": processOnOffSwitch({optInfCheck}, arg, pass, info) - of "nanchecks": processOnOffSwitch({optNaNCheck}, arg, pass, info) - of "nilchecks": processOnOffSwitch({optNilCheck}, arg, pass, info) - of "objchecks": processOnOffSwitch({optObjCheck}, arg, pass, info) - of "fieldchecks": processOnOffSwitch({optFieldCheck}, arg, pass, info) - of "rangechecks": processOnOffSwitch({optRangeCheck}, arg, pass, info) - of "boundchecks": processOnOffSwitch({optBoundsCheck}, arg, pass, info) - of "overflowchecks": processOnOffSwitch({optOverflowCheck}, arg, pass, info) - of "linedir": processOnOffSwitch({optLineDir}, arg, pass, info) - of "assertions", "a": processOnOffSwitch({optAssert}, arg, pass, info) - of "deadcodeelim": processOnOffSwitchG({optDeadCodeElim}, arg, pass, info) + processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info) + of "infchecks": processOnOffSwitch(conf, {optInfCheck}, arg, pass, info) + of "nanchecks": processOnOffSwitch(conf, {optNaNCheck}, arg, pass, info) + of "nilchecks": processOnOffSwitch(conf, {optNilCheck}, arg, pass, info) + of "objchecks": processOnOffSwitch(conf, {optObjCheck}, arg, pass, info) + of "fieldchecks": processOnOffSwitch(conf, {optFieldCheck}, arg, pass, info) + of "rangechecks": processOnOffSwitch(conf, {optRangeCheck}, arg, pass, info) + of "boundchecks": processOnOffSwitch(conf, {optBoundsCheck}, arg, pass, info) + of "overflowchecks": processOnOffSwitch(conf, {optOverflowCheck}, arg, pass, info) + of "movechecks": processOnOffSwitch(conf, {optMoveCheck}, arg, pass, info) + of "linedir": processOnOffSwitch(conf, {optLineDir}, arg, pass, info) + of "assertions", "a": processOnOffSwitch(conf, {optAssert}, arg, pass, info) + of "deadcodeelim": discard # deprecated, dead code elim always on of "threads": - processOnOffSwitchG({optThreads}, arg, pass, info) - #if optThreads in gGlobalOptions: incl(gNotes, warnGcUnsafe) - of "tlsemulation": processOnOffSwitchG({optTlsEmulation}, arg, pass, info) - of "taintmode": processOnOffSwitchG({optTaintMode}, arg, pass, info) + processOnOffSwitchG(conf, {optThreads}, arg, pass, info) + #if optThreads in conf.globalOptions: incl(conf.notes, warnGcUnsafe) + of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info) + of "taintmode": processOnOffSwitchG(conf, {optTaintMode}, arg, pass, info) of "implicitstatic": - processOnOffSwitch({optImplicitStatic}, arg, pass, info) + processOnOffSwitch(conf, {optImplicitStatic}, arg, pass, info) of "patterns": - processOnOffSwitch({optPatterns}, arg, pass, info) + processOnOffSwitch(conf, {optPatterns}, arg, pass, info) of "opt": - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) case arg.normalize of "speed": - incl(gOptions, optOptimizeSpeed) - excl(gOptions, optOptimizeSize) + incl(conf.options, optOptimizeSpeed) + excl(conf.options, optOptimizeSize) of "size": - excl(gOptions, optOptimizeSpeed) - incl(gOptions, optOptimizeSize) + excl(conf.options, optOptimizeSpeed) + incl(conf.options, optOptimizeSize) of "none": - excl(gOptions, optOptimizeSpeed) - excl(gOptions, optOptimizeSize) - else: localError(info, errNoneSpeedOrSizeExpectedButXFound, arg) + excl(conf.options, optOptimizeSpeed) + excl(conf.options, optOptimizeSize) + else: localError(conf, info, errNoneSpeedOrSizeExpectedButXFound % arg) of "app": - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) case arg.normalize of "gui": - incl(gGlobalOptions, optGenGuiApp) - defineSymbol("executable") - defineSymbol("guiapp") + incl(conf.globalOptions, optGenGuiApp) + defineSymbol(conf.symbols, "executable") + defineSymbol(conf.symbols, "guiapp") of "console": - excl(gGlobalOptions, optGenGuiApp) - defineSymbol("executable") - defineSymbol("consoleapp") + excl(conf.globalOptions, optGenGuiApp) + defineSymbol(conf.symbols, "executable") + defineSymbol(conf.symbols, "consoleapp") of "lib": - incl(gGlobalOptions, optGenDynLib) - excl(gGlobalOptions, optGenGuiApp) - defineSymbol("library") - defineSymbol("dll") + incl(conf.globalOptions, optGenDynLib) + excl(conf.globalOptions, optGenGuiApp) + defineSymbol(conf.symbols, "library") + defineSymbol(conf.symbols, "dll") of "staticlib": - incl(gGlobalOptions, optGenStaticLib) - excl(gGlobalOptions, optGenGuiApp) - defineSymbol("library") - defineSymbol("staticlib") - else: localError(info, errGuiConsoleOrLibExpectedButXFound, arg) + incl(conf.globalOptions, optGenStaticLib) + excl(conf.globalOptions, optGenGuiApp) + defineSymbol(conf.symbols, "library") + defineSymbol(conf.symbols, "staticlib") + else: localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg) of "passc", "t": - expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: extccomp.addCompileOptionCmd(arg) + expectArg(conf, switch, arg, pass, info) + if pass in {passCmd2, passPP}: extccomp.addCompileOptionCmd(conf, arg) of "passl", "l": - expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: extccomp.addLinkOptionCmd(arg) + expectArg(conf, switch, arg, pass, info) + if pass in {passCmd2, passPP}: extccomp.addLinkOptionCmd(conf, arg) of "cincludes": - expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: cIncludes.add arg.processPath(info) + expectArg(conf, switch, arg, pass, info) + if pass in {passCmd2, passPP}: cIncludes.add processPath(conf, arg, info) of "clibdir": - expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: cLibs.add arg.processPath(info) + expectArg(conf, switch, arg, pass, info) + if pass in {passCmd2, passPP}: cLibs.add processPath(conf, arg, info) of "clib": - expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: cLinkedLibs.add arg.processPath(info) + expectArg(conf, switch, arg, pass, info) + if pass in {passCmd2, passPP}: cLinkedLibs.add processPath(conf, arg, info) of "header": - if config != nil: config.headerFile = arg - incl(gGlobalOptions, optGenIndex) + if conf != nil: conf.headerFile = arg + incl(conf.globalOptions, optGenIndex) of "index": - processOnOffSwitchG({optGenIndex}, arg, pass, info) + processOnOffSwitchG(conf, {optGenIndex}, arg, pass, info) of "import": - expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: implicitImports.add arg + expectArg(conf, switch, arg, pass, info) + if pass in {passCmd2, passPP}: conf.implicitImports.add arg of "include": - expectArg(switch, arg, pass, info) - if pass in {passCmd2, passPP}: implicitIncludes.add arg + expectArg(conf, switch, arg, pass, info) + if pass in {passCmd2, passPP}: conf.implicitIncludes.add arg of "listcmd": - expectNoArg(switch, arg, pass, info) - incl(gGlobalOptions, optListCmd) + expectNoArg(conf, switch, arg, pass, info) + incl(conf.globalOptions, optListCmd) of "genmapping": - expectNoArg(switch, arg, pass, info) - incl(gGlobalOptions, optGenMapping) + expectNoArg(conf, switch, arg, pass, info) + incl(conf.globalOptions, optGenMapping) of "os": - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) if pass in {passCmd1, passPP}: theOS = platform.nameToOS(arg) - if theOS == osNone: localError(info, errUnknownOS, arg) + if theOS == osNone: localError(conf, info, "unknown OS: '$1'" % arg) elif theOS != platform.hostOS: setTarget(theOS, targetCPU) of "cpu": - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) if pass in {passCmd1, passPP}: cpu = platform.nameToCPU(arg) - if cpu == cpuNone: localError(info, errUnknownCPU, arg) + if cpu == cpuNone: localError(conf, info, "unknown CPU: '$1'" % arg) elif cpu != platform.hostCPU: setTarget(targetOS, cpu) of "run", "r": - expectNoArg(switch, arg, pass, info) - incl(gGlobalOptions, optRun) + expectNoArg(conf, switch, arg, pass, info) + incl(conf.globalOptions, optRun) of "verbosity": - expectArg(switch, arg, pass, info) - gVerbosity = parseInt(arg) - gNotes = NotesVerbosity[gVerbosity] - incl(gNotes, enableNotes) - excl(gNotes, disableNotes) - gMainPackageNotes = gNotes + expectArg(conf, switch, arg, pass, info) + conf.verbosity = parseInt(arg) + conf.notes = NotesVerbosity[conf.verbosity] + incl(conf.notes, conf.enableNotes) + excl(conf.notes, conf.disableNotes) + conf.mainPackageNotes = conf.notes of "parallelbuild": - expectArg(switch, arg, pass, info) - gNumberOfProcessors = parseInt(arg) + expectArg(conf, switch, arg, pass, info) + conf.numberOfProcessors = parseInt(arg) of "version", "v": - expectNoArg(switch, arg, pass, info) - writeVersionInfo(pass) + expectNoArg(conf, switch, arg, pass, info) + writeVersionInfo(conf, pass) of "advanced": - expectNoArg(switch, arg, pass, info) - writeAdvancedUsage(pass) + expectNoArg(conf, switch, arg, pass, info) + writeAdvancedUsage(conf, pass) + of "fullhelp": + expectNoArg(conf, switch, arg, pass, info) + writeFullhelp(conf, pass) of "help", "h": - expectNoArg(switch, arg, pass, info) - helpOnError(pass) + expectNoArg(conf, switch, arg, pass, info) + helpOnError(conf, pass) of "symbolfiles": case arg.normalize - of "on": gSymbolFiles = enabledSf - of "off": gSymbolFiles = disabledSf - of "writeonly": gSymbolFiles = writeOnlySf - of "readonly": gSymbolFiles = readOnlySf - else: localError(info, errOnOrOffExpectedButXFound, arg) + of "on": conf.symbolFiles = enabledSf + of "off": conf.symbolFiles = disabledSf + of "writeonly": conf.symbolFiles = writeOnlySf + of "readonly": conf.symbolFiles = readOnlySf + of "v2": conf.symbolFiles = v2Sf + else: localError(conf, info, "invalid option for --symbolFiles: " & arg) of "skipcfg": - expectNoArg(switch, arg, pass, info) - incl(gGlobalOptions, optSkipConfigFile) + expectNoArg(conf, switch, arg, pass, info) + incl(conf.globalOptions, optSkipConfigFile) of "skipprojcfg": - expectNoArg(switch, arg, pass, info) - incl(gGlobalOptions, optSkipProjConfigFile) + expectNoArg(conf, switch, arg, pass, info) + incl(conf.globalOptions, optSkipProjConfigFile) of "skipusercfg": - expectNoArg(switch, arg, pass, info) - incl(gGlobalOptions, optSkipUserConfigFile) + expectNoArg(conf, switch, arg, pass, info) + incl(conf.globalOptions, optSkipUserConfigFile) of "skipparentcfg": - expectNoArg(switch, arg, pass, info) - incl(gGlobalOptions, optSkipParentConfigFiles) + expectNoArg(conf, switch, arg, pass, info) + incl(conf.globalOptions, optSkipParentConfigFiles) of "genscript", "gendeps": - expectNoArg(switch, arg, pass, info) - incl(gGlobalOptions, optGenScript) - of "colors": processOnOffSwitchG({optUseColors}, arg, pass, info) + expectNoArg(conf, switch, arg, pass, info) + incl(conf.globalOptions, optGenScript) + incl(conf.globalOptions, optCompileOnly) + of "colors": processOnOffSwitchG(conf, {optUseColors}, arg, pass, info) of "lib": - expectArg(switch, arg, pass, info) - libpath = processPath(arg, info, notRelativeToProj=true) + expectArg(conf, switch, arg, pass, info) + conf.libpath = processPath(conf, arg, info, notRelativeToProj=true) of "putenv": - expectArg(switch, arg, pass, info) - splitSwitch(arg, key, val, pass, info) + expectArg(conf, switch, arg, pass, info) + splitSwitch(conf, arg, key, val, pass, info) os.putEnv(key, val) of "cc": - expectArg(switch, arg, pass, info) - setCC(arg) + expectArg(conf, switch, arg, pass, info) + setCC(conf, arg, info) of "track": - expectArg(switch, arg, pass, info) - track(arg, info) + expectArg(conf, switch, arg, pass, info) + track(conf, arg, info) of "trackdirty": - expectArg(switch, arg, pass, info) - trackDirty(arg, info) + expectArg(conf, switch, arg, pass, info) + trackDirty(conf, arg, info) of "suggest": - expectNoArg(switch, arg, pass, info) - gIdeCmd = ideSug + expectNoArg(conf, switch, arg, pass, info) + conf.ideCmd = ideSug of "def": - expectNoArg(switch, arg, pass, info) - gIdeCmd = ideDef + expectNoArg(conf, switch, arg, pass, info) + conf.ideCmd = ideDef of "eval": - expectArg(switch, arg, pass, info) - gEvalExpr = arg + expectArg(conf, switch, arg, pass, info) + conf.evalExpr = arg of "context": - expectNoArg(switch, arg, pass, info) - gIdeCmd = ideCon + expectNoArg(conf, switch, arg, pass, info) + conf.ideCmd = ideCon of "usages": - expectNoArg(switch, arg, pass, info) - gIdeCmd = ideUse + expectNoArg(conf, switch, arg, pass, info) + conf.ideCmd = ideUse of "stdout": - expectNoArg(switch, arg, pass, info) - incl(gGlobalOptions, optStdout) + expectNoArg(conf, switch, arg, pass, info) + incl(conf.globalOptions, optStdout) of "listfullpaths": - expectNoArg(switch, arg, pass, info) - gListFullPaths = true + expectNoArg(conf, switch, arg, pass, info) + incl conf.globalOptions, optListFullPaths of "dynliboverride": - dynlibOverride(switch, arg, pass, info) + dynlibOverride(conf, switch, arg, pass, info) of "dynliboverrideall": - expectNoArg(switch, arg, pass, info) - gDynlibOverrideAll = true + expectNoArg(conf, switch, arg, pass, info) + incl conf.globalOptions, optDynlibOverrideAll of "cs": # only supported for compatibility. Does nothing. - expectArg(switch, arg, pass, info) + expectArg(conf, switch, arg, pass, info) of "experimental": - expectNoArg(switch, arg, pass, info) - gExperimentalMode = true + if arg.len == 0: + conf.features.incl oldExperimentalFeatures + else: + try: + conf.features.incl parseEnum[Feature](arg) + except ValueError: + localError(conf, info, "unknown experimental feature") of "nocppexceptions": - expectNoArg(switch, arg, pass, info) - incl(gGlobalOptions, optNoCppExceptions) - defineSymbol("noCppExceptions") + expectNoArg(conf, switch, arg, pass, info) + incl(conf.globalOptions, optNoCppExceptions) + defineSymbol(conf.symbols, "noCppExceptions") of "cppdefine": - expectArg(switch, arg, pass, info) - if config != nil: - config.cppDefine(arg) + expectArg(conf, switch, arg, pass, info) + if conf != nil: + conf.cppDefine(arg) of "newruntime": - expectNoArg(switch, arg, pass, info) - newDestructors = true - defineSymbol("nimNewRuntime") + expectNoArg(conf, switch, arg, pass, info) + doAssert(conf != nil) + incl(conf.features, destructor) + defineSymbol(conf.symbols, "nimNewRuntime") + of "cppcompiletonamespace": + expectNoArg(conf, switch, arg, pass, info) + incl conf.globalOptions, optUseNimNamespace + defineSymbol(conf.symbols, "cppCompileToNamespace") else: - if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg) - else: invalidCmdLineOption(pass, switch, info) + if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg) + else: invalidCmdLineOption(conf, pass, switch, info) -proc processCommand(switch: string, pass: TCmdLinePass) = - var cmd, arg: string - splitSwitch(switch, cmd, arg, pass, gCmdLineInfo) - processSwitch(cmd, arg, pass, gCmdLineInfo) +template gCmdLineInfo*(): untyped = newLineInfo(config, "command line", 1, 1) +proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef) = + var cmd, arg: string + splitSwitch(config, switch, cmd, arg, pass, gCmdLineInfo) + processSwitch(cmd, arg, pass, gCmdLineInfo, config) -var - arguments* = "" - # the arguments to be passed to the program that - # should be run -proc processSwitch*(pass: TCmdLinePass; p: OptParser) = +proc processSwitch*(pass: TCmdLinePass; p: OptParser; config: ConfigRef) = # hint[X]:off is parsed as (p.key = "hint[X]", p.val = "off") # we fix this here var bracketLe = strutils.find(p.key, '[') if bracketLe >= 0: var key = substr(p.key, 0, bracketLe - 1) var val = substr(p.key, bracketLe + 1) & ':' & p.val - processSwitch(key, val, pass, gCmdLineInfo) + processSwitch(key, val, pass, gCmdLineInfo, config) else: - processSwitch(p.key, p.val, pass, gCmdLineInfo) + processSwitch(p.key, p.val, pass, gCmdLineInfo, config) proc processArgument*(pass: TCmdLinePass; p: OptParser; - argsCount: var int): bool = + argsCount: var int; config: ConfigRef): bool = if argsCount == 0: # nim filename.nims is the same as "nim e filename.nims": if p.key.endswith(".nims"): - options.command = "e" - options.gProjectName = unixToNativePath(p.key) - arguments = cmdLineRest(p) + config.command = "e" + config.projectName = unixToNativePath(p.key) + config.arguments = cmdLineRest(p) result = true elif pass != passCmd2: - options.command = p.key + config.command = p.key else: - if pass == passCmd1: options.commandArgs.add p.key + if pass == passCmd1: config.commandArgs.add p.key if argsCount == 1: # support UNIX style filenames everywhere for portable build scripts: - options.gProjectName = unixToNativePath(p.key) - arguments = cmdLineRest(p) + config.projectName = unixToNativePath(p.key) + config.arguments = cmdLineRest(p) result = true inc argsCount diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index a52214e73..773c0faf9 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -12,78 +12,30 @@ import strtabs, platform, strutils, idents -# We need to use a StringTableRef here as defined symbols are always guaranteed -# to be style insensitive. Otherwise hell would break lose. -var gSymbols: StringTableRef - const catNone = "false" -proc defineSymbol*(symbol: string, value: string = "true") = - gSymbols[symbol] = value - -proc undefSymbol*(symbol: string) = - gSymbols[symbol] = catNone - -proc isDefined*(symbol: string): bool = - if gSymbols.hasKey(symbol): - 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, osDragonfly, osMacosx, - osAndroid} - of "linux": - result = targetOS in {osLinux, osAndroid} - of "bsd": - result = targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly} - 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, - osDragonfly, osMacosx} - else: discard - -proc isDefined*(symbol: PIdent): bool = isDefined(symbol.s) +proc defineSymbol*(symbols: StringTableRef; symbol: string, value: string = "true") = + symbols[symbol] = value -proc lookupSymbol*(symbol: string): string = - result = if isDefined(symbol): gSymbols[symbol] else: nil +proc undefSymbol*(symbols: StringTableRef; symbol: string) = + symbols[symbol] = catNone -proc lookupSymbol*(symbol: PIdent): string = lookupSymbol(symbol.s) +#proc lookupSymbol*(symbols: StringTableRef; symbol: string): string = +# result = if isDefined(symbol): gSymbols[symbol] else: nil -iterator definedSymbolNames*: string = - for key, val in pairs(gSymbols): +iterator definedSymbolNames*(symbols: StringTableRef): string = + for key, val in pairs(symbols): if val != catNone: yield key -proc countDefinedSymbols*(): int = +proc countDefinedSymbols*(symbols: StringTableRef): int = result = 0 - for key, val in pairs(gSymbols): + for key, val in pairs(symbols): if val != catNone: inc(result) -proc initDefines*() = - gSymbols = newStringTable(modeStyleInsensitive) - defineSymbol("nimrod") # 'nimrod' is always defined +proc initDefines*(symbols: StringTableRef) = # for bootstrapping purposes and old code: + template defineSymbol(s) = symbols.defineSymbol(s) defineSymbol("nimhygiene") defineSymbol("niminheritable") defineSymbol("nimmixin") @@ -112,3 +64,9 @@ proc initDefines*() = defineSymbol("nimNewRoof") defineSymbol("nimHasRunnableExamples") defineSymbol("nimNewDot") + defineSymbol("nimHasNilChecks") + defineSymbol("nimSymKind") + defineSymbol("nimVmEqIdent") + defineSymbol("nimNoNil") + defineSymbol("nimNoZeroTerminator") + defineSymbol("nimNotNil") diff --git a/compiler/configuration.nim b/compiler/configuration.nim new file mode 100644 index 000000000..f9f0e623c --- /dev/null +++ b/compiler/configuration.nim @@ -0,0 +1,360 @@ +# +# +# The Nim Compiler +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module contains the rather excessive configuration object that +## needs to be passed around to everything so that the compiler becomes +## more useful as a library. + +import tables + +const + explanationsBaseUrl* = "https://nim-lang.org/docs/manual" + +type + TMsgKind* = enum + errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile, + errXExpected, + errGridTableNotImplemented, + errGeneralParseError, + errNewSectionExpected, + errInvalidDirectiveX, + errGenerated, + errUser, + warnCannotOpenFile, + warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, + warnDeprecated, warnConfigDeprecated, + warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, + warnUnknownSubstitutionX, warnLanguageXNotSupported, + warnFieldXNotSupported, warnCommentXIgnored, + warnTypelessParam, + warnUseBase, warnWriteToForeignHeap, warnUnsafeCode, + warnEachIdentIsTuple, warnShadowIdent, + warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, + warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, + warnInconsistentSpacing, warnUser, + hintSuccess, hintSuccessX, + hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, + hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, + hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, + hintConditionAlwaysTrue, hintName, hintPattern, + hintExecuting, hintLinking, hintDependency, + hintSource, hintPerformance, hintStackTrace, hintGCStats, + hintUser, hintUserRaw + +const + MsgKindToStr*: array[TMsgKind, string] = [ + errUnknown: "unknown error", + errInternal: "internal error: $1", + errIllFormedAstX: "illformed AST: $1", + errCannotOpenFile: "cannot open '$1'", + errXExpected: "'$1' expected", + errGridTableNotImplemented: "grid table is not implemented", + errGeneralParseError: "general parse error", + errNewSectionExpected: "new section expected", + errInvalidDirectiveX: "invalid directive: '$1'", + errGenerated: "$1", + errUser: "$1", + warnCannotOpenFile: "cannot open '$1'", + warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored", + warnXIsNeverRead: "'$1' is never read", + warnXmightNotBeenInit: "'$1' might not have been initialized", + warnDeprecated: "$1 is deprecated", + warnConfigDeprecated: "config file '$1' is deprecated", + warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)", + warnUnknownMagic: "unknown magic '$1' might crash the compiler", + warnRedefinitionOfLabel: "redefinition of label '$1'", + warnUnknownSubstitutionX: "unknown substitution '$1'", + warnLanguageXNotSupported: "language '$1' not supported", + warnFieldXNotSupported: "field '$1' not supported", + warnCommentXIgnored: "comment '$1' ignored", + warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'", + warnUseBase: "use {.base.} for base methods; baseless methods are deprecated", + warnWriteToForeignHeap: "write to foreign heap", + warnUnsafeCode: "unsafe code: '$1'", + warnEachIdentIsTuple: "each identifier is a tuple", + warnShadowIdent: "shadowed identifier: '$1'", + warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.", + warnProveField: "cannot prove that field '$1' is accessible", + warnProveIndex: "cannot prove index '$1' is valid", + warnGcUnsafe: "not GC-safe: '$1'", + warnGcUnsafe2: "$1", + warnUninit: "'$1' might not have been initialized", + warnGcMem: "'$1' uses GC'ed memory", + warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.", + warnLockLevel: "$1", + warnResultShadowed: "Special variable 'result' is shadowed.", + warnInconsistentSpacing: "Number of spaces around '$#' is not consistent", + warnUser: "$1", + hintSuccess: "operation successful", + hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)", + hintLineTooLong: "line too long", + hintXDeclaredButNotUsed: "'$1' is declared but not used", + hintConvToBaseNotNeeded: "conversion to base object is not needed", + hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless", + hintExprAlwaysX: "expression evaluates always to '$1'", + hintQuitCalled: "quit() called", + hintProcessing: "$1", + hintCodeBegin: "generated code listing:", + hintCodeEnd: "end of listing", + hintConf: "used config file '$1'", + hintPath: "added path: '$1'", + hintConditionAlwaysTrue: "condition is always true: '$1'", + hintName: "name should be: '$1'", + hintPattern: "$1", + hintExecuting: "$1", + hintLinking: "", + hintDependency: "$1", + hintSource: "$1", + hintPerformance: "$1", + hintStackTrace: "$1", + hintGCStats: "$1", + hintUser: "$1", + hintUserRaw: "$1"] + +const + WarningsToStr* = ["CannotOpenFile", "OctalEscape", + "XIsNeverRead", "XmightNotBeenInit", + "Deprecated", "ConfigDeprecated", + "SmallLshouldNotBeUsed", "UnknownMagic", + "RedefinitionOfLabel", "UnknownSubstitutionX", + "LanguageXNotSupported", "FieldXNotSupported", + "CommentXIgnored", + "TypelessParam", "UseBase", "WriteToForeignHeap", + "UnsafeCode", "EachIdentIsTuple", "ShadowIdent", + "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", + "GcMem", "Destructor", "LockLevel", "ResultShadowed", + "Spacing", "User"] + + HintsToStr* = ["Success", "SuccessX", "LineTooLong", + "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", + "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", + "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency", + "Source", "Performance", "StackTrace", "GCStats", + "User", "UserRaw"] + +const + fatalMin* = errUnknown + fatalMax* = errInternal + errMin* = errUnknown + errMax* = errUser + warnMin* = warnCannotOpenFile + warnMax* = pred(hintSuccess) + hintMin* = hintSuccess + hintMax* = high(TMsgKind) + +static: + doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1 + doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1 + +type + TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints + TNoteKinds* = set[TNoteKind] + +const + NotesVerbosity*: array[0..3, TNoteKinds] = [ + {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, + warnProveField, warnProveIndex, + warnGcUnsafe, + hintSuccessX, hintPath, hintConf, + hintProcessing, hintPattern, + hintDependency, + hintExecuting, hintLinking, + hintCodeBegin, hintCodeEnd, + hintSource, hintStackTrace, + hintGCStats}, + {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, + warnProveField, warnProveIndex, + warnGcUnsafe, + hintPath, + hintDependency, + hintCodeBegin, hintCodeEnd, + hintSource, hintStackTrace, + hintGCStats}, + {low(TNoteKind)..high(TNoteKind)} - {hintStackTrace, warnUninit}, + {low(TNoteKind)..high(TNoteKind)}] + +const + errXMustBeCompileTime* = "'$1' can only be used in compile-time context" + errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected" + +#[ +errStringLiteralExpected: "string literal expected", +errIntLiteralExpected: "integer literal expected", +errIdentifierExpected: "identifier expected, but found '$1'", +errNewlineExpected: "newline expected, but found '$1'", +errInvalidModuleName: "invalid module name: '$1'", +errOnOrOffExpected: "'on' or 'off' expected", +errNoneSpeedOrSizeExpected: "'none', 'speed' or 'size' expected", +errInvalidPragma: "invalid pragma", +errUnknownPragma: "unknown pragma: '$1'", +errAtPopWithoutPush: "'pop' without a 'push' pragma", +errEmptyAsm: "empty asm statement", +errInvalidIndentation: "invalid indentation", + +errNoReturnWithReturnTypeNotAllowed: "routines with NoReturn pragma are not allowed to have return type", +errAttemptToRedefine: , +errStmtInvalidAfterReturn: "statement not allowed after 'return', 'break', 'raise', 'continue' or proc call with noreturn pragma", +errStmtExpected: "statement expected", +errInvalidLabel: "'$1' is no label", +errInvalidCmdLineOption: "invalid command line option: '$1'", +errCmdLineArgExpected: "argument for command line option expected: '$1'", +errCmdLineNoArgExpected: "invalid argument for command line option: '$1'", +errInvalidVarSubstitution: "invalid variable substitution in '$1'", +errUnknownVar: "unknown variable: '$1'", +errUnknownCcompiler: "unknown C compiler: '$1'", +errOnOrOffExpectedButXFound: "'on' or 'off' expected, but '$1' found", +errOnOffOrListExpectedButXFound: "'on', 'off' or 'list' expected, but '$1' found", +errGenOutExpectedButXFound: "'c', 'c++' or 'yaml' expected, but '$1' found", +, +errInvalidMultipleAsgn: "multiple assignment is not allowed", +errColonOrEqualsExpected: "':' or '=' expected, but found '$1'", +errUndeclaredField: "undeclared field: '$1'", +errUndeclaredRoutine: "attempting to call undeclared routine: '$1'", +errUseQualifier: "ambiguous identifier: '$1' -- use a qualifier", +errTypeExpected: "type expected", +errSystemNeeds: "system module needs '$1'", +errExecutionOfProgramFailed: "execution of an external program failed: '$1'", +errNotOverloadable: , +errInvalidArgForX: "invalid argument for '$1'", +errStmtHasNoEffect: "statement has no effect", +, +errXExpectsArrayType: "'$1' expects an array type", +errIteratorCannotBeInstantiated: "'$1' cannot be instantiated because its body has not been compiled yet", +errExprXAmbiguous: "expression '$1' ambiguous in this context", +errConstantDivisionByZero: , +errOrdinalOrFloatTypeExpected: "ordinal or float type expected", +errOverOrUnderflow: , +errCannotEvalXBecauseIncompletelyDefined: , +errChrExpectsRange0_255: "'chr' expects an int in the range 0..255", +errDynlibRequiresExportc: "'dynlib' requires 'exportc'", +errNilAccess: "attempt to access a nil address", +errIndexOutOfBounds: "index out of bounds", +errIndexTypesDoNotMatch: "index types do not match", +errBracketsInvalidForType: "'[]' operator invalid for this type", +errValueOutOfSetBounds: "value out of set bounds", +errFieldNotInit: "field '$1' not initialized", +errExprXCannotBeCalled: "expression '$1' cannot be called", +errExprHasNoType: "expression has no type", +errExprXHasNoType:, +errCastNotInSafeMode: "'cast' not allowed in safe mode", +errExprCannotBeCastToX: , +errCommaOrParRiExpected: "',' or ')' expected", +errCurlyLeOrParLeExpected: "'{' or '(' expected", +errSectionExpected: "section ('type', 'proc', etc.) expected", +errRangeExpected: "range expected", +errMagicOnlyInSystem: "'magic' only allowed in system module", +errPowerOfTwoExpected: "power of two expected", +errStringMayNotBeEmpty: "string literal may not be empty", +errCallConvExpected: "calling convention expected", +errProcOnlyOneCallConv: "a proc can only have one calling convention", +errSymbolMustBeImported: "symbol must be imported if 'lib' pragma is used", +errExprMustBeBool: "expression must be of type 'bool'", +errConstExprExpected: "constant expression expected", +errDuplicateCaseLabel: "duplicate case label", +errRangeIsEmpty: "range is empty", +, +errSelectorMustBeOrdinal: "selector must be of an ordinal type", +errOrdXMustNotBeNegative: "ord($1) must not be negative", +errLenXinvalid: "len($1) must be less than 32768", +errTypeXhasUnknownSize: "type '$1' has unknown size", +errConstNeedsConstExpr: "a constant can only be initialized with a constant expression", +errConstNeedsValue: "a constant needs a value", +errResultCannotBeOpenArray: "the result type cannot be on open array", +errSizeTooBig: "computing the type's size produced an overflow", +errInheritanceOnlyWithEnums: "inheritance only works with an enum", +errIllegalRecursionInTypeX:, +errCannotInstantiateX: "cannot instantiate: '$1'", +errTypeMismatch: "type mismatch: got <", +errButExpected: "but expected one of: ", +errButExpectedX: "but expected '$1'", +errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3", +errWrongNumberOfArguments: "wrong number of arguments", +errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'", +errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters", +errXCannotBePassedToProcVar: , +, +errImplOfXexpected: , + +errIllegalConvFromXtoY: , +errCannotBindXTwice: "cannot bind parameter '$1' twice", +errInvalidOrderInArrayConstructor: , +errInvalidOrderInEnumX: "invalid order in enum '$1'", +errEnumXHasHoles: "enum '$1' has holes", +errExceptExpected: "'except' or 'finally' expected", +errInvalidTry: "after catch all 'except' or 'finally' no section may follow", +errOptionExpected: , +errXisNoLabel: "'$1' is not a label", +errNotAllCasesCovered: "not all cases are covered", +errUnknownSubstitionVar: "unknown substitution variable: '$1'", +errComplexStmtRequiresInd: "complex statement requires indentation", +errXisNotCallable: "'$1' is not callable", +errNoPragmasAllowedForX: "no pragmas allowed for $1", +, +errInvalidParamKindX: "invalid param kind: '$1'", +errDefaultArgumentInvalid: "default argument invalid", +errNamedParamHasToBeIdent: "named parameter has to be an identifier", +errNoReturnTypeForX: "no return type allowed for $1", +errConvNeedsOneArg: "a type conversion needs exactly one argument", +errInvalidPragmaX: , +errXNotAllowedHere: "$1 not allowed here", +errXisNoType: "invalid type: '$1'", +errCircumNeedsPointer: "'[]' needs a pointer or reference type", +errInvalidExpression: "invalid expression", +errInvalidExpressionX: "invalid expression: '$1'", +errEnumHasNoValueX: "enum has no value '$1'", +, +errNoCommand: "no command given", +errInvalidCommandX: "invalid command: '$1'", +errXNeedsParamObjectType: , +errTemplateInstantiationTooNested: "template instantiation too nested, try --evalTemplateLimit:N", +errMacroInstantiationTooNested: "macro instantiation too nested, try --evalMacroLimit:N", +errInstantiationFrom: "template/generic instantiation from here", +errInvalidIndexValueForTuple: "invalid index value for tuple subscript", +errCommandExpectsFilename: "command expects a filename argument", +errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file", +errXExpected: "'$1' expected", +, +errCastToANonConcreteType: "cannot cast to a non concrete type: '$1'", +errInvalidSectionStart: "invalid section start", +errGridTableNotImplemented: "grid table is not implemented", +errGeneralParseError: "general parse error", +errNewSectionExpected: "new section expected", +errWhitespaceExpected: "whitespace expected, got '$1'", +errXisNoValidIndexFile: "'$1' is no valid index file", +errCannotRenderX: "cannot render reStructuredText element '$1'", +errVarVarTypeNotAllowed: , +errInstantiateXExplicitly: "instantiate '$1' explicitly", +errOnlyACallOpCanBeDelegator: , +errUsingNoSymbol: "'$1' is not a variable, constant or a proc name", +errMacroBodyDependsOnGenericTypes: "the macro body cannot be compiled, " & + "because the parameter '$1' has a generic type", +errDestructorNotGenericEnough: "Destructor signature is too specific. " & + "A destructor must be associated will all instantiations of a generic type", +errInlineIteratorsAsProcParams: "inline iterators can be used as parameters only for " & + "templates, macros and other inline iterators", +errXExpectsTwoArguments: "'$1' expects two arguments", +errXExpectsObjectTypes: "'$1' expects object types", +errXcanNeverBeOfThisSubtype: "'$1' can never be of this subtype", +errTooManyIterations: "interpretation requires too many iterations; " & + "if you are sure this is not a bug in your code edit " & + "compiler/vmdef.MaxLoopIterations and rebuild the compiler", +errFieldXNotFound: "field '$1' cannot be found", +errInvalidConversionFromTypeX: "invalid conversion from type '$1'", +errAssertionFailed: "assertion failed", +errCannotGenerateCodeForX: "cannot generate code for '$1'", +errXRequiresOneArgument: "$1 requires one parameter", +errUnhandledExceptionX: "unhandled exception: $1", +errCyclicTree: "macro returned a cyclic abstract syntax tree", +errXisNoMacroOrTemplate: "'$1' is no macro or template", +errXhasSideEffects: "'$1' can have side effects", +errWrongSymbolX:, +errIllegalCaptureX: "illegal capture '$1'", +errXCannotBeClosure: "'$1' cannot have 'closure' calling convention", +, +]# diff --git a/compiler/depends.nim b/compiler/depends.nim index 2b600c1da..732404232 100644 --- a/compiler/depends.nim +++ b/compiler/depends.nim @@ -19,6 +19,7 @@ proc generateDot*(project: string) type TGen = object of TPassContext module*: PSym + config: ConfigRef PGen = ref TGen var gDotGraph: Rope # the generated DOT file; we need a global variable @@ -33,10 +34,10 @@ proc addDotDependency(c: PPassContext, n: PNode): PNode = case n.kind of nkImportStmt: for i in countup(0, sonsLen(n) - 1): - var imported = getModuleName(n.sons[i]) + var imported = getModuleName(g.config, n.sons[i]) addDependencyAux(g.module.name.s, imported) of nkFromStmt, nkImportExceptStmt: - var imported = getModuleName(n.sons[0]) + var imported = getModuleName(g.config, n.sons[0]) addDependencyAux(g.module.name.s, imported) of nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr: for i in countup(0, sonsLen(n) - 1): discard addDotDependency(c, n.sons[i]) @@ -52,6 +53,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = var g: PGen new(g) g.module = module + g.config = graph.config result = g const gendependPass* = makePass(open = myOpen, process = addDotDependency) diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index 0fdeceba0..31c735794 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -89,11 +89,35 @@ ## tmp.bar)) ## destroy(tmp.bar) ## destroy(tmp.x); destroy(tmp.y) +## + +##[ +From https://github.com/nim-lang/Nim/wiki/Destructors + +Rule Pattern Transformed into +---- ------- ---------------- +1.1 var x: T; stmts var x: T; try stmts + finally: `=destroy`(x) +1.2 var x: sink T; stmts var x: sink T; stmts; ensureEmpty(x) +2 x = f() `=sink`(x, f()) +3 x = lastReadOf z `=sink`(x, z) +4.1 y = sinkParam `=sink`(y, sinkParam) +4.2 x = y `=`(x, y) # a copy +5.1 f_sink(g()) f_sink(g()) +5.2 f_sink(y) f_sink(copy y); # copy unless we can see it's the last read +5.3 f_sink(move y) f_sink(y); reset(y) # explicit moves empties 'y' +5.4 f_noSink(g()) var tmp = bitwiseCopy(g()); f(tmp); `=destroy`(tmp) +Remarks: Rule 1.2 is not yet implemented because ``sink`` is currently + not allowed as a local variable. + +``move`` builtin needs to be implemented. +]## import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, - strutils, options, dfa, lowerings, rodread + strutils, options, dfa, lowerings, rodread, tables, modulegraphs, + configuration const InterestingSyms = {skVar, skResult, skLet} @@ -106,6 +130,15 @@ type tmpObj: PType tmp: PSym destroys, topLevelVars: PNode + toDropBit: Table[int, PSym] + graph: ModuleGraph + +proc getTemp(c: var Con; typ: PType; info: TLineInfo): PNode = + # XXX why are temps fields in an object here? + let f = newSym(skField, getIdent(":d" & $c.tmpObj.n.len), c.owner, info) + f.typ = typ + rawAddField c.tmpObj, f + result = rawDirectAccess(c.tmp, f) proc isHarmlessVar*(s: PSym; c: Con): bool = # 's' is harmless if it used only once and its @@ -174,7 +207,7 @@ proc patchHead(n: PNode) = if n[1].typ.isNil: # XXX toptree crashes without this workaround. Figure out why. return - let t = n[1].typ.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred}) + let t = n[1].typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink, tyInferred}) template patch(op, field) = if s.name.s == op and field != nil and field != s: n.sons[0].sym = field @@ -191,47 +224,118 @@ proc patchHead(s: PSym) = template genOp(opr, opname) = let op = opr if op == nil: - globalError(dest.info, "internal error: '" & opname & "' operator not found for type " & typeToString(t)) + globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator not found for type " & typeToString(t)) elif op.ast[genericParamsPos].kind != nkEmpty: - globalError(dest.info, "internal error: '" & opname & "' operator is generic") + globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator is generic") patchHead op result = newTree(nkCall, newSymNode(op), newTree(nkHiddenAddr, dest)) -proc genSink(t: PType; dest: PNode): PNode = - let t = t.skipTypes({tyGenericInst, tyAlias}) +proc genSink(c: Con; t: PType; dest: PNode): PNode = + let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) genOp(if t.sink != nil: t.sink else: t.assignment, "=sink") -proc genCopy(t: PType; dest: PNode): PNode = - let t = t.skipTypes({tyGenericInst, tyAlias}) +proc genCopy(c: Con; t: PType; dest: PNode): PNode = + let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) genOp(t.assignment, "=") -proc genDestroy(t: PType; dest: PNode): PNode = - let t = t.skipTypes({tyGenericInst, tyAlias}) +proc genDestroy(c: Con; t: PType; dest: PNode): PNode = + let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) genOp(t.destructor, "=destroy") proc addTopVar(c: var Con; v: PNode) = c.topLevelVars.add newTree(nkIdentDefs, v, emptyNode, emptyNode) +proc dropBit(c: var Con; s: PSym): PSym = + result = c.toDropBit.getOrDefault(s.id) + assert result != nil + +proc registerDropBit(c: var Con; s: PSym) = + let result = newSym(skTemp, getIdent(s.name.s & "_AliveBit"), c.owner, s.info) + result.typ = getSysType(c.graph, s.info, tyBool) + let trueVal = newIntTypeNode(nkIntLit, 1, result.typ) + c.topLevelVars.add newTree(nkIdentDefs, newSymNode result, emptyNode, trueVal) + c.toDropBit[s.id] = result + # generate: + # if not sinkParam_AliveBit: `=destroy`(sinkParam) + c.destroys.add newTree(nkIfStmt, + newTree(nkElifBranch, newSymNode result, genDestroy(c, s.typ, newSymNode s))) + proc p(n: PNode; c: var Con): PNode template recurse(n, dest) = for i in 0..<n.len: dest.add p(n[i], c) +proc isSinkParam(s: PSym): bool {.inline.} = + result = s.kind == skParam and s.typ.kind == tySink + +const constrExprs = nkCallKinds+{nkObjConstr} + +proc destructiveMoveSink(n: PNode; c: var Con): PNode = + # generate: (chckMove(sinkParam_AliveBit); sinkParam_AliveBit = false; sinkParam) + result = newNodeIT(nkStmtListExpr, n.info, n.typ) + let bit = newSymNode dropBit(c, n.sym) + if optMoveCheck in c.owner.options: + result.add callCodegenProc(c.graph, "chckMove", bit) + result.add newTree(nkAsgn, bit, + newIntTypeNode(nkIntLit, 0, getSysType(c.graph, n.info, tyBool))) + result.add n + proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = - if ri.kind in nkCallKinds+{nkObjConstr}: - result = genSink(ri.typ, dest) + if ri.kind in constrExprs: + result = genSink(c, ri.typ, dest) # watch out and no not transform 'ri' twice if it's a call: let ri2 = copyNode(ri) recurse(ri, ri2) result.add ri2 elif ri.kind == nkSym and isHarmlessVar(ri.sym, c): - result = genSink(ri.typ, dest) + result = genSink(c, ri.typ, dest) result.add p(ri, c) + elif ri.kind == nkSym and isSinkParam(ri.sym): + result = genSink(c, ri.typ, dest) + result.add destructiveMoveSink(ri, c) else: - result = genCopy(ri.typ, dest) + result = genCopy(c, ri.typ, dest) result.add p(ri, c) +proc passCopyToSink(n: PNode; c: var Con): PNode = + result = newNodeIT(nkStmtListExpr, n.info, n.typ) + let tmp = getTemp(c, n.typ, n.info) + if hasDestructor(n.typ): + var m = genCopy(c, n.typ, tmp) + m.add p(n, c) + result.add m + message(c.graph.config, n.info, hintPerformance, + "passing '$1' to a sink parameter introduces an implicit copy; " & + "use 'move($1)' to prevent it" % $n) + else: + result.add newTree(nkAsgn, tmp, p(n, c)) + result.add tmp + +proc genReset(n: PNode; c: var Con): PNode = + result = newNodeI(nkCall, n.info) + result.add(newSymNode(createMagic(c.graph, "reset", mReset))) + # The mReset builtin does not take the address: + result.add n + +proc destructiveMoveVar(n: PNode; c: var Con): PNode = + # generate: (let tmp = v; reset(v); tmp) + result = newNodeIT(nkStmtListExpr, n.info, n.typ) + + var temp = newSym(skLet, getIdent("blitTmp"), c.owner, n.info) + var v = newNodeI(nkLetSection, n.info) + let tempAsNode = newSymNode(temp) + + var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3) + vpart.sons[0] = tempAsNode + vpart.sons[1] = ast.emptyNode + vpart.sons[2] = n + add(v, vpart) + + result.add v + result.add genReset(n, c) + result.add tempAsNode + proc p(n: PNode; c: var Con): PNode = case n.kind of nkVarSection, nkLetSection: @@ -243,7 +347,7 @@ proc p(n: PNode; c: var Con): PNode = let L = it.len-1 let ri = it[L] if it.kind == nkVarTuple and hasDestructor(ri.typ): - let x = lowerTupleUnpacking(it, c.owner) + let x = lowerTupleUnpacking(c.graph, it, c.owner) result.add p(x, c) elif it.kind == nkIdentDefs and hasDestructor(it[0].typ): for j in 0..L-2: @@ -252,7 +356,7 @@ proc p(n: PNode; c: var Con): PNode = # move the variable declaration to the top of the frame: c.addTopVar v # make sure it's destroyed at the end of the proc: - c.destroys.add genDestroy(v.typ, v) + c.destroys.add genDestroy(c, v.typ, v) if ri.kind != nkEmpty: let r = moveOrCopy(v, ri, c) result.add r @@ -266,22 +370,45 @@ proc p(n: PNode; c: var Con): PNode = varSection.add itCopy result.add varSection of nkCallKinds: + let parameters = n[0].typ + let L = if parameters != nil: parameters.len else: 0 + for i in 1 ..< n.len: + let arg = n[i] + if i < L and parameters[i].kind == tySink: + if arg.kind in nkCallKinds: + # recurse but skip the call expression in order to prevent + # destructor injections: Rule 5.1 is different from rule 5.4! + let a = copyNode(arg) + recurse(arg, a) + n.sons[i] = a + elif arg.kind in {nkObjConstr, nkCharLit..nkFloat128Lit}: + discard "object construction to sink parameter: nothing to do" + elif arg.kind == nkSym and isHarmlessVar(arg.sym, c): + # if x is a variable and it its last read we eliminate its + # destructor invokation, but don't. We need to reset its memory + # to disable its destructor which we have not elided: + n.sons[i] = destructiveMoveVar(arg, c) + elif arg.kind == nkSym and isSinkParam(arg.sym): + # mark the sink parameter as used: + n.sons[i] = destructiveMoveSink(arg, c) + else: + # an object that is not temporary but passed to a 'sink' parameter + # results in a copy. + n.sons[i] = passCopyToSink(arg, c) + else: + n.sons[i] = p(arg, c) + if n.typ != nil and hasDestructor(n.typ): discard "produce temp creation" result = newNodeIT(nkStmtListExpr, n.info, n.typ) - let f = newSym(skField, getIdent(":d" & $c.tmpObj.n.len), c.owner, n.info) - f.typ = n.typ - rawAddField c.tmpObj, f - var m = genSink(n.typ, rawDirectAccess(c.tmp, f)) - var call = copyNode(n) - recurse(n, call) - m.add call - result.add m - result.add rawDirectAccess(c.tmp, f) - c.destroys.add genDestroy(n.typ, rawDirectAccess(c.tmp, f)) + let tmp = getTemp(c, n.typ, n.info) + var sinkExpr = genSink(c, n.typ, tmp) + sinkExpr.add n + result.add sinkExpr + result.add tmp + c.destroys.add genDestroy(c, n.typ, tmp) else: - result = copyNode(n) - recurse(n, result) + result = n of nkAsgn, nkFastAsgn: if hasDestructor(n[0].typ): result = moveOrCopy(n[0], n[1], c) @@ -295,22 +422,29 @@ proc p(n: PNode; c: var Con): PNode = result = copyNode(n) recurse(n, result) -proc injectDestructorCalls*(owner: PSym; n: PNode): PNode = +proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode = when defined(nimDebugDestroys): echo "injecting into ", n var c: Con c.owner = owner c.tmp = newSym(skTemp, getIdent":d", owner, n.info) - c.tmpObj = createObj(owner, n.info) + c.tmpObj = createObj(g, owner, n.info) c.tmp.typ = c.tmpObj c.destroys = newNodeI(nkStmtList, n.info) c.topLevelVars = newNodeI(nkVarSection, n.info) + c.toDropBit = initTable[int, PSym]() + c.graph = g let cfg = constructCfg(owner, n) shallowCopy(c.g, cfg) c.jumpTargets = initIntSet() for i in 0..<c.g.len: if c.g[i].kind in {goto, fork}: c.jumpTargets.incl(i+c.g[i].dest) + if owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}: + let params = owner.typ.n + for i in 1 ..< params.len: + let param = params[i].sym + if param.typ.kind == tySink: registerDropBit(c, param) let body = p(n, c) if c.tmp.typ.n.len > 0: c.addTopVar(newSymNode c.tmp) diff --git a/compiler/dfa.nim b/compiler/dfa.nim index b648995f4..0fd706178 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -23,7 +23,7 @@ ## "A Graph–Free Approach to Data–Flow Analysis" by Markus Mohnen. ## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf -import ast, astalgo, types, intsets, tables, msgs +import ast, astalgo, types, intsets, tables, msgs, options type InstrKind* = enum @@ -102,14 +102,14 @@ proc genLabel(c: Con): TPosition = proc jmpBack(c: var Con, n: PNode, p = TPosition(0)) = let dist = p.int - c.code.len - internalAssert(-0x7fff < dist and dist < 0x7fff) + doAssert(-0x7fff < dist and dist < 0x7fff) c.code.add Instr(n: n, kind: goto, dest: dist) proc patch(c: var Con, p: TPosition) = # patch with current index let p = p.int let diff = c.code.len - p - internalAssert(-0x7fff < diff and diff < 0x7fff) + doAssert(-0x7fff < diff and diff < 0x7fff) c.code[p].dest = diff proc popBlock(c: var Con; oldLen: int) = @@ -160,7 +160,7 @@ proc genBreak(c: var Con; n: PNode) = if c.blocks[i].label == n.sons[0].sym: c.blocks[i].fixups.add L1 return - globalError(n.info, errGenerated, "VM problem: cannot find 'break' target") + #globalError(n.info, "VM problem: cannot find 'break' target") else: c.blocks[c.blocks.high].fixups.add L1 @@ -323,7 +323,7 @@ proc gen(c: var Con; n: PNode) = of nkBreakStmt: genBreak(c, n) of nkTryStmt: genTry(c, n) of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange, - nkBracket, nkCurly, nkPar, nkClosure, nkObjConstr: + nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr: for x in n: gen(c, x) of nkPragmaBlock: gen(c, n.lastSon) of nkDiscardStmt: gen(c, n.sons[0]) @@ -334,7 +334,7 @@ proc gen(c: var Con; n: PNode) = of nkVarSection, nkLetSection: genVarSection(c, n) else: discard -proc dfa(code: seq[Instr]) = +proc dfa(code: seq[Instr]; conf: ConfigRef) = var u = newSeq[IntSet](code.len) # usages var d = newSeq[IntSet](code.len) # defs var c = newSeq[IntSet](code.len) # consumed @@ -426,17 +426,17 @@ proc dfa(code: seq[Instr]) = of use, useWithinCall: let s = code[i].sym if s.id notin d[i]: - localError(code[i].n.info, "usage of uninitialized variable: " & s.name.s) + localError(conf, code[i].n.info, "usage of uninitialized variable: " & s.name.s) if s.id in c[i]: - localError(code[i].n.info, "usage of an already consumed variable: " & s.name.s) + localError(conf, code[i].n.info, "usage of an already consumed variable: " & s.name.s) else: discard -proc dataflowAnalysis*(s: PSym; body: PNode) = +proc dataflowAnalysis*(s: PSym; body: PNode; conf: ConfigRef) = var c = Con(code: @[], blocks: @[]) gen(c, body) when defined(useDfa) and defined(debugDfa): echoCfg(c.code) - dfa(c.code) + dfa(c.code, conf) proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph = ## constructs a control flow graph for ``body``. diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 65dcb73c9..7ab2f0eee 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -16,7 +16,7 @@ import wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast, packages/docutils/rst, packages/docutils/rstgen, times, packages/docutils/highlite, sempass2, json, xmltree, cgi, - typesrenderer, astalgo, modulepaths + typesrenderer, astalgo, modulepaths, configuration type TSections = array[TSymKind, Rope] @@ -29,6 +29,7 @@ type jArray: JsonNode types: TStrTable isPureRst: bool + conf*: ConfigRef PDoc* = ref TDocumentor ## Alias to type less. @@ -53,42 +54,47 @@ proc attachToType(d: PDoc; p: PSym): PSym = if params.len > 0: check(0) for i in 2..<params.len: check(i) -proc compilerMsgHandler(filename: string, line, col: int, - msgKind: rst.MsgKind, arg: string) {.procvar.} = - # translate msg kind: - var k: msgs.TMsgKind - case msgKind - of meCannotOpenFile: k = errCannotOpenFile - of meExpected: k = errXExpected - of meGridTableNotImplemented: k = errGridTableNotImplemented - of meNewSectionExpected: k = errNewSectionExpected - of meGeneralParseError: k = errGeneralParseError - of meInvalidDirective: k = errInvalidDirectiveX - of mwRedefinitionOfLabel: k = warnRedefinitionOfLabel - of mwUnknownSubstitution: k = warnUnknownSubstitutionX - of mwUnsupportedLanguage: k = warnLanguageXNotSupported - of mwUnsupportedField: k = warnFieldXNotSupported - globalError(newLineInfo(filename, line, col), k, arg) - -proc docgenFindFile(s: string): string {.procvar.} = - result = options.findFile(s) - if result.len == 0: - result = getCurrentDir() / s - if not existsFile(result): result = "" +template declareClosures = + proc compilerMsgHandler(filename: string, line, col: int, + msgKind: rst.MsgKind, arg: string) {.procvar.} = + # translate msg kind: + var k: TMsgKind + case msgKind + of meCannotOpenFile: k = errCannotOpenFile + of meExpected: k = errXExpected + of meGridTableNotImplemented: k = errGridTableNotImplemented + of meNewSectionExpected: k = errNewSectionExpected + of meGeneralParseError: k = errGeneralParseError + of meInvalidDirective: k = errInvalidDirectiveX + of mwRedefinitionOfLabel: k = warnRedefinitionOfLabel + of mwUnknownSubstitution: k = warnUnknownSubstitutionX + of mwUnsupportedLanguage: k = warnLanguageXNotSupported + of mwUnsupportedField: k = warnFieldXNotSupported + globalError(conf, newLineInfo(conf, filename, line, col), k, arg) + + proc docgenFindFile(s: string): string {.procvar.} = + result = options.findFile(conf, s) + if result.len == 0: + result = getCurrentDir() / s + if not existsFile(result): result = "" proc parseRst(text, filename: string, line, column: int, hasToc: var bool, - rstOptions: RstParseOptions): PRstNode = + rstOptions: RstParseOptions; + conf: ConfigRef): PRstNode = + declareClosures() result = rstParse(text, filename, line, column, hasToc, rstOptions, docgenFindFile, compilerMsgHandler) -proc newDocumentor*(filename: string, config: StringTableRef): PDoc = +proc newDocumentor*(filename: string, conf: ConfigRef): PDoc = + declareClosures() new(result) - initRstGenerator(result[], (if gCmd != cmdRst2tex: outHtml else: outLatex), - options.gConfigVars, filename, {roSupportRawDirective}, + result.conf = conf + initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex), + conf.configVars, filename, {roSupportRawDirective}, docgenFindFile, compilerMsgHandler) - if config.hasKey("doc.googleAnalytics"): + if conf.configVars.hasKey("doc.googleAnalytics"): result.analytics = """ <script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ @@ -100,7 +106,7 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc = ga('send', 'pageview'); </script> - """ % [config.getOrDefault"doc.googleAnalytics"] + """ % [conf.configVars.getOrDefault"doc.googleAnalytics"] else: result.analytics = "" @@ -109,10 +115,10 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc = result.jArray = newJArray() initStrTable result.types result.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string; status: int; content: string) = - localError(newLineInfo(d.filename, -1, -1), warnUser, "only 'rst2html' supports the ':test:' attribute") + localError(conf, newLineInfo(conf, d.filename, -1, -1), warnUser, "only 'rst2html' supports the ':test:' attribute") -proc dispA(dest: var Rope, xml, tex: string, args: openArray[Rope]) = - if gCmd != cmdRst2tex: addf(dest, xml, args) +proc dispA(conf: ConfigRef; dest: var Rope, xml, tex: string, args: openArray[Rope]) = + if conf.cmd != cmdRst2tex: addf(dest, xml, args) else: addf(dest, tex, args) proc getVarIdx(varnames: openArray[string], id: string): int = @@ -121,7 +127,8 @@ proc getVarIdx(varnames: openArray[string], id: string): int = return i result = -1 -proc ropeFormatNamedVars(frmt: FormatStr, varnames: openArray[string], +proc ropeFormatNamedVars(conf: ConfigRef; frmt: FormatStr, + varnames: openArray[string], varvalues: openArray[Rope]): Rope = var i = 0 var L = len(frmt) @@ -144,7 +151,8 @@ proc ropeFormatNamedVars(frmt: FormatStr, varnames: openArray[string], j = (j * 10) + ord(frmt[i]) - ord('0') inc(i) 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: + rawMessage(conf, errGenerated, "Invalid format string; too many $s: " & frmt) num = j add(result, varvalues[j - 1]) of 'A'..'Z', 'a'..'z', '\x80'..'\xFF': @@ -155,20 +163,23 @@ proc ropeFormatNamedVars(frmt: FormatStr, varnames: openArray[string], if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break var idx = getVarIdx(varnames, id) if idx >= 0: add(result, varvalues[idx]) - else: rawMessage(errUnknownSubstitionVar, id) + else: rawMessage(conf, errGenerated, "unknown substition variable: " & id) of '{': var id = "" inc(i) - while frmt[i] != '}': - if frmt[i] == '\0': rawMessage(errTokenExpected, "}") + while i < frmt.len and frmt[i] != '}': add(id, frmt[i]) inc(i) - inc(i) # skip } - # search for the variable: - var idx = getVarIdx(varnames, id) + if i >= frmt.len: + rawMessage(conf, errGenerated, "expected closing '}'") + else: + inc(i) # skip } + # search for the variable: + let idx = getVarIdx(varnames, id) if idx >= 0: add(result, varvalues[idx]) - else: rawMessage(errUnknownSubstitionVar, id) - else: internalError("ropeFormatNamedVars") + else: rawMessage(conf, errGenerated, "unknown substition variable: " & id) + else: + add(result, "$") var start = i while i < L: if frmt[i] != '$': inc(i) @@ -181,7 +192,7 @@ proc genComment(d: PDoc, n: PNode): string = if n.comment != nil: renderRstToOut(d[], parseRst(n.comment, toFilename(n.info), toLinenumber(n.info), toColumn(n.info), - dummyHasToc, d.options), result) + dummyHasToc, d.options, d.conf), result) proc genRecComment(d: PDoc, n: PNode): Rope = if n == nil: return nil @@ -220,45 +231,46 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe of tkEof: break of tkComment: - dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}", + dispA(d.conf, result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}", [rope(esc(d.target, literal))]) of tokKeywordLow..tokKeywordHigh: - dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}", + dispA(d.conf, result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}", [rope(literal)]) of tkOpr: - dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}", + dispA(d.conf, result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}", [rope(esc(d.target, literal))]) of tkStrLit..tkTripleStrLit: - dispA(result, "<span class=\"StringLit\">$1</span>", + dispA(d.conf, result, "<span class=\"StringLit\">$1</span>", "\\spanStringLit{$1}", [rope(esc(d.target, literal))]) of tkCharLit: - dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}", + dispA(d.conf, result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}", [rope(esc(d.target, literal))]) of tkIntLit..tkUInt64Lit: - dispA(result, "<span class=\"DecNumber\">$1</span>", + dispA(d.conf, result, "<span class=\"DecNumber\">$1</span>", "\\spanDecNumber{$1}", [rope(esc(d.target, literal))]) of tkFloatLit..tkFloat128Lit: - dispA(result, "<span class=\"FloatNumber\">$1</span>", + dispA(d.conf, result, "<span class=\"FloatNumber\">$1</span>", "\\spanFloatNumber{$1}", [rope(esc(d.target, literal))]) of tkSymbol: - dispA(result, "<span class=\"Identifier\">$1</span>", + dispA(d.conf, result, "<span class=\"Identifier\">$1</span>", "\\spanIdentifier{$1}", [rope(esc(d.target, literal))]) of tkSpaces, tkInvalid: add(result, literal) of tkCurlyDotLe: - dispA(result, """<span class="Other pragmabegin">$1</span><div class="pragma">""", + dispA(d.conf, result, """<span class="Other pragmabegin">$1</span><div class="pragma">""", "\\spanOther{$1}", [rope(esc(d.target, literal))]) of tkCurlyDotRi: - dispA(result, "</div><span class=\"Other pragmaend\">$1</span>", + dispA(d.conf, result, "</div><span class=\"Other pragmaend\">$1</span>", "\\spanOther{$1}", [rope(esc(d.target, literal))]) of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi, tkBracketDotLe, tkBracketDotRi, tkParDotLe, tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkAccent, tkColonColon, - tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr: - dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}", + tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr, + tkBracketLeColon: + dispA(d.conf, result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}", [rope(esc(d.target, literal))]) proc getAllRunnableExamples(d: PDoc; n: PNode; dest: var Rope) = @@ -266,7 +278,7 @@ proc getAllRunnableExamples(d: PDoc; n: PNode; dest: var Rope) = of nkCallKinds: if n[0].kind == nkSym and n[0].sym.magic == mRunnableExamples and n.len >= 2 and n.lastSon.kind == nkStmtList: - dispA(dest, "\n<strong class=\"examples_text\">$1</strong>\n", + dispA(d.conf, dest, "\n<strong class=\"examples_text\">$1</strong>\n", "\n\\textbf{$1}\n", [rope"Examples:"]) inc d.listingCounter let id = $d.listingCounter @@ -335,7 +347,6 @@ proc getName(d: PDoc, n: PNode, splitAfter = -1): string = of nkOpenSymChoice, nkClosedSymChoice: result = getName(d, n[0], splitAfter) else: - internalError(n.info, "getName()") result = "" proc getNameIdent(n: PNode): PIdent = @@ -365,7 +376,6 @@ proc getRstName(n: PNode): PRstNode = of nkOpenSymChoice, nkClosedSymChoice: result = getRstName(n[0]) else: - internalError(n.info, "getRstName()") result = nil proc newUniquePlainSymbol(d: PDoc, original: string): string = @@ -489,22 +499,22 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = symbolOrIdEncRope = encodeUrl(symbolOrId).rope var seeSrcRope: Rope = nil - let docItemSeeSrc = getConfigVar("doc.item.seesrc") + let docItemSeeSrc = getConfigVar(d.conf, "doc.item.seesrc") if docItemSeeSrc.len > 0: - let cwd = getCurrentDir().canonicalizePath() + let cwd = canonicalizePath(d.conf, getCurrentDir()) var path = n.info.toFullPath if path.startsWith(cwd): path = path[cwd.len+1 .. ^1].replace('\\', '/') - let gitUrl = getConfigVar("git.url") + let gitUrl = getConfigVar(d.conf, "git.url") if gitUrl.len > 0: - var commit = getConfigVar("git.commit") + var commit = getConfigVar(d.conf, "git.commit") if commit.len == 0: commit = "master" - dispA(seeSrcRope, "$1", "", [ropeFormatNamedVars(docItemSeeSrc, + dispA(d.conf, seeSrcRope, "$1", "", [ropeFormatNamedVars(d.conf, docItemSeeSrc, ["path", "line", "url", "commit"], [rope path, rope($n.info.line), rope gitUrl, rope commit])]) - add(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"), + add(d.section[k], ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.item"), ["name", "header", "desc", "itemID", "header_plain", "itemSym", "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "seeSrc"], [nameRope, result, comm, itemIDRope, plainNameRope, plainSymbolRope, @@ -515,7 +525,7 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = let att = attachToType(d, nameNode.sym) if att != nil: attype = rope esc(d.target, att.name.s) - add(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"), + add(d.toc[k], ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.item.toc"), ["name", "header", "desc", "itemID", "header_plain", "itemSym", "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "attype"], [rope(getName(d, nameNode, d.splitAfter)), result, comm, @@ -543,7 +553,7 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode = initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments}) - result = %{ "name": %name, "type": %($k), "line": %n.info.line, + result = %{ "name": %name, "type": %($k), "line": %n.info.line.int, "col": %n.info.col} if comm != nil and comm != "": result["description"] = %comm @@ -568,9 +578,9 @@ proc traceDeps(d: PDoc, it: PNode) = traceDeps(d, a) else: if d.section[k] != nil: add(d.section[k], ", ") - dispA(d.section[k], + dispA(d.conf, d.section[k], "<a class=\"reference external\" href=\"$1.html\">$1</a>", - "$1", [rope(getModuleName(it))]) + "$1", [rope(getModuleName(d.conf, it))]) proc generateDoc*(d: PDoc, n: PNode) = case n.kind @@ -617,7 +627,7 @@ proc generateJson*(d: PDoc, n: PNode) = of nkCommentStmt: if n.comment != nil and startsWith(n.comment, "##"): let stripped = n.comment.substr(2).strip - d.add %{ "comment": %stripped, "line": %n.info.line, + d.add %{ "comment": %stripped, "line": %n.info.line.int, "col": %n.info.col } of nkProcDef: when useEffectSystem: documentRaises(n) @@ -703,10 +713,10 @@ proc genSection(d: PDoc, kind: TSymKind) = ] if d.section[kind] == nil: return var title = sectionNames[kind].rope - d.section[kind] = ropeFormatNamedVars(getConfigVar("doc.section"), [ + d.section[kind] = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.section"), [ "sectionid", "sectionTitle", "sectionTitleID", "content"], [ ord(kind).rope, title, rope(ord(kind) + 50), d.section[kind]]) - d.toc[kind] = ropeFormatNamedVars(getConfigVar("doc.section.toc"), [ + d.toc[kind] = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.section.toc"), [ "sectionid", "sectionTitle", "sectionTitleID", "content"], [ ord(kind).rope, title, rope(ord(kind) + 50), d.toc[kind]]) @@ -722,7 +732,7 @@ proc genOutFile(d: PDoc): Rope = genSection(d, i) add(toc, d.toc[i]) if toc != nil: - toc = ropeFormatNamedVars(getConfigVar("doc.toc"), ["content"], [toc]) + toc = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.toc"), ["content"], [toc]) 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. @@ -736,13 +746,13 @@ proc genOutFile(d: PDoc): Rope = let bodyname = if d.hasToc and not d.isPureRst: "doc.body_toc_group" elif d.hasToc: "doc.body_toc" else: "doc.body_no_toc" - content = ropeFormatNamedVars(getConfigVar(bodyname), ["title", + content = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, bodyname), ["title", "tableofcontents", "moduledesc", "date", "time", "content"], [title.rope, toc, d.modDesc, rope(getDateStr()), rope(getClockStr()), code]) - if optCompileOnly notin gGlobalOptions: + if optCompileOnly notin d.conf.globalOptions: # XXX what is this hack doing here? 'optCompileOnly' means raw output!? - code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", + code = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.file"), ["title", "tableofcontents", "moduledesc", "date", "time", "content", "author", "version", "analytics"], [title.rope, toc, d.modDesc, rope(getDateStr()), @@ -753,60 +763,60 @@ proc genOutFile(d: PDoc): Rope = result = code proc generateIndex*(d: PDoc) = - if optGenIndex in gGlobalOptions: - writeIndexFile(d[], splitFile(options.outFile).dir / + if optGenIndex in d.conf.globalOptions: + writeIndexFile(d[], splitFile(d.conf.outFile).dir / splitFile(d.filename).name & IndexExt) -proc getOutFile2(filename, ext, dir: string): string = - if gWholeProject: - let d = if options.outFile != "": options.outFile else: dir +proc getOutFile2(conf: ConfigRef; filename, ext, dir: string): string = + if optWholeProject in conf.globalOptions: + let d = if conf.outFile != "": conf.outFile else: dir createDir(d) result = d / changeFileExt(filename, ext) else: - result = getOutFile(filename, ext) + result = getOutFile(conf, filename, ext) proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) = var content = genOutFile(d) - if optStdout in gGlobalOptions: + if optStdout in d.conf.globalOptions: writeRope(stdout, content) else: - writeRope(content, getOutFile2(filename, outExt, "htmldocs"), useWarning) + writeRope(content, getOutFile2(d.conf, filename, outExt, "htmldocs"), useWarning) proc writeOutputJson*(d: PDoc, filename, outExt: string, useWarning = false) = let content = %*{"orig": d.filename, - "nimble": getPackageName(d.filename), + "nimble": getPackageName(d.conf, d.filename), "entries": d.jArray} - if optStdout in gGlobalOptions: + if optStdout in d.conf.globalOptions: write(stdout, $content) else: var f: File - if open(f, getOutFile2(splitFile(filename).name, + if open(f, getOutFile2(d.conf, splitFile(filename).name, outExt, "jsondocs"), fmWrite): write(f, $content) close(f) else: discard "fixme: error report" -proc commandDoc*() = - var ast = parseFile(gProjectMainIdx, newIdentCache()) +proc commandDoc*(conf: ConfigRef) = + var ast = parseFile(conf.projectMainIdx.FileIndex, newIdentCache(), conf) if ast == nil: return - var d = newDocumentor(gProjectFull, options.gConfigVars) + var d = newDocumentor(conf.projectFull, conf) d.hasToc = true generateDoc(d, ast) - writeOutput(d, gProjectFull, HtmlExt) + writeOutput(d, conf.projectFull, HtmlExt) generateIndex(d) -proc commandRstAux(filename, outExt: string) = +proc commandRstAux(conf: ConfigRef; filename, outExt: string) = var filen = addFileExt(filename, "txt") - var d = newDocumentor(filen, options.gConfigVars) + var d = newDocumentor(filen, conf) d.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string; status: int; content: string) = var outp: string if filename.len == 0: inc(d.id) let nameOnly = splitFile(d.filename).name - let subdir = getNimcacheDir() / nameOnly + let subdir = getNimcacheDir(conf) / nameOnly createDir(subdir) outp = subdir / (nameOnly & "_snippet_" & $d.id & ".nim") elif isAbsolute(filename): @@ -815,14 +825,14 @@ proc commandRstAux(filename, outExt: string) = # Nim's convention: every path is relative to the file it was written in: outp = splitFile(d.filename).dir / filename writeFile(outp, content) - let cmd = unescape(cmd) % quoteShell(outp) - rawMessage(hintExecuting, cmd) + let cmd = cmd % quoteShell(outp) + rawMessage(conf, hintExecuting, cmd) if execShellCmd(cmd) != status: - rawMessage(errExecutionOfProgramFailed, cmd) + rawMessage(conf, errGenerated, "executing of external program failed: " & cmd) d.isPureRst = true var rst = parseRst(readFile(filen), filen, 0, 1, d.hasToc, - {roSupportRawDirective}) + {roSupportRawDirective}, conf) var modDesc = newStringOfCap(30_000) #d.modDesc = newMutableRope(30_000) renderRstToOut(d[], rst, modDesc) @@ -831,50 +841,50 @@ proc commandRstAux(filename, outExt: string) = writeOutput(d, filename, outExt) generateIndex(d) -proc commandRst2Html*() = - commandRstAux(gProjectFull, HtmlExt) +proc commandRst2Html*(conf: ConfigRef) = + commandRstAux(conf, conf.projectFull, HtmlExt) -proc commandRst2TeX*() = +proc commandRst2TeX*(conf: ConfigRef) = splitter = "\\-" - commandRstAux(gProjectFull, TexExt) + commandRstAux(conf, conf.projectFull, TexExt) -proc commandJson*() = - var ast = parseFile(gProjectMainIdx, newIdentCache()) +proc commandJson*(conf: ConfigRef) = + var ast = parseFile(conf.projectMainIdx.FileIndex, newIdentCache(), conf) if ast == nil: return - var d = newDocumentor(gProjectFull, options.gConfigVars) + var d = newDocumentor(conf.projectFull, conf) d.hasToc = true generateJson(d, ast) let json = d.jArray let content = rope(pretty(json)) - if optStdout in gGlobalOptions: + if optStdout in d.conf.globalOptions: writeRope(stdout, content) else: #echo getOutFile(gProjectFull, JsonExt) - writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false) + writeRope(content, getOutFile(conf, conf.projectFull, JsonExt), useWarning = false) -proc commandTags*() = - var ast = parseFile(gProjectMainIdx, newIdentCache()) +proc commandTags*(conf: ConfigRef) = + var ast = parseFile(conf.projectMainIdx.FileIndex, newIdentCache(), conf) if ast == nil: return - var d = newDocumentor(gProjectFull, options.gConfigVars) + var d = newDocumentor(conf.projectFull, conf) d.hasToc = true var content: Rope generateTags(d, ast, content) - if optStdout in gGlobalOptions: + if optStdout in d.conf.globalOptions: writeRope(stdout, content) else: #echo getOutFile(gProjectFull, TagsExt) - writeRope(content, getOutFile(gProjectFull, TagsExt), useWarning = false) + writeRope(content, getOutFile(conf, conf.projectFull, TagsExt), useWarning = false) -proc commandBuildIndex*() = - var content = mergeIndexes(gProjectFull).rope +proc commandBuildIndex*(conf: ConfigRef) = + var content = mergeIndexes(conf.projectFull).rope - let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", + let code = ropeFormatNamedVars(conf, getConfigVar(conf, "doc.file"), ["title", "tableofcontents", "moduledesc", "date", "time", "content", "author", "version", "analytics"], ["Index".rope, nil, nil, rope(getDateStr()), rope(getClockStr()), content, nil, nil, nil]) # no analytics because context is not available - writeRope(code, getOutFile("theindex", HtmlExt)) + writeRope(code, getOutFile(conf, "theindex", HtmlExt)) diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index 118f1c7c5..d9a73e1cd 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -25,8 +25,8 @@ template closeImpl(body: untyped) {.dirty.} = var g = PGen(p) let useWarning = sfMainModule notin g.module.flags #echo g.module.name.s, " ", g.module.owner.id, " ", gMainPackageId - if (g.module.owner.id == gMainPackageId and gWholeProject) or - sfMainModule in g.module.flags: + if (g.module.owner.id == gMainPackageId and optWholeProject in g.doc.conf.globalOptions) or + sfMainModule in g.module.flags: body try: generateIndex(g.doc) @@ -55,7 +55,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = var g: PGen new(g) g.module = module - var d = newDocumentor(module.filename, options.gConfigVars) + var d = newDocumentor(module.filename, graph.config) d.hasToc = true g.doc = d result = g diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index 51b65258b..0e3d0609d 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -86,10 +86,10 @@ proc mapType(t: ast.PType): ptr libffi.TType = 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, + of tyVar, tyLent, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr, tyStmt, tyTypeDesc, tyProc, tyArray, tyStatic, tyNil: result = addr libffi.type_pointer - of tyDistinct, tyAlias: + of tyDistinct, tyAlias, tySink: result = mapType(t.sons[0]) else: result = nil @@ -112,12 +112,12 @@ template `+!`(x, y: untyped): untyped = proc packSize(v: PNode, typ: PType): int = ## computes the size of the blob case typ.kind - of tyPtr, tyRef, tyVar: + of tyPtr, tyRef, tyVar, tyLent: if v.kind in {nkNilLit, nkPtrLit}: result = sizeof(pointer) else: result = sizeof(pointer) + packSize(v.sons[0], typ.lastSon) - of tyDistinct, tyGenericInst, tyAlias: + of tyDistinct, tyGenericInst, tyAlias, tySink: result = packSize(v, typ.sons[0]) of tyArray: # consider: ptr array[0..1000_000, int] which is common for interfacing; @@ -151,7 +151,7 @@ proc getField(n: PNode; position: int): PSym = else: discard proc packObject(x: PNode, typ: PType, res: pointer) = - internalAssert x.kind in {nkObjConstr, nkPar} + internalAssert x.kind in {nkObjConstr, nkPar, nkTupleConstr} # compute the field's offsets: discard typ.getSize for i in countup(ord(x.kind == nkObjConstr), sonsLen(x) - 1): @@ -209,7 +209,7 @@ proc pack(v: PNode, typ: PType, res: pointer) = awr(cstring, cstring(v.strVal)) else: globalError(v.info, "cannot map pointer/proc value to FFI") - of tyPtr, tyRef, tyVar: + of tyPtr, tyRef, tyVar, tyLent: if v.kind == nkNilLit: # nothing to do since the memory is 0 initialized anyway discard @@ -231,7 +231,7 @@ proc pack(v: PNode, typ: PType, res: pointer) = packObject(v, typ, res) of tyNil: discard - of tyDistinct, tyGenericInst, tyAlias: + of tyDistinct, tyGenericInst, tyAlias, tySink: pack(v, typ.sons[0], res) else: globalError(v.info, "cannot map value to FFI " & typeToString(v.typ)) @@ -260,14 +260,14 @@ proc unpackObject(x: pointer, typ: PType, n: PNode): PNode = # 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 = newNode(nkTupleConstr) 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}: + if result.kind notin {nkObjConstr, nkPar, nkTupleConstr}: globalError(n.info, "cannot map value from FFI") if typ.n.isNil: globalError(n.info, "cannot unpack unnamed tuple") @@ -364,7 +364,7 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode = result = n else: awi(nkPtrLit, cast[ByteAddress](p)) - of tyPtr, tyRef, tyVar: + of tyPtr, tyRef, tyVar, tyLent: let p = rd(pointer, x) if p.isNil: setNil() @@ -388,14 +388,14 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode = aws(nkStrLit, $p) of tyNil: setNil() - of tyDistinct, tyGenericInst, tyAlias: + of tyDistinct, tyGenericInst, tyAlias, tySink: result = unpack(x, typ.lastSon, 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, + if x.kind == nkPtrLit and x.typ.kind in {tyPtr, tyRef, tyVar, tyLent, tyPointer, tyProc, tyCString, tyString, tySequence}: result = newNodeIT(x.kind, x.info, destTyp) diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 704ff819c..01c56ec9c 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -14,11 +14,12 @@ import rodread type - TemplCtx {.pure, final.} = object + TemplCtx = object owner, genSymOwner: PSym instLines: bool # use the instantiation lines numbers mapping: TIdTable # every gensym'ed symbol needs to be mapped to some # new symbol + config: ConfigRef proc copyNode(ctx: TemplCtx, a, b: PNode): PNode = result = copyNode(a) @@ -42,7 +43,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = s.kind == skType and s.typ != nil and s.typ.kind == tyGenericParam: handleParam actual.sons[s.owner.typ.len + s.position - 1] else: - internalAssert sfGenSym in s.flags or s.kind == skType + internalAssert c.config, sfGenSym in s.flags or s.kind == skType var x = PSym(idTableGet(c.mapping, s)) if x == nil: x = copySym(s, false) @@ -59,11 +60,16 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = evalTemplateAux(templ.sons[i], actual, c, res) result.add res -proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode = +const + errWrongNumberOfArguments = "wrong number of arguments" + errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters" + errTemplateInstantiationTooNested = "template instantiation too nested" + +proc evalTemplateArgs(n: PNode, s: PSym; conf: ConfigRef; fromHlo: bool): PNode = # if the template has zero arguments, it can be called without ``()`` # `n` is then a nkSym or something similar var totalParams = case n.kind - of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: n.len-1 + of nkCallKinds: n.len-1 else: 0 var @@ -82,10 +88,10 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode = if givenRegularParams < 0: givenRegularParams = 0 if totalParams > expectedRegularParams + genericParams: - globalError(n.info, errWrongNumberOfArguments) + globalError(conf, n.info, errWrongNumberOfArguments) if totalParams < genericParams: - globalError(n.info, errMissingGenericParamsForTemplate, + globalError(conf, n.info, errMissingGenericParamsForTemplate % n.renderTree) result = newNodeI(nkArgList, n.info) @@ -97,7 +103,7 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode = for i in givenRegularParams+1 .. expectedRegularParams: let default = s.typ.n.sons[i].sym.ast if default.isNil or default.kind == nkEmpty: - localError(n.info, errWrongNumberOfArguments) + localError(conf, n.info, errWrongNumberOfArguments) addSon(result, ast.emptyNode) else: addSon(result, default.copyTree) @@ -106,8 +112,9 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode = for i in 1 .. genericParams: result.addSon n.sons[givenRegularParams + i] -var evalTemplateCounter* = 0 - # to prevent endless recursion in templates instantiation +# to prevent endless recursion in template instantiation +const evalTemplateLimit* = 1000 +var evalTemplateCounter* = 0 # XXX remove this global proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode = when true: @@ -131,17 +138,19 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode = result.add res result.typ = res.typ -proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; fromHlo=false): PNode = +proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; + conf: ConfigRef; fromHlo=false): PNode = inc(evalTemplateCounter) - if evalTemplateCounter > 100: - globalError(n.info, errTemplateInstantiationTooNested) + if evalTemplateCounter > evalTemplateLimit: + globalError(conf, n.info, errTemplateInstantiationTooNested) result = n # replace each param by the corresponding node: - var args = evalTemplateArgs(n, tmpl, fromHlo) + var args = evalTemplateArgs(n, tmpl, conf, fromHlo) var ctx: TemplCtx ctx.owner = tmpl ctx.genSymOwner = genSymOwner + ctx.config = conf initIdTable(ctx.mapping) let body = tmpl.getBody @@ -150,7 +159,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; fromHlo=false): PNode = evalTemplateAux(body, args, ctx, result) if result.len == 1: result = result.sons[0] else: - localError(result.info, errIllFormedAstX, + localError(conf, result.info, "illformed AST: " & renderTree(result, {renderNoComments})) else: result = copyNode(body) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 62990593d..3f0e6f611 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -14,7 +14,7 @@ import ropes, os, strutils, osproc, platform, condsyms, options, msgs, - securehash, streams + configuration, std / sha1, streams #from debuginfo import writeDebugInfo @@ -176,10 +176,10 @@ compiler bcc: result = ( name: "bcc", objExt: "obj", - optSpeed: " -O2 -6 ", + optSpeed: " -O3 -6 ", optSize: " -O1 -6 ", - compilerExe: "bcc32", - cppCompiler: "", + compilerExe: "bcc32c", + cppCompiler: "cpp32c", compileTmpl: "-c $options $include -o$objfile $file", buildGui: " -tW", buildDll: " -tWD", @@ -193,7 +193,9 @@ compiler bcc: pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", structStmtFmt: "$1 $2", - props: {hasCpp}) + props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, + hasAttribute}) + # Digital Mars C Compiler compiler dmc: @@ -374,80 +376,81 @@ proc nameToCC*(name: string): TSystemCC = return i result = ccNone -proc getConfigVar(c: TSystemCC, suffix: string): string = +proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string = # use ``cpu.os.cc`` for cross compilation, unless ``--compileOnly`` is given # for niminst support let fullSuffix = - if gCmd == cmdCompileToCpp: + if conf.cmd == cmdCompileToCpp: ".cpp" & suffix - elif gCmd == cmdCompileToOC: + elif conf.cmd == cmdCompileToOC: ".objc" & suffix - elif gCmd == cmdCompileToJS: + elif conf.cmd == cmdCompileToJS: ".js" & suffix else: suffix if (platform.hostOS != targetOS or platform.hostCPU != targetCPU) and - optCompileOnly notin gGlobalOptions: + optCompileOnly notin conf.globalOptions: let fullCCname = platform.CPU[targetCPU].name & '.' & platform.OS[targetOS].name & '.' & CC[c].name & fullSuffix - result = getConfigVar(fullCCname) + result = getConfigVar(conf, fullCCname) if result.len == 0: # not overriden for this cross compilation setting? - result = getConfigVar(CC[c].name & fullSuffix) + result = getConfigVar(conf, CC[c].name & fullSuffix) else: - result = getConfigVar(CC[c].name & fullSuffix) + result = getConfigVar(conf, CC[c].name & fullSuffix) -proc setCC*(ccname: string) = +proc setCC*(conf: ConfigRef; ccname: string; info: TLineInfo) = cCompiler = nameToCC(ccname) - if cCompiler == ccNone: rawMessage(errUnknownCcompiler, ccname) - compileOptions = getConfigVar(cCompiler, ".options.always") + if cCompiler == ccNone: + localError(conf, info, "unknown C compiler: '$1'" % ccname) + compileOptions = getConfigVar(conf, cCompiler, ".options.always") linkOptions = "" - ccompilerpath = getConfigVar(cCompiler, ".path") - for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name) - defineSymbol(CC[cCompiler].name) + ccompilerpath = getConfigVar(conf, cCompiler, ".path") + for i in countup(low(CC), high(CC)): undefSymbol(conf.symbols, CC[i].name) + defineSymbol(conf.symbols, CC[cCompiler].name) proc addOpt(dest: var string, src: string) = if len(dest) == 0 or dest[len(dest)-1] != ' ': add(dest, " ") add(dest, src) -proc addLinkOption*(option: string) = +proc addLinkOption*(conf: ConfigRef; option: string) = addOpt(linkOptions, option) -proc addCompileOption*(option: string) = +proc addCompileOption*(conf: ConfigRef; option: string) = if strutils.find(compileOptions, option, 0) < 0: addOpt(compileOptions, option) -proc addLinkOptionCmd*(option: string) = +proc addLinkOptionCmd*(conf: ConfigRef; option: string) = addOpt(linkOptionsCmd, option) -proc addCompileOptionCmd*(option: string) = +proc addCompileOptionCmd*(conf: ConfigRef; option: string) = compileOptionsCmd.add(option) -proc initVars*() = +proc initVars*(conf: ConfigRef) = # we need to define the symbol here, because ``CC`` may have never been set! - for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name) - defineSymbol(CC[cCompiler].name) - addCompileOption(getConfigVar(cCompiler, ".options.always")) + for i in countup(low(CC), high(CC)): undefSymbol(conf.symbols, CC[i].name) + defineSymbol(conf.symbols, CC[cCompiler].name) + addCompileOption(conf, getConfigVar(conf, cCompiler, ".options.always")) #addLinkOption(getConfigVar(cCompiler, ".options.linker")) if len(ccompilerpath) == 0: - ccompilerpath = getConfigVar(cCompiler, ".path") + ccompilerpath = getConfigVar(conf, cCompiler, ".path") -proc completeCFilePath*(cfile: string, createSubDir: bool = true): string = - result = completeGeneratedFilePath(cfile, createSubDir) +proc completeCFilePath*(conf: ConfigRef; cfile: string, createSubDir: bool = true): string = + result = completeGeneratedFilePath(conf, cfile, createSubDir) -proc toObjFile*(filename: string): string = +proc toObjFile*(conf: ConfigRef; filename: string): string = # Object file for compilation #if filename.endsWith(".cpp"): # result = changeFileExt(filename, "cpp." & CC[cCompiler].objExt) #else: result = changeFileExt(filename, CC[cCompiler].objExt) -proc addFileToCompile*(cf: Cfile) = +proc addFileToCompile*(conf: ConfigRef; cf: Cfile) = toCompile.add(cf) -proc resetCompilationLists* = +proc resetCompilationLists*(conf: ConfigRef) = toCompile.setLen 0 ## XXX: we must associate these with their originating module # when the module is loaded/unloaded it adds/removes its items @@ -455,108 +458,112 @@ proc resetCompilationLists* = # Maybe we can do that in checkDep on the other hand? externalToLink.setLen 0 -proc addExternalFileToLink*(filename: string) = +proc addExternalFileToLink*(conf: ConfigRef; filename: string) = externalToLink.insert(filename, 0) -proc execWithEcho(cmd: string, msg = hintExecuting): int = - rawMessage(msg, cmd) +proc execWithEcho(conf: ConfigRef; cmd: string, msg = hintExecuting): int = + rawMessage(conf, msg, cmd) result = execCmd(cmd) -proc execExternalProgram*(cmd: string, msg = hintExecuting) = - if execWithEcho(cmd, msg) != 0: - rawMessage(errExecutionOfProgramFailed, cmd) +proc execExternalProgram*(conf: ConfigRef; cmd: string, msg = hintExecuting) = + if execWithEcho(conf, cmd, msg) != 0: + rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" % + cmd) -proc generateScript(projectFile: string, script: Rope) = +proc generateScript(conf: ConfigRef; projectFile: string, script: Rope) = let (dir, name, ext) = splitFile(projectFile) - writeRope(script, dir / addFileExt("compile_" & name, + writeRope(script, getNimcacheDir(conf) / addFileExt("compile_" & name, platform.OS[targetOS].scriptExt)) + copyFile(conf.libpath / "nimbase.h", getNimcacheDir(conf) / "nimbase.h") -proc getOptSpeed(c: TSystemCC): string = - result = getConfigVar(c, ".options.speed") +proc getOptSpeed(conf: ConfigRef; c: TSystemCC): string = + result = getConfigVar(conf, c, ".options.speed") if result == "": result = CC[c].optSpeed # use default settings from this file -proc getDebug(c: TSystemCC): string = - result = getConfigVar(c, ".options.debug") +proc getDebug(conf: ConfigRef; c: TSystemCC): string = + result = getConfigVar(conf, c, ".options.debug") if result == "": result = CC[c].debug # use default settings from this file -proc getOptSize(c: TSystemCC): string = - result = getConfigVar(c, ".options.size") +proc getOptSize(conf: ConfigRef; c: TSystemCC): string = + result = getConfigVar(conf, c, ".options.size") if result == "": result = CC[c].optSize # use default settings from this file -proc noAbsolutePaths: bool {.inline.} = +proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} = # We used to check current OS != specified OS, but this makes no sense # really: Cross compilation from Linux to Linux for example is entirely # reasonable. # `optGenMapping` is included here for niminst. - result = gGlobalOptions * {optGenScript, optGenMapping} != {} + result = conf.globalOptions * {optGenScript, optGenMapping} != {} -proc cFileSpecificOptions(cfilename: string): string = +proc cFileSpecificOptions(conf: ConfigRef; cfilename: string): string = result = compileOptions for option in compileOptionsCmd: if strutils.find(result, option, 0) < 0: addOpt(result, option) - var trunk = splitFile(cfilename).name - if optCDebug in gGlobalOptions: - var key = trunk & ".debug" - if existsConfigVar(key): addOpt(result, getConfigVar(key)) - else: addOpt(result, getDebug(cCompiler)) - if optOptimizeSpeed in gOptions: - var key = trunk & ".speed" - if existsConfigVar(key): addOpt(result, getConfigVar(key)) - else: addOpt(result, getOptSpeed(cCompiler)) - elif optOptimizeSize in gOptions: - var key = trunk & ".size" - if existsConfigVar(key): addOpt(result, getConfigVar(key)) - else: addOpt(result, getOptSize(cCompiler)) - var key = trunk & ".always" - if existsConfigVar(key): addOpt(result, getConfigVar(key)) - -proc getCompileOptions: string = - result = cFileSpecificOptions("__dummy__") - -proc getLinkOptions: string = + let trunk = splitFile(cfilename).name + if optCDebug in conf.globalOptions: + let key = trunk & ".debug" + if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key)) + else: addOpt(result, getDebug(conf, cCompiler)) + if optOptimizeSpeed in conf.options: + let key = trunk & ".speed" + if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key)) + else: addOpt(result, getOptSpeed(conf, cCompiler)) + elif optOptimizeSize in conf.options: + let key = trunk & ".size" + if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key)) + else: addOpt(result, getOptSize(conf, cCompiler)) + let key = trunk & ".always" + if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key)) + +proc getCompileOptions(conf: ConfigRef): string = + result = cFileSpecificOptions(conf, "__dummy__") + +proc getLinkOptions(conf: ConfigRef): string = result = linkOptions & " " & linkOptionsCmd & " " for linkedLib in items(cLinkedLibs): result.add(CC[cCompiler].linkLibCmd % linkedLib.quoteShell) for libDir in items(cLibs): result.add(join([CC[cCompiler].linkDirCmd, libDir.quoteShell])) -proc needsExeExt(): bool {.inline.} = - result = (optGenScript in gGlobalOptions and targetOS == osWindows) or +proc needsExeExt(conf: ConfigRef): bool {.inline.} = + result = (optGenScript in conf.globalOptions and targetOS == osWindows) or (platform.hostOS == osWindows) -proc getCompilerExe(compiler: TSystemCC; cfile: string): string = - result = if gCmd == cmdCompileToCpp and not cfile.endsWith(".c"): +proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: string): string = + result = if conf.cmd == cmdCompileToCpp and not cfile.endsWith(".c"): CC[compiler].cppCompiler else: CC[compiler].compilerExe if result.len == 0: - rawMessage(errCompilerDoesntSupportTarget, CC[compiler].name) + rawMessage(conf, errGenerated, + "Compiler '$1' doesn't support the requested target" % + CC[compiler].name) -proc getLinkerExe(compiler: TSystemCC): string = +proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string = result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe - elif gMixedMode and gCmd != cmdCompileToCpp: CC[compiler].cppCompiler - else: compiler.getCompilerExe("") + elif gMixedMode and conf.cmd != cmdCompileToCpp: CC[compiler].cppCompiler + else: getCompilerExe(conf, compiler, "") -proc getCompileCFileCmd*(cfile: Cfile): string = +proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string = var c = cCompiler - var options = cFileSpecificOptions(cfile.cname) - var exe = getConfigVar(c, ".exe") - if exe.len == 0: exe = c.getCompilerExe(cfile.cname) + var options = cFileSpecificOptions(conf, cfile.cname) + var exe = getConfigVar(conf, c, ".exe") + if exe.len == 0: exe = getCompilerExe(conf, c, cfile.cname) - if needsExeExt(): exe = addFileExt(exe, "exe") - if optGenDynLib in gGlobalOptions and + if needsExeExt(conf): exe = addFileExt(exe, "exe") + if optGenDynLib in conf.globalOptions and ospNeedsPIC in platform.OS[targetOS].props: add(options, ' ' & CC[c].pic) var includeCmd, compilePattern: string - if not noAbsolutePaths(): + if not noAbsolutePaths(conf): # compute include paths: - includeCmd = CC[c].includeCmd & quoteShell(libpath) + includeCmd = CC[c].includeCmd & quoteShell(conf.libpath) for includeDir in items(cIncludes): includeCmd.add(join([CC[c].includeCmd, includeDir.quoteShell])) @@ -564,18 +571,18 @@ proc getCompileCFileCmd*(cfile: Cfile): string = compilePattern = joinPath(ccompilerpath, exe) else: includeCmd = "" - compilePattern = c.getCompilerExe(cfile.cname) + compilePattern = getCompilerExe(conf, c, cfile.cname) - var cf = if noAbsolutePaths(): extractFilename(cfile.cname) + var cf = if noAbsolutePaths(conf): extractFilename(cfile.cname) else: cfile.cname var objfile = if cfile.obj.len == 0: - if not cfile.flags.contains(CfileFlag.External) or noAbsolutePaths(): - toObjFile(cf) + if not cfile.flags.contains(CfileFlag.External) or noAbsolutePaths(conf): + toObjFile(conf, cf) else: - completeCFilePath(toObjFile(cf)) - elif noAbsolutePaths(): + completeCFilePath(conf, toObjFile(conf, cf)) + elif noAbsolutePaths(conf): extractFilename(cfile.obj) else: cfile.obj @@ -584,30 +591,30 @@ proc getCompileCFileCmd*(cfile: Cfile): string = cf = quoteShell(cf) result = quoteShell(compilePattern % [ "file", cf, "objfile", objfile, "options", options, - "include", includeCmd, "nim", getPrefixDir(), - "nim", getPrefixDir(), "lib", libpath]) + "include", includeCmd, "nim", getPrefixDir(conf), + "nim", getPrefixDir(conf), "lib", conf.libpath]) add(result, ' ') addf(result, CC[c].compileTmpl, [ "file", cf, "objfile", objfile, "options", options, "include", includeCmd, - "nim", quoteShell(getPrefixDir()), - "nim", quoteShell(getPrefixDir()), - "lib", quoteShell(libpath)]) + "nim", quoteShell(getPrefixDir(conf)), + "nim", quoteShell(getPrefixDir(conf)), + "lib", quoteShell(conf.libpath)]) -proc footprint(cfile: Cfile): SecureHash = +proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash = result = secureHash( $secureHashFile(cfile.cname) & platform.OS[targetOS].name & platform.CPU[targetCPU].name & extccomp.CC[extccomp.cCompiler].name & - getCompileCFileCmd(cfile)) + getCompileCFileCmd(conf, cfile)) -proc externalFileChanged(cfile: Cfile): bool = - if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}: +proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool = + if conf.cmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}: return false - var hashFile = toGeneratedFile(cfile.cname.withPackageName, "sha1") - var currentHash = footprint(cfile) + var hashFile = toGeneratedFile(conf, conf.withPackageName(cfile.cname), "sha1") + var currentHash = footprint(conf, cfile) var f: File if open(f, hashFile, fmRead): let oldHash = parseSecureHash(f.readLine()) @@ -620,133 +627,137 @@ proc externalFileChanged(cfile: Cfile): bool = f.writeLine($currentHash) close(f) -proc addExternalFileToCompile*(c: var Cfile) = - if optForceFullMake notin gGlobalOptions and not externalFileChanged(c): +proc addExternalFileToCompile*(conf: ConfigRef; c: var Cfile) = + if optForceFullMake notin conf.globalOptions and not externalFileChanged(conf, c): c.flags.incl CfileFlag.Cached toCompile.add(c) -proc addExternalFileToCompile*(filename: string) = +proc addExternalFileToCompile*(conf: ConfigRef; filename: string) = var c = Cfile(cname: filename, - obj: toObjFile(completeCFilePath(changeFileExt(filename, ""), false)), + obj: toObjFile(conf, completeCFilePath(conf, changeFileExt(filename, ""), false)), flags: {CfileFlag.External}) - addExternalFileToCompile(c) + addExternalFileToCompile(conf, c) -proc compileCFile(list: CFileList, script: var Rope, cmds: var TStringSeq, +proc compileCFile(conf: ConfigRef; list: CFileList, script: var Rope, cmds: var TStringSeq, prettyCmds: var TStringSeq) = for it in list: # call the C compiler for the .c file: if it.flags.contains(CfileFlag.Cached): continue - var compileCmd = getCompileCFileCmd(it) - if optCompileOnly notin gGlobalOptions: + var compileCmd = getCompileCFileCmd(conf, it) + if optCompileOnly notin conf.globalOptions: add(cmds, compileCmd) let (_, name, _) = splitFile(it.cname) add(prettyCmds, "CC: " & name) - if optGenScript in gGlobalOptions: + if optGenScript in conf.globalOptions: add(script, compileCmd) add(script, tnl) -proc getLinkCmd(projectfile, objfiles: string): string = - if optGenStaticLib in gGlobalOptions: +proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string = + if optGenStaticLib in conf.globalOptions: var libname: string - if options.outFile.len > 0: - libname = options.outFile.expandTilde + if conf.outFile.len > 0: + libname = conf.outFile.expandTilde if not libname.isAbsolute(): libname = getCurrentDir() / libname else: - libname = (libNameTmpl() % splitFile(gProjectName).name) + libname = (libNameTmpl() % splitFile(conf.projectName).name) result = CC[cCompiler].buildLib % ["libfile", libname, "objfiles", objfiles] else: - var linkerExe = getConfigVar(cCompiler, ".linkerexe") - if len(linkerExe) == 0: linkerExe = cCompiler.getLinkerExe + var linkerExe = getConfigVar(conf, cCompiler, ".linkerexe") + if len(linkerExe) == 0: linkerExe = getLinkerExe(conf, cCompiler) # bug #6452: We must not use ``quoteShell`` here for ``linkerExe`` - if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe") - if noAbsolutePaths(): result = linkerExe + if needsExeExt(conf): linkerExe = addFileExt(linkerExe, "exe") + if noAbsolutePaths(conf): result = linkerExe else: result = joinPath(ccompilerpath, linkerExe) - let buildgui = if optGenGuiApp in gGlobalOptions: CC[cCompiler].buildGui + let buildgui = if optGenGuiApp in conf.globalOptions: CC[cCompiler].buildGui else: "" var exefile, builddll: string - if optGenDynLib in gGlobalOptions: + if optGenDynLib in conf.globalOptions: exefile = platform.OS[targetOS].dllFrmt % splitFile(projectfile).name builddll = CC[cCompiler].buildDll else: exefile = splitFile(projectfile).name & platform.OS[targetOS].exeExt builddll = "" - if options.outFile.len > 0: - exefile = options.outFile.expandTilde + if conf.outFile.len > 0: + exefile = conf.outFile.expandTilde if not exefile.isAbsolute(): exefile = getCurrentDir() / exefile - if not noAbsolutePaths(): + if not noAbsolutePaths(conf): if not exefile.isAbsolute(): exefile = joinPath(splitFile(projectfile).dir, exefile) when false: - if optCDebug in gGlobalOptions: + if optCDebug in conf.globalOptions: writeDebugInfo(exefile.changeFileExt("ndb")) exefile = quoteShell(exefile) - let linkOptions = getLinkOptions() & " " & - getConfigVar(cCompiler, ".options.linker") - var linkTmpl = getConfigVar(cCompiler, ".linkTmpl") + let linkOptions = getLinkOptions(conf) & " " & + getConfigVar(conf, cCompiler, ".options.linker") + var linkTmpl = getConfigVar(conf, cCompiler, ".linkTmpl") if linkTmpl.len == 0: linkTmpl = CC[cCompiler].linkTmpl result = quoteShell(result % ["builddll", builddll, "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles, - "exefile", exefile, "nim", getPrefixDir(), "lib", libpath]) + "exefile", exefile, "nim", getPrefixDir(conf), "lib", conf.libpath]) result.add ' ' addf(result, linkTmpl, ["builddll", builddll, "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles, "exefile", exefile, - "nim", quoteShell(getPrefixDir()), - "lib", quoteShell(libpath)]) + "nim", quoteShell(getPrefixDir(conf)), + "lib", quoteShell(conf.libpath)]) -template tryExceptOSErrorMessage(errorPrefix: string = "", body: untyped): typed = +template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body: untyped): typed = try: body except OSError: let ose = (ref OSError)(getCurrentException()) if errorPrefix.len > 0: - rawMessage(errGenerated, errorPrefix & " " & ose.msg & " " & $ose.errorCode) + rawMessage(conf, errGenerated, errorPrefix & " " & ose.msg & " " & $ose.errorCode) else: - rawMessage(errExecutionOfProgramFailed, ose.msg & " " & $ose.errorCode) + rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" % + (ose.msg & " " & $ose.errorCode)) raise -proc execLinkCmd(linkCmd: string) = - tryExceptOSErrorMessage("invocation of external linker program failed."): - execExternalProgram(linkCmd, - if optListCmd in gGlobalOptions or gVerbosity > 1: hintExecuting else: hintLinking) +proc execLinkCmd(conf: ConfigRef; linkCmd: string) = + tryExceptOSErrorMessage(conf, "invocation of external linker program failed."): + execExternalProgram(conf, linkCmd, + if optListCmd in conf.globalOptions or conf.verbosity > 1: hintExecuting else: hintLinking) -proc execCmdsInParallel(cmds: seq[string]; prettyCb: proc (idx: int)) = +proc execCmdsInParallel(conf: ConfigRef; cmds: seq[string]; prettyCb: proc (idx: int)) = let runCb = proc (idx: int, p: Process) = let exitCode = p.peekExitCode if exitCode != 0: - rawMessage(errGenerated, "execution of an external compiler program '" & + rawMessage(conf, errGenerated, "execution of an external compiler program '" & cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n" & p.outputStream.readAll.strip) - if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors() + if conf.numberOfProcessors == 0: conf.numberOfProcessors = countProcessors() var res = 0 - if gNumberOfProcessors <= 1: + if conf.numberOfProcessors <= 1: for i in countup(0, high(cmds)): - tryExceptOSErrorMessage("invocation of external compiler program failed."): - res = execWithEcho(cmds[i]) - if res != 0: rawMessage(errExecutionOfProgramFailed, cmds[i]) + tryExceptOSErrorMessage(conf, "invocation of external compiler program failed."): + res = execWithEcho(conf, cmds[i]) + if res != 0: + rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" % + cmds[i]) else: - tryExceptOSErrorMessage("invocation of external compiler program failed."): - if optListCmd in gGlobalOptions or gVerbosity > 1: + tryExceptOSErrorMessage(conf, "invocation of external compiler program failed."): + if optListCmd in conf.globalOptions or conf.verbosity > 1: res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath}, - gNumberOfProcessors, afterRunEvent=runCb) - elif gVerbosity == 1: + conf.numberOfProcessors, afterRunEvent=runCb) + elif conf.verbosity == 1: res = execProcesses(cmds, {poStdErrToStdOut, poUsePath}, - gNumberOfProcessors, prettyCb, afterRunEvent=runCb) + conf.numberOfProcessors, prettyCb, afterRunEvent=runCb) else: res = execProcesses(cmds, {poStdErrToStdOut, poUsePath}, - gNumberOfProcessors, afterRunEvent=runCb) + conf.numberOfProcessors, afterRunEvent=runCb) if res != 0: - if gNumberOfProcessors <= 1: - rawMessage(errExecutionOfProgramFailed, cmds.join()) + if conf.numberOfProcessors <= 1: + rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" % + cmds.join()) -proc callCCompiler*(projectfile: string) = +proc callCCompiler*(conf: ConfigRef; projectfile: string) = var linkCmd: string - if gGlobalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}: + if conf.globalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}: return # speed up that call if only compiling and no script shall be # generated #var c = cCompiler @@ -755,36 +766,36 @@ proc callCCompiler*(projectfile: string) = var prettyCmds: TStringSeq = @[] let prettyCb = proc (idx: int) = echo prettyCmds[idx] - compileCFile(toCompile, script, cmds, prettyCmds) - if optCompileOnly notin gGlobalOptions: - execCmdsInParallel(cmds, prettyCb) - if optNoLinking notin gGlobalOptions: + compileCFile(conf, toCompile, script, cmds, prettyCmds) + if optCompileOnly notin conf.globalOptions: + execCmdsInParallel(conf, cmds, prettyCb) + if optNoLinking notin conf.globalOptions: # call the linker: var objfiles = "" for it in externalToLink: - let objFile = if noAbsolutePaths(): it.extractFilename else: it + let objFile = if noAbsolutePaths(conf): it.extractFilename else: it add(objfiles, ' ') add(objfiles, quoteShell( addFileExt(objFile, CC[cCompiler].objExt))) for x in toCompile: - let objFile = if noAbsolutePaths(): x.obj.extractFilename else: x.obj + let objFile = if noAbsolutePaths(conf): x.obj.extractFilename else: x.obj add(objfiles, ' ') add(objfiles, quoteShell(objFile)) - linkCmd = getLinkCmd(projectfile, objfiles) - if optCompileOnly notin gGlobalOptions: - execLinkCmd(linkCmd) + linkCmd = getLinkCmd(conf, projectfile, objfiles) + if optCompileOnly notin conf.globalOptions: + execLinkCmd(conf, linkCmd) else: linkCmd = "" - if optGenScript in gGlobalOptions: + if optGenScript in conf.globalOptions: add(script, linkCmd) add(script, tnl) - generateScript(projectfile, script) + generateScript(conf, projectfile, script) #from json import escapeJson import json -proc writeJsonBuildInstructions*(projectfile: string) = +proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: string) = template lit(x: untyped) = f.write x template str(x: untyped) = when compiles(escapeJson(x, buf)): @@ -794,11 +805,11 @@ proc writeJsonBuildInstructions*(projectfile: string) = else: f.write escapeJson(x) - proc cfiles(f: File; buf: var string; clist: CfileList, isExternal: bool) = + proc cfiles(conf: ConfigRef; f: File; buf: var string; clist: CfileList, isExternal: bool) = var pastStart = false for it in clist: if CfileFlag.Cached in it.flags: continue - let compileCmd = getCompileCFileCmd(it) + let compileCmd = getCompileCFileCmd(conf, it) if pastStart: lit "],\L" lit "[" str it.cname @@ -807,11 +818,11 @@ proc writeJsonBuildInstructions*(projectfile: string) = pastStart = true lit "]\L" - proc linkfiles(f: File; buf, objfiles: var string; clist: CfileList; + proc linkfiles(conf: ConfigRef; f: File; buf, objfiles: var string; clist: CfileList; llist: seq[string]) = var pastStart = false for it in llist: - let objfile = if noAbsolutePaths(): it.extractFilename + let objfile = if noAbsolutePaths(conf): it.extractFilename else: it let objstr = addFileExt(objfile, CC[cCompiler].objExt) add(objfiles, ' ') @@ -832,25 +843,25 @@ proc writeJsonBuildInstructions*(projectfile: string) = var buf = newStringOfCap(50) let file = projectfile.splitFile.name - let jsonFile = toGeneratedFile(file, "json") + let jsonFile = toGeneratedFile(conf, file, "json") var f: File if open(f, jsonFile, fmWrite): lit "{\"compile\":[\L" - cfiles(f, buf, toCompile, false) + cfiles(conf, f, buf, toCompile, false) lit "],\L\"link\":[\L" var objfiles = "" # XXX add every file here that is to link - linkfiles(f, buf, objfiles, toCompile, externalToLink) + linkfiles(conf, f, buf, objfiles, toCompile, externalToLink) lit "],\L\"linkcmd\": " - str getLinkCmd(projectfile, objfiles) + str getLinkCmd(conf, projectfile, objfiles) lit "\L}\L" close(f) -proc runJsonBuildInstructions*(projectfile: string) = +proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: string) = let file = projectfile.splitFile.name - let jsonFile = toGeneratedFile(file, "json") + let jsonFile = toGeneratedFile(conf, file, "json") try: let data = json.parseFile(jsonFile) let toCompile = data["compile"] @@ -867,32 +878,32 @@ proc runJsonBuildInstructions*(projectfile: string) = let prettyCb = proc (idx: int) = echo prettyCmds[idx] - execCmdsInParallel(cmds, prettyCb) + execCmdsInParallel(conf, cmds, prettyCb) let linkCmd = data["linkcmd"] doAssert linkCmd.kind == JString - execLinkCmd(linkCmd.getStr) + execLinkCmd(conf, linkCmd.getStr) except: echo getCurrentException().getStackTrace() quit "error evaluating JSON file: " & jsonFile -proc genMappingFiles(list: CFileList): Rope = +proc genMappingFiles(conf: ConfigRef; list: CFileList): Rope = for it in list: addf(result, "--file:r\"$1\"$N", [rope(it.cname)]) -proc writeMapping*(gSymbolMapping: Rope) = - if optGenMapping notin gGlobalOptions: return +proc writeMapping*(conf: ConfigRef; symbolMapping: Rope) = + if optGenMapping notin conf.globalOptions: return var code = rope("[C_Files]\n") - add(code, genMappingFiles(toCompile)) + add(code, genMappingFiles(conf, toCompile)) add(code, "\n[C_Compiler]\nFlags=") - add(code, strutils.escape(getCompileOptions())) + add(code, strutils.escape(getCompileOptions(conf))) add(code, "\n[Linker]\nFlags=") - add(code, strutils.escape(getLinkOptions() & " " & - getConfigVar(cCompiler, ".options.linker"))) + add(code, strutils.escape(getLinkOptions(conf) & " " & + getConfigVar(conf, cCompiler, ".options.linker"))) add(code, "\n[Environment]\nlibpath=") - add(code, strutils.escape(libpath)) + add(code, strutils.escape(conf.libpath)) - addf(code, "\n[Symbols]$n$1", [gSymbolMapping]) - writeRope(code, joinPath(gProjectPath, "mapping.txt")) + addf(code, "\n[Symbols]$n$1", [symbolMapping]) + writeRope(code, joinPath(conf.projectPath, "mapping.txt")) diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim index ca9a3a801..6c16a0b4e 100644 --- a/compiler/filter_tmpl.nim +++ b/compiler/filter_tmpl.nim @@ -27,7 +27,7 @@ type emit, conc, toStr: string curly, bracket, par: int pendingExprLine: bool - + config: ConfigRef const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'} @@ -35,16 +35,15 @@ const proc newLine(p: var TTmplParser) = llStreamWrite(p.outp, repeat(')', p.emitPar)) p.emitPar = 0 - if p.info.line > int16(1): llStreamWrite(p.outp, "\n") + if p.info.line > uint16(1): llStreamWrite(p.outp, "\n") if p.pendingExprLine: llStreamWrite(p.outp, spaces(2)) p.pendingExprLine = false proc scanPar(p: var TTmplParser, d: int) = var i = d - while true: + while i < p.x.len: case p.x[i] - of '\0': break of '(': inc(p.par) of ')': dec(p.par) of '[': inc(p.bracket) @@ -63,16 +62,19 @@ const proc parseLine(p: var TTmplParser) = var j = 0 - while p.x[j] == ' ': inc(j) - if p.x[0] == p.nimDirective and p.x[1] == '?': + let len = p.x.len + + while j < len and p.x[j] == ' ': inc(j) + + if len >= 2 and p.x[0] == p.nimDirective and p.x[1] == '?': newLine(p) - elif p.x[j] == p.nimDirective: + elif j < len and p.x[j] == p.nimDirective: newLine(p) inc(j) - while p.x[j] == ' ': inc(j) + while j < len and p.x[j] == ' ': inc(j) let d = j var keyw = "" - while p.x[j] in PatternChars: + while j < len and p.x[j] in PatternChars: add(keyw, p.x[j]) inc(j) @@ -84,7 +86,7 @@ proc parseLine(p: var TTmplParser) = dec(p.indent, 2) else: p.info.col = int16(j) - localError(p.info, errXNotAllowedHere, "end") + localError(p.config, p.info, "'end' does not close a control flow construct") llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, "#end") of "if", "when", "try", "while", "for", "block", "case", "proc", "iterator", @@ -126,10 +128,8 @@ proc parseLine(p: var TTmplParser) = llStreamWrite(p.outp, "(\"") inc(p.emitPar) p.state = psTempl - while true: + while j < len: case p.x[j] - of '\0': - break of '\x01'..'\x1F', '\x80'..'\xFF': llStreamWrite(p.outp, "\\x") llStreamWrite(p.outp, toHex(ord(p.x[j]), 2)) @@ -156,11 +156,8 @@ proc parseLine(p: var TTmplParser) = llStreamWrite(p.outp, '(') inc(j) var curly = 0 - while true: + while j < len: case p.x[j] - of '\0': - localError(p.info, errXExpected, "}") - break of '{': inc(j) inc(curly) @@ -173,6 +170,9 @@ proc parseLine(p: var TTmplParser) = else: llStreamWrite(p.outp, p.x[j]) inc(j) + if curly > 0: + localError(p.config, p.info, "expected closing '}'") + break llStreamWrite(p.outp, ')') llStreamWrite(p.outp, p.conc) llStreamWrite(p.outp, '\"') @@ -181,7 +181,7 @@ proc parseLine(p: var TTmplParser) = llStreamWrite(p.outp, p.conc) llStreamWrite(p.outp, p.toStr) llStreamWrite(p.outp, '(') - while p.x[j] in PatternChars: + while j < len and p.x[j] in PatternChars: llStreamWrite(p.outp, p.x[j]) inc(j) llStreamWrite(p.outp, ')') @@ -193,28 +193,29 @@ proc parseLine(p: var TTmplParser) = inc(j) else: p.info.col = int16(j) - localError(p.info, errInvalidExpression, "$") + localError(p.config, p.info, "invalid expression") else: llStreamWrite(p.outp, p.x[j]) inc(j) llStreamWrite(p.outp, "\\n\"") -proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream = +proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode; conf: ConfigRef): PLLStream = var p: TTmplParser - p.info = newLineInfo(filename, 0, 0) + p.config = conf + p.info = newLineInfo(conf, filename, 0, 0) p.outp = llStreamOpen("") p.inp = stdin - p.subsChar = charArg(call, "subschar", 1, '$') - p.nimDirective = charArg(call, "metachar", 2, '#') - p.emit = strArg(call, "emit", 3, "result.add") - p.conc = strArg(call, "conc", 4, " & ") - p.toStr = strArg(call, "tostring", 5, "$") + p.subsChar = charArg(conf, call, "subschar", 1, '$') + p.nimDirective = charArg(conf, call, "metachar", 2, '#') + p.emit = strArg(conf, call, "emit", 3, "result.add") + p.conc = strArg(conf, call, "conc", 4, " & ") + p.toStr = strArg(conf, call, "tostring", 5, "$") p.x = newStringOfCap(120) # do not process the first line which contains the directive: if llStreamReadLine(p.inp, p.x): - p.info.line = p.info.line + int16(1) + p.info.line = p.info.line + 1'u16 while llStreamReadLine(p.inp, p.x): - p.info.line = p.info.line + int16(1) + p.info.line = p.info.line + 1'u16 parseLine(p) newLine(p) result = p.outp diff --git a/compiler/filters.nim b/compiler/filters.nim index 37df628ed..3ebbad678 100644 --- a/compiler/filters.nim +++ b/compiler/filters.nim @@ -13,43 +13,44 @@ import llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options, renderer -proc invalidPragma(n: PNode) = - localError(n.info, errXNotAllowedHere, renderTree(n, {renderNoComments})) +proc invalidPragma(conf: ConfigRef; n: PNode) = + localError(conf, n.info, + "'$1' not allowed here" % renderTree(n, {renderNoComments})) -proc getArg(n: PNode, name: string, pos: int): PNode = +proc getArg(conf: ConfigRef; n: PNode, name: string, pos: int): PNode = result = nil if n.kind in {nkEmpty..nkNilLit}: return for i in countup(1, sonsLen(n) - 1): if n.sons[i].kind == nkExprEqExpr: - if n.sons[i].sons[0].kind != nkIdent: invalidPragma(n) + if n.sons[i].sons[0].kind != nkIdent: invalidPragma(conf, n) if cmpIgnoreStyle(n.sons[i].sons[0].ident.s, name) == 0: return n.sons[i].sons[1] elif i == pos: return n.sons[i] -proc charArg*(n: PNode, name: string, pos: int, default: char): char = - var x = getArg(n, name, pos) +proc charArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: char): char = + var x = getArg(conf, n, name, pos) if x == nil: result = default elif x.kind == nkCharLit: result = chr(int(x.intVal)) - else: invalidPragma(n) + else: invalidPragma(conf, n) -proc strArg*(n: PNode, name: string, pos: int, default: string): string = - var x = getArg(n, name, pos) +proc strArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: string): string = + var x = getArg(conf, n, name, pos) if x == nil: result = default elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal - else: invalidPragma(n) + else: invalidPragma(conf, n) -proc boolArg*(n: PNode, name: string, pos: int, default: bool): bool = - var x = getArg(n, name, pos) +proc boolArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: bool): bool = + var x = getArg(conf, n, name, pos) if x == nil: result = default elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "true") == 0: result = true elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "false") == 0: result = false - else: invalidPragma(n) + else: invalidPragma(conf, n) -proc filterStrip*(stdin: PLLStream, filename: string, call: PNode): PLLStream = - var pattern = strArg(call, "startswith", 1, "") - var leading = boolArg(call, "leading", 2, true) - var trailing = boolArg(call, "trailing", 3, true) +proc filterStrip*(conf: ConfigRef; stdin: PLLStream, filename: string, call: PNode): PLLStream = + var pattern = strArg(conf, call, "startswith", 1, "") + var leading = boolArg(conf, call, "leading", 2, true) + var trailing = boolArg(conf, call, "trailing", 3, true) result = llStreamOpen("") var line = newStringOfCap(80) while llStreamReadLine(stdin, line): @@ -60,10 +61,10 @@ proc filterStrip*(stdin: PLLStream, filename: string, call: PNode): PLLStream = llStreamWriteln(result, line) llStreamClose(stdin) -proc filterReplace*(stdin: PLLStream, filename: string, call: PNode): PLLStream = - var sub = strArg(call, "sub", 1, "") - if len(sub) == 0: invalidPragma(call) - var by = strArg(call, "by", 2, "") +proc filterReplace*(conf: ConfigRef; stdin: PLLStream, filename: string, call: PNode): PLLStream = + var sub = strArg(conf, call, "sub", 1, "") + if len(sub) == 0: invalidPragma(conf, call) + var by = strArg(conf, call, "by", 2, "") result = llStreamOpen("") var line = newStringOfCap(80) while llStreamReadLine(stdin, line): diff --git a/compiler/gorgeimpl.nim b/compiler/gorgeimpl.nim index 2c51752cd..44c7651bc 100644 --- a/compiler/gorgeimpl.nim +++ b/compiler/gorgeimpl.nim @@ -9,7 +9,7 @@ ## Module that implements ``gorge`` for the compiler. -import msgs, securehash, os, osproc, streams, strutils, options +import msgs, std / sha1, os, osproc, streams, strutils, options proc readOutput(p: Process): (string, int) = result[0] = "" @@ -21,11 +21,11 @@ proc readOutput(p: Process): (string, int) = result[0].setLen(result[0].len - "\n".len) result[1] = p.waitForExit -proc opGorge*(cmd, input, cache: string, info: TLineInfo): (string, int) = +proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (string, int) = let workingDir = parentDir(info.toFullPath) if cache.len > 0:# and optForceFullMake notin gGlobalOptions: let h = secureHash(cmd & "\t" & input & "\t" & cache) - let filename = options.toGeneratedFile("gorge_" & $h, "txt") + let filename = options.toGeneratedFile(conf, "gorge_" & $h, "txt") var f: File if open(f, filename): result = (f.readAll, 0) diff --git a/compiler/guards.nim b/compiler/guards.nim index a5e6058c9..d39ea799b 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -10,7 +10,7 @@ ## This module implements the 'implies' relation for guards. import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents, - saturate + saturate, modulegraphs, options, configuration const someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, @@ -83,18 +83,25 @@ proc isLetLocation(m: PNode, isApprox: bool): bool = proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true) -let - opLe = createMagic("<=", mLeI) - opLt = createMagic("<", mLtI) - opAnd = createMagic("and", mAnd) - opOr = createMagic("or", mOr) - opIsNil = createMagic("isnil", mIsNil) - opEq = createMagic("==", mEqI) - opAdd = createMagic("+", mAddI) - opSub = createMagic("-", mSubI) - opMul = createMagic("*", mMulI) - opDiv = createMagic("div", mDivI) - opLen = createMagic("len", mLengthSeq) +type + Operators* = object + opNot, opContains, opLe, opLt, opAnd, opOr, opIsNil, opEq: PSym + opAdd, opSub, opMul, opDiv, opLen: PSym + +proc initOperators*(g: ModuleGraph): Operators = + result.opLe = createMagic(g, "<=", mLeI) + result.opLt = createMagic(g, "<", mLtI) + result.opAnd = createMagic(g, "and", mAnd) + result.opOr = createMagic(g, "or", mOr) + result.opIsNil = createMagic(g, "isnil", mIsNil) + result.opEq = createMagic(g, "==", mEqI) + result.opAdd = createMagic(g, "+", mAddI) + result.opSub = createMagic(g, "-", mSubI) + result.opMul = createMagic(g, "*", mMulI) + result.opDiv = createMagic(g, "div", mDivI) + result.opLen = createMagic(g, "len", mLengthSeq) + result.opNot = createMagic(g, "not", mNot) + result.opContains = createMagic(g, "contains", mInSet) proc swapArgs(fact: PNode, newOp: PSym): PNode = result = newNodeI(nkCall, fact.info, 3) @@ -102,16 +109,16 @@ proc swapArgs(fact: PNode, newOp: PSym): PNode = result.sons[1] = fact.sons[2] result.sons[2] = fact.sons[1] -proc neg(n: PNode): PNode = +proc neg(n: PNode; o: Operators): PNode = if n == nil: return nil case n.getMagic of mNot: result = n.sons[1] of someLt: # not (a < b) == a >= b == b <= a - result = swapArgs(n, opLe) + result = swapArgs(n, o.opLe) of someLe: - result = swapArgs(n, opLt) + result = swapArgs(n, o.opLt) of mInSet: if n.sons[1].kind != nkCurly: return nil let t = n.sons[2].typ.skipTypes(abstractInst) @@ -133,11 +140,11 @@ proc neg(n: PNode): PNode = of mOr: # not (a or b) --> not a and not b let - a = n.sons[1].neg - b = n.sons[2].neg + a = n.sons[1].neg(o) + b = n.sons[2].neg(o) if a != nil and b != nil: result = newNodeI(nkCall, n.info, 3) - result.sons[0] = newSymNode(opAnd) + result.sons[0] = newSymNode(o.opAnd) result.sons[1] = a result.sons[2] = b elif a != nil: @@ -147,7 +154,7 @@ proc neg(n: PNode): PNode = else: # leave not (a == 4) as it is result = newNodeI(nkCall, n.info, 2) - result.sons[0] = newSymNode(opNot) + result.sons[0] = newSymNode(o.opNot) result.sons[1] = n proc buildCall(op: PSym; a: PNode): PNode = @@ -181,7 +188,7 @@ proc `|div|`(a, b: PNode): PNode = if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal div b.intVal else: result.floatVal = a.floatVal / b.floatVal -proc negate(a, b, res: PNode): PNode = +proc negate(a, b, res: PNode; o: Operators): PNode = if b.kind in {nkCharLit..nkUInt64Lit} and b.intVal != low(BiggestInt): var b = copyNode(b) b.intVal = -b.intVal @@ -189,11 +196,11 @@ proc negate(a, b, res: PNode): PNode = b.intVal = b.intVal |+| a.intVal result = b else: - result = buildCall(opAdd, a, b) + result = buildCall(o.opAdd, a, b) elif b.kind in {nkFloatLit..nkFloat64Lit}: var b = copyNode(b) b.floatVal = -b.floatVal - result = buildCall(opAdd, a, b) + result = buildCall(o.opAdd, a, b) else: result = res @@ -205,7 +212,7 @@ proc lowBound*(x: PNode): PNode = result = nkIntLit.newIntNode(firstOrd(x.typ)) result.info = x.info -proc highBound*(x: PNode): PNode = +proc highBound*(x: PNode; o: Operators): PNode = let typ = x.typ.skipTypes(abstractInst) result = if typ.kind == tyArray: nkIntLit.newIntNode(lastOrd(typ)) @@ -213,23 +220,23 @@ proc highBound*(x: PNode): PNode = x.sym.kind == skConst: nkIntLit.newIntNode(x.sym.ast.len-1) else: - opAdd.buildCall(opLen.buildCall(x), minusOne()) + o.opAdd.buildCall(o.opLen.buildCall(x), minusOne()) result.info = x.info -proc reassociation(n: PNode): PNode = +proc reassociation(n: PNode; o: Operators): PNode = result = n # (foo+5)+5 --> foo+10; same for '*' case result.getMagic of someAdd: if result[2].isValue and result[1].getMagic in someAdd and result[1][2].isValue: - result = opAdd.buildCall(result[1][1], result[1][2] |+| result[2]) + result = o.opAdd.buildCall(result[1][1], result[1][2] |+| result[2]) if result[2].intVal == 0: result = result[1] of someMul: if result[2].isValue and result[1].getMagic in someMul and result[1][2].isValue: - result = opMul.buildCall(result[1][1], result[1][2] |*| result[2]) + result = o.opMul.buildCall(result[1][1], result[1][2] |*| result[2]) if result[2].intVal == 1: result = result[1] elif result[2].intVal == 0: @@ -243,12 +250,12 @@ proc pred(n: PNode): PNode = else: result = n -proc canon*(n: PNode): PNode = +proc canon*(n: PNode; o: Operators): PNode = # XXX for now only the new code in 'semparallel' uses this if n.safeLen >= 1: result = shallowCopy(n) for i in 0 ..< n.len: - result.sons[i] = canon(n.sons[i]) + result.sons[i] = canon(n.sons[i], o) elif n.kind == nkSym and n.sym.kind == skLet and n.sym.ast.getMagic in (someEq + someAdd + someMul + someMin + someMax + someHigh + {mUnaryLt} + someSub + someLen + someDiv): @@ -263,24 +270,24 @@ proc canon*(n: PNode): PNode = # (4 + foo) + 2 --> (foo + 4) + 2 of someHigh: # high == len+(-1) - result = opAdd.buildCall(opLen.buildCall(result[1]), minusOne()) + result = o.opAdd.buildCall(o.opLen.buildCall(result[1]), minusOne()) of mUnaryLt: - result = buildCall(opAdd, result[1], minusOne()) + result = buildCall(o.opAdd, result[1], minusOne()) of someSub: # x - 4 --> x + (-4) - result = negate(result[1], result[2], result) + result = negate(result[1], result[2], result, o) of someLen: - result.sons[0] = opLen.newSymNode + result.sons[0] = o.opLen.newSymNode of someLt: # x < y same as x <= y-1: - let y = n[2].canon + let y = n[2].canon(o) let p = pred(y) - let minus = if p != y: p else: opAdd.buildCall(y, minusOne()).canon - result = opLe.buildCall(n[1].canon, minus) + let minus = if p != y: p else: o.opAdd.buildCall(y, minusOne()).canon(o) + result = o.opLe.buildCall(n[1].canon(o), minus) else: discard result = skipConv(result) - result = reassociation(result) + result = reassociation(result, o) # most important rule: (x-4) <= a.len --> x <= a.len+4 case result.getMagic of someLe: @@ -291,10 +298,10 @@ proc canon*(n: PNode): PNode = case x.getMagic of someSub: result = buildCall(result[0].sym, x[1], - reassociation(opAdd.buildCall(y, x[2]))) + reassociation(o.opAdd.buildCall(y, x[2]), o)) of someAdd: # Rule A: - let plus = negate(y, x[2], nil).reassociation + let plus = negate(y, x[2], nil, o).reassociation(o) if plus != nil: result = buildCall(result[0].sym, x[1], plus) else: discard elif y.kind in nkCallKinds and y.len == 3 and y[2].isValue and @@ -303,9 +310,9 @@ proc canon*(n: PNode): PNode = case y.getMagic of someSub: result = buildCall(result[0].sym, y[1], - reassociation(opAdd.buildCall(x, y[2]))) + reassociation(o.opAdd.buildCall(x, y[2]), o)) of someAdd: - let plus = negate(x, y[2], nil).reassociation + let plus = negate(x, y[2], nil, o).reassociation(o) # ensure that Rule A will not trigger afterwards with the # additional 'not isLetLocation' constraint: if plus != nil and not isLetLocation(x, true): @@ -323,15 +330,15 @@ proc canon*(n: PNode): PNode = result.sons[2] = y[1] else: discard -proc `+@`*(a: PNode; b: BiggestInt): PNode = - canon(if b != 0: opAdd.buildCall(a, nkIntLit.newIntNode(b)) else: a) +proc buildAdd*(a: PNode; b: BiggestInt; o: Operators): PNode = + canon(if b != 0: o.opAdd.buildCall(a, nkIntLit.newIntNode(b)) else: a, o) -proc usefulFact(n: PNode): PNode = +proc usefulFact(n: PNode; o: Operators): PNode = case n.getMagic of someEq: if skipConv(n.sons[2]).kind == nkNilLit and ( isLetLocation(n.sons[1], false) or isVar(n.sons[1])): - result = opIsNil.buildCall(n.sons[1]) + result = o.opIsNil.buildCall(n.sons[1]) else: if isLetLocation(n.sons[1], true) or isLetLocation(n.sons[2], true): # XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1' @@ -351,11 +358,11 @@ proc usefulFact(n: PNode): PNode = result = n of mAnd: let - a = usefulFact(n.sons[1]) - b = usefulFact(n.sons[2]) + a = usefulFact(n.sons[1], o) + b = usefulFact(n.sons[2], o) if a != nil and b != nil: result = newNodeI(nkCall, n.info, 3) - result.sons[0] = newSymNode(opAnd) + result.sons[0] = newSymNode(o.opAnd) result.sons[1] = a result.sons[2] = b elif a != nil: @@ -363,9 +370,9 @@ proc usefulFact(n: PNode): PNode = elif b != nil: result = b of mNot: - let a = usefulFact(n.sons[1]) + let a = usefulFact(n.sons[1], o) if a != nil: - result = a.neg + result = a.neg(o) of mOr: # 'or' sucks! (p.isNil or q.isNil) --> hard to do anything # with that knowledge... @@ -374,14 +381,14 @@ proc usefulFact(n: PNode): PNode = # (x == 3) or (y == 2) ---> not ( not (x==3) and not (y == 2)) # not (x != 3 and y != 2) let - a = usefulFact(n.sons[1]).neg - b = usefulFact(n.sons[2]).neg + a = usefulFact(n.sons[1], o).neg(o) + b = usefulFact(n.sons[2], o).neg(o) if a != nil and b != nil: result = newNodeI(nkCall, n.info, 3) - result.sons[0] = newSymNode(opAnd) + result.sons[0] = newSymNode(o.opAnd) result.sons[1] = a result.sons[2] = b - result = result.neg + result = result.neg(o) elif n.kind == nkSym and n.sym.kind == skLet: # consider: # let a = 2 < x @@ -389,32 +396,34 @@ proc usefulFact(n: PNode): PNode = # ... # We make can easily replace 'a' by '2 < x' here: if n.sym.ast != nil: - result = usefulFact(n.sym.ast) + result = usefulFact(n.sym.ast, o) elif n.kind == nkStmtListExpr: - result = usefulFact(n.lastSon) + result = usefulFact(n.lastSon, o) type - TModel* = seq[PNode] # the "knowledge base" + TModel* = object + s*: seq[PNode] # the "knowledge base" + o*: Operators proc addFact*(m: var TModel, nn: PNode) = - let n = usefulFact(nn) - if n != nil: m.add n + let n = usefulFact(nn, m.o) + if n != nil: m.s.add n proc addFactNeg*(m: var TModel, n: PNode) = - let n = n.neg + let n = n.neg(m.o) 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 sameOpr(a, b: PSym): bool = + case a.magic + of someEq: result = b.magic in someEq + of someLe: result = b.magic in someLe + of someLt: result = b.magic in someLt + of someLen: result = b.magic in someLen + of someAdd: result = b.magic in someAdd + of someSub: result = b.magic in someSub + of someMul: result = b.magic in someMul + of someDiv: result = b.magic in someDiv + else: result = a == b proc sameTree*(a, b: PNode): bool = result = false @@ -425,7 +434,7 @@ proc sameTree*(a, b: PNode): bool = 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) + result = a.sym.magic == b.sym.magic or sameOpr(a.sym, b.sym) of nkIdent: result = a.ident.id == b.ident.id of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal @@ -462,8 +471,8 @@ proc invalidateFacts*(m: var TModel, n: PNode) = # The same mechanism could be used for more complex data stored on the heap; # procs that 'write: []' cannot invalidate 'n.kind' for instance. In fact, we # could CSE these expressions then and help C's optimizer. - for i in 0..high(m): - if m[i] != nil and m[i].hasSubTree(n): m[i] = nil + for i in 0..high(m.s): + if m.s[i] != nil and m.s[i].hasSubTree(n): m.s[i] = nil proc valuesUnequal(a, b: PNode): bool = if a.isValue and b.isValue: @@ -486,7 +495,7 @@ proc impliesEq(fact, eq: PNode): TImplication = if sameTree(fact.sons[2], eq.sons[loc]) and isValue(eq.sons[val]): if inSet(fact.sons[1], eq.sons[val]): result = impYes else: result = impNo - of mNot, mOr, mAnd: internalError(eq.info, "impliesEq") + of mNot, mOr, mAnd: assert(false, "impliesEq") else: discard proc leImpliesIn(x, c, aSet: PNode): TImplication = @@ -549,7 +558,7 @@ proc impliesIn(fact, loc, aSet: PNode): TImplication = elif sameTree(fact.sons[2], loc): # 4 < x --> 3 <= x result = geImpliesIn(fact.sons[2], fact.sons[1].pred, aSet) - of mNot, mOr, mAnd: internalError(loc.info, "impliesIn") + of mNot, mOr, mAnd: assert(false, "impliesIn") else: discard proc valueIsNil(n: PNode): TImplication = @@ -567,11 +576,11 @@ proc impliesIsNil(fact, eq: PNode): TImplication = result = valueIsNil(fact.sons[2].skipConv) elif sameTree(fact.sons[2], eq.sons[1]): result = valueIsNil(fact.sons[1].skipConv) - of mNot, mOr, mAnd: internalError(eq.info, "impliesIsNil") + of mNot, mOr, mAnd: assert(false, "impliesIsNil") else: discard proc impliesGe(fact, x, c: PNode): TImplication = - internalAssert isLocation(x) + assert isLocation(x) case fact.sons[0].sym.magic of someEq: if sameTree(fact.sons[1], x): @@ -603,7 +612,7 @@ proc impliesGe(fact, x, c: PNode): TImplication = # fact: 3 <= x; question: x >= 2 ? --> true iff 2 <= 3 if isValue(fact.sons[1]) and isValue(c): if leValue(c, fact.sons[1]): result = impYes - of mNot, mOr, mAnd: internalError(x.info, "impliesGe") + of mNot, mOr, mAnd: assert(false, "impliesGe") else: discard proc impliesLe(fact, x, c: PNode): TImplication = @@ -643,7 +652,7 @@ proc impliesLe(fact, x, c: PNode): TImplication = if isValue(fact.sons[1]) and isValue(c): if leValue(c, fact.sons[1].pred): result = impNo - of mNot, mOr, mAnd: internalError(x.info, "impliesLe") + of mNot, mOr, mAnd: assert(false, "impliesLe") else: discard proc impliesLt(fact, x, c: PNode): TImplication = @@ -707,14 +716,14 @@ proc factImplies(fact, prop: PNode): TImplication = proc doesImply*(facts: TModel, prop: PNode): TImplication = assert prop.kind in nkCallKinds - for f in facts: + for f in facts.s: # facts can be invalidated, in which case they are 'nil': if not f.isNil: result = f.factImplies(prop) if result != impUnknown: return -proc impliesNotNil*(facts: TModel, arg: PNode): TImplication = - result = doesImply(facts, opIsNil.buildCall(arg).neg) +proc impliesNotNil*(m: TModel, arg: PNode): TImplication = + result = doesImply(m, m.o.opIsNil.buildCall(arg).neg(m.o)) proc simpleSlice*(a, b: PNode): BiggestInt = # returns 'c' if a..b matches (i+c)..(i+c), -1 otherwise. (i)..(i) is matched @@ -768,8 +777,10 @@ macro `=~`(x: PNode, pat: untyped): bool = var conds = newTree(nnkBracket) m(x, pat, conds) - when declared(macros.toNimIdent): - result = nestList(toNimIdent"and", conds) + when compiles(nestList(ident"and", conds)): + result = nestList(ident"and", conds) + #elif declared(macros.toNimIdent): + # result = nestList(toNimIdent"and", conds) else: result = nestList(!"and", conds) @@ -815,20 +826,20 @@ proc ple(m: TModel; a, b: PNode): TImplication = if a.getMagic in someMul and a[2].isValue and a[1].getMagic in someDiv and a[1][2].isValue: # simplify (x div 4) * 2 <= y to x div (c div d) <= y - if ple(m, buildCall(opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes: + if ple(m, buildCall(m.o.opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes: return impYes # x*3 + x == x*4. It follows that: # x*3 + y <= x*4 if y <= x and 3 <= 4 if a =~ x*dc + y and b =~ x2*ec: if sameTree(x, x2): - let ec1 = opAdd.buildCall(ec, minusOne()) + let ec1 = m.o.opAdd.buildCall(ec, minusOne()) if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? x: return impYes elif a =~ x*dc and b =~ x2*ec + y: #echo "BUG cam ehrer e ", a, " <=? ", b if sameTree(x, x2): - let ec1 = opAdd.buildCall(ec, minusOne()) + let ec1 = m.o.opAdd.buildCall(ec, minusOne()) if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? zero(): return impYes @@ -861,9 +872,9 @@ proc ple(m: TModel; a, b: PNode): TImplication = # use the knowledge base: return pleViaModel(m, a, b) - #return doesImply(m, opLe.buildCall(a, b)) + #return doesImply(m, o.opLe.buildCall(a, b)) -type TReplacements = seq[tuple[a,b: PNode]] +type TReplacements = seq[tuple[a, b: PNode]] proc replaceSubTree(n, x, by: PNode): PNode = if sameTree(n, x): @@ -881,11 +892,11 @@ proc applyReplacements(n: PNode; rep: TReplacements): PNode = proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication = # now check for inferrable facts: a <= b and b <= c implies a <= c - for i in 0..m.high: - let fact = m[i] + for i in 0..m.s.high: + let fact = m.s[i] if fact != nil and fact.getMagic in someLe: # mark as used: - m[i] = nil + m.s[i] = nil # i <= len-100 # i <=? len-1 # --> true if (len-100) <= (len-1) @@ -917,7 +928,7 @@ proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication = proc pleViaModel(model: TModel; aa, bb: PNode): TImplication = # compute replacements: var replacements: TReplacements = @[] - for fact in model: + for fact in model.s: if fact != nil and fact.getMagic in someEq: let a = fact[1] let b = fact[2] @@ -927,12 +938,13 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication = var a = aa var b = bb if replacements.len > 0: - m = @[] + m.s = @[] + m.o = model.o # make the other facts consistent: - for fact in model: + for fact in model.s: if fact != nil and fact.getMagic notin someEq: # XXX 'canon' should not be necessary here, but it is - m.add applyReplacements(fact, replacements).canon + m.s.add applyReplacements(fact, replacements).canon(m.o) a = applyReplacements(aa, replacements) b = applyReplacements(bb, replacements) else: @@ -941,31 +953,31 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication = result = pleViaModelRec(m, a, b) proc proveLe*(m: TModel; a, b: PNode): TImplication = - let x = canon(opLe.buildCall(a, b)) + let x = canon(m.o.opLe.buildCall(a, b), m.o) #echo "ROOT ", renderTree(x[1]), " <=? ", renderTree(x[2]) result = ple(m, x[1], x[2]) if result == impUnknown: # try an alternative: a <= b iff not (b < a) iff not (b+1 <= a): - let y = canon(opLe.buildCall(opAdd.buildCall(b, one()), a)) + let y = canon(m.o.opLe.buildCall(m.o.opAdd.buildCall(b, one()), a), m.o) result = ~ple(m, y[1], y[2]) proc addFactLe*(m: var TModel; a, b: PNode) = - m.add canon(opLe.buildCall(a, b)) + m.s.add canon(m.o.opLe.buildCall(a, b), m.o) proc settype(n: PNode): PType = result = newType(tySet, n.typ.owner) addSonSkipIntLit(result, n.typ) -proc buildOf(it, loc: PNode): PNode = +proc buildOf(it, loc: PNode; o: Operators): PNode = var s = newNodeI(nkCurly, it.info, it.len-1) s.typ = settype(loc) for i in 0..it.len-2: s.sons[i] = it.sons[i] result = newNodeI(nkCall, it.info, 3) - result.sons[0] = newSymNode(opContains) + result.sons[0] = newSymNode(o.opContains) result.sons[1] = s result.sons[2] = loc -proc buildElse(n: PNode): PNode = +proc buildElse(n: PNode; o: Operators): PNode = var s = newNodeIT(nkCurly, n.info, settype(n.sons[0])) for i in 1..n.len-2: let branch = n.sons[i] @@ -973,23 +985,23 @@ proc buildElse(n: PNode): PNode = for j in 0..branch.len-2: s.add(branch.sons[j]) result = newNodeI(nkCall, n.info, 3) - result.sons[0] = newSymNode(opContains) + result.sons[0] = newSymNode(o.opContains) result.sons[1] = s result.sons[2] = n.sons[0] proc addDiscriminantFact*(m: var TModel, n: PNode) = var fact = newNodeI(nkCall, n.info, 3) - fact.sons[0] = newSymNode(opEq) + fact.sons[0] = newSymNode(m.o.opEq) fact.sons[1] = n.sons[0] fact.sons[2] = n.sons[1] - m.add fact + m.s.add fact proc addAsgnFact*(m: var TModel, key, value: PNode) = var fact = newNodeI(nkCall, key.info, 3) - fact.sons[0] = newSymNode(opEq) + fact.sons[0] = newSymNode(m.o.opEq) fact.sons[1] = key fact.sons[2] = value - m.add fact + m.s.add fact proc sameSubexprs*(m: TModel; a, b: PNode): bool = # This should be used to check whether two *path expressions* refer to the @@ -1002,7 +1014,7 @@ proc sameSubexprs*(m: TModel; a, b: PNode): bool = # However, nil checking requires exactly the same mechanism! But for now # we simply use sameTree and live with the unsoundness of the analysis. var check = newNodeI(nkCall, a.info, 3) - check.sons[0] = newSymNode(opEq) + check.sons[0] = newSymNode(m.o.opEq) check.sons[1] = a check.sons[2] = b result = m.doesImply(check) == impYes @@ -1010,11 +1022,11 @@ proc sameSubexprs*(m: TModel; a, b: PNode): bool = proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) = let branch = n.sons[i] if branch.kind == nkOfBranch: - m.add buildOf(branch, n.sons[0]) + m.s.add buildOf(branch, n.sons[0], m.o) else: - m.add n.buildElse.neg + m.s.add n.buildElse(m.o).neg(m.o) -proc buildProperFieldCheck(access, check: PNode): PNode = +proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode = if check.sons[1].kind == nkCurly: result = copyTree(check) if access.kind == nkDotExpr: @@ -1026,10 +1038,10 @@ proc buildProperFieldCheck(access, check: PNode): PNode = else: # it is some 'not' assert check.getMagic == mNot - result = buildProperFieldCheck(access, check.sons[1]).neg + result = buildProperFieldCheck(access, check.sons[1], o).neg(o) -proc checkFieldAccess*(m: TModel, n: PNode) = +proc checkFieldAccess*(m: TModel, n: PNode; conf: ConfigRef) = 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], m.o) if check != nil and m.doesImply(check) != impYes: - message(n.info, warnProveField, renderTree(n.sons[0])); break + message(conf, n.info, warnProveField, renderTree(n.sons[0])); break diff --git a/compiler/hlo.nim b/compiler/hlo.nim index 2bffaa173..8251e3179 100644 --- a/compiler/hlo.nim +++ b/compiler/hlo.nim @@ -12,12 +12,12 @@ proc hlo(c: PContext, n: PNode): PNode proc evalPattern(c: PContext, n, orig: PNode): PNode = - internalAssert n.kind == nkCall and n.sons[0].kind == nkSym + internalAssert c.config, n.kind == nkCall and n.sons[0].kind == nkSym # we need to ensure that the resulting AST is semchecked. However, it's # aweful to semcheck before macro invocation, so we don't and treat # templates and macros as immediate in this context. var rule: string - if optHints in gOptions and hintPattern in gNotes: + if optHints in c.config.options and hintPattern in c.config.notes: rule = renderTree(n, {renderNoComments}) let s = n.sons[0].sym case s.kind @@ -27,8 +27,8 @@ proc evalPattern(c: PContext, n, orig: PNode): PNode = result = semTemplateExpr(c, n, s, {efFromHlo}) else: result = semDirectOp(c, n, {}) - if optHints in gOptions and hintPattern in gNotes: - message(orig.info, hintPattern, rule & " --> '" & + if optHints in c.config.options and hintPattern in c.config.notes: + message(c.config, orig.info, hintPattern, rule & " --> '" & renderTree(result, {renderNoComments}) & "'") proc applyPatterns(c: PContext, n: PNode): PNode = @@ -44,8 +44,8 @@ proc applyPatterns(c: PContext, n: PNode): PNode = assert x.kind in {nkStmtList, nkCall} # better be safe than sorry, so check evalTemplateCounter too: inc(evalTemplateCounter) - if evalTemplateCounter > 100: - globalError(n.info, errTemplateInstantiationTooNested) + if evalTemplateCounter > evalTemplateLimit: + globalError(c.config, n.info, "template instantiation too nested") # deactivate this pattern: c.patterns[i] = nil if x.kind == nkStmtList: @@ -86,18 +86,18 @@ proc hlo(c: PContext, n: PNode): PNode = else: result = fitNode(c, n.typ, result, n.info) # optimization has been applied so check again: - result = commonOptimizations(c.module, result) + result = commonOptimizations(c.graph, c.module, result) result = hlo(c, result) - result = commonOptimizations(c.module, result) + result = commonOptimizations(c.graph, c.module, result) proc hloBody(c: PContext, n: PNode): PNode = # fast exit: - if c.patterns.len == 0 or optPatterns notin gOptions: return n + if c.patterns.len == 0 or optPatterns notin c.config.options: return n c.hloLoopDetector = 0 result = hlo(c, n) proc hloStmt(c: PContext, n: PNode): PNode = # fast exit: - if c.patterns.len == 0 or optPatterns notin gOptions: return n + if c.patterns.len == 0 or optPatterns notin c.config.options: return n c.hloLoopDetector = 0 result = hlo(c, n) diff --git a/compiler/idents.nim b/compiler/idents.nim index 2cce4710e..34cf5bf1e 100644 --- a/compiler/idents.nim +++ b/compiler/idents.nim @@ -37,7 +37,7 @@ proc resetIdentCache*() = for i in low(legacy.buckets)..high(legacy.buckets): legacy.buckets[i] = nil -proc cmpIgnoreStyle(a, b: cstring, blen: int): int = +proc cmpIgnoreStyle*(a, b: cstring, blen: int): int = if a[0] != b[0]: return 1 var i = 0 var j = 0 diff --git a/compiler/idgen.nim b/compiler/idgen.nim index c6b1a4d07..7d103ffd7 100644 --- a/compiler/idgen.nim +++ b/compiler/idgen.nim @@ -36,20 +36,20 @@ proc setId*(id: int) {.inline.} = proc idSynchronizationPoint*(idRange: int) = gFrontEndId = (gFrontEndId div idRange + 1) * idRange + 1 -proc toGid(f: string): string = +proc toGid(conf: ConfigRef; f: string): string = # we used to use ``f.addFileExt("gid")`` (aka ``$project.gid``), but this # will cause strange bugs if multiple projects are in the same folder, so # we simply use a project independent name: - result = options.completeGeneratedFilePath("nim.gid") + result = options.completeGeneratedFilePath(conf, "nim.gid") -proc saveMaxIds*(project: string) = - var f = open(project.toGid, fmWrite) +proc saveMaxIds*(conf: ConfigRef; project: string) = + var f = open(toGid(conf, project), fmWrite) f.writeLine($gFrontEndId) f.close() -proc loadMaxIds*(project: string) = +proc loadMaxIds*(conf: ConfigRef; project: string) = var f: File - if open(f, project.toGid, fmRead): + if open(f, toGid(conf, project), fmRead): var line = newStringOfCap(20) if f.readLine(line): var frontEndId = parseInt(line) diff --git a/compiler/importer.nim b/compiler/importer.nim index 46d675b27..90e774a50 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -11,11 +11,18 @@ import intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups, - semdata, passes, renderer, modulepaths + semdata, passes, renderer, modulepaths, sigmatch, configuration proc evalImport*(c: PContext, n: PNode): PNode proc evalFrom*(c: PContext, n: PNode): PNode +proc readExceptSet*(c: PContext, n: PNode): IntSet = + assert n.kind in {nkImportExceptStmt, nkExportExceptStmt} + result = initIntSet() + for i in 1 ..< n.len: + let ident = lookups.considerQuotedIdent(c.config, n[i]) + result.incl(ident.id) + proc importPureEnumField*(c: PContext; s: PSym) = var check = strTableGet(c.importTable.symbols, s.name) if check == nil: @@ -40,7 +47,7 @@ proc rawImportSymbol(c: PContext, s: PSym) = for j in countup(0, sonsLen(etyp.n) - 1): var e = etyp.n.sons[j].sym if e.kind != skEnumField: - internalError(s.info, "rawImportSymbol") + internalError(c.config, s.info, "rawImportSymbol") # BUGFIX: because of aliases for enums the symbol may already # have been put into the symbol table # BUGFIX: but only iff they are the same symbols! @@ -62,14 +69,14 @@ proc rawImportSymbol(c: PContext, s: PSym) = if hasPattern(s): addPattern(c, s) proc importSymbol(c: PContext, n: PNode, fromMod: PSym) = - let ident = lookups.considerQuotedIdent(n) + let ident = lookups.considerQuotedIdent(c.config, n) let s = strTableGet(fromMod.tab, ident) if s == nil: errorUndeclaredIdentifier(c, n.info, ident.s) else: if s.kind == skStub: loadStub(s) if s.kind notin ExportableSymKinds: - internalError(n.info, "importSymbol: 2") + internalError(c.config, n.info, "importSymbol: 2") # for an enumeration we have to add all identifiers case s.kind of skProcKinds: @@ -77,7 +84,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) = var it: TIdentIter var e = initIdentIter(it, fromMod.tab, s.name) while e != nil: - if e.name.id != s.name.id: internalError(n.info, "importSymbol: 3") + if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3") rawImportSymbol(c, e) e = nextIdentIter(it, fromMod.tab) else: rawImportSymbol(c, s) @@ -89,7 +96,7 @@ proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) = if s.kind != skModule: if s.kind != skEnumField: if s.kind notin ExportableSymKinds: - internalError(s.info, "importAllSymbols: " & $s.kind) + internalError(c.config, s.info, "importAllSymbols: " & $s.kind) if exceptSet.isNil or s.name.id notin exceptSet: rawImportSymbol(c, s) s = nextIter(i, fromMod.tab) @@ -110,22 +117,23 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet) = elif exceptSet.isNil or s.name.id notin exceptSet: rawImportSymbol(c, s) of nkExportExceptStmt: - localError(n.info, errGenerated, "'export except' not implemented") + localError(c.config, n.info, "'export except' not implemented") else: for i in 0..safeLen(n)-1: importForwarded(c, n.sons[i], exceptSet) -proc importModuleAs(n: PNode, realModule: PSym): PSym = +proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym = result = realModule if n.kind != nkImportAs: discard elif n.len != 2 or n.sons[1].kind != nkIdent: - localError(n.info, errGenerated, "module alias must be an identifier") + localError(c.config, n.info, "module alias must be an identifier") elif n.sons[1].ident.id != realModule.name.id: # some misguided guy will write 'import abc.foo as foo' ... - result = createModuleAlias(realModule, n.sons[1].ident, realModule.info) + result = createModuleAlias(realModule, n.sons[1].ident, realModule.info, + c.config.options) proc myImportModule(c: PContext, n: PNode): PSym = - var f = checkModuleName(n) + var f = checkModuleName(c.config, n) if f != InvalidFileIDX: let L = c.graph.importStack.len let recursion = c.graph.importStack.find(f) @@ -138,7 +146,7 @@ proc myImportModule(c: PContext, n: PNode): PSym = err.add toFullPath(c.graph.importStack[i]) & " imports " & toFullPath(c.graph.importStack[i+1]) c.recursiveDep = err - result = importModuleAs(n, gImportModule(c.graph, c.module, f, c.cache)) + result = importModuleAs(c, n, gImportModule(c.graph, c.module, f, c.cache)) #echo "set back to ", L c.graph.importStack.setLen(L) # we cannot perform this check reliably because of @@ -146,10 +154,13 @@ proc myImportModule(c: PContext, n: PNode): PSym = when true: if result.info.fileIndex == c.module.info.fileIndex and result.info.fileIndex == n.info.fileIndex: - localError(n.info, errGenerated, "A module cannot import itself") + localError(c.config, n.info, "A module cannot import itself") if sfDeprecated in result.flags: - message(n.info, warnDeprecated, result.name.s) - #suggestSym(n.info, result, false) + if result.constraint != nil: + message(c.config, n.info, warnDeprecated, result.constraint.strVal & "; " & result.name.s) + else: + message(c.config, n.info, warnDeprecated, result.name.s) + suggestSym(c.config, n.info, result, c.graph.usageSym, false) proc impMod(c: PContext; it: PNode) = let m = myImportModule(c, it) @@ -179,7 +190,7 @@ proc evalImport(c: PContext, n: PNode): PNode = proc evalFrom(c: PContext, n: PNode): PNode = result = n - checkMinSonsLen(n, 2) + checkMinSonsLen(n, 2, c.config) var m = myImportModule(c, n.sons[0]) if m != nil: n.sons[0] = newSymNode(m) @@ -190,14 +201,10 @@ proc evalFrom(c: PContext, n: PNode): PNode = proc evalImportExcept*(c: PContext, n: PNode): PNode = result = n - checkMinSonsLen(n, 2) + checkMinSonsLen(n, 2, c.config) var m = myImportModule(c, n.sons[0]) if m != nil: n.sons[0] = newSymNode(m) addDecl(c, m, n.info) # add symbol to symbol table of module - var exceptSet = initIntSet() - for i in countup(1, sonsLen(n) - 1): - let ident = lookups.considerQuotedIdent(n.sons[i]) - exceptSet.incl(ident.id) - importAllSymbolsExcept(c, m, exceptSet) + importAllSymbolsExcept(c, m, readExceptSet(c, n)) #importForwarded(c, m.ast, exceptSet) diff --git a/compiler/installer.ini b/compiler/installer.ini index 8052121bf..2847e4e62 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -6,7 +6,7 @@ Name: "Nim" Version: "$version" Platforms: """ windows: i386;amd64 - linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64 + linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv64 macosx: i386;amd64;powerpc64 solaris: i386;amd64;sparc;sparc64 freebsd: i386;amd64 diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index dac2de746..25b554f7b 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -8,7 +8,6 @@ # # This is the JavaScript code generator. -# Also a PHP code generator. ;-) discard """ The JS code generator contains only 2 tricks: @@ -31,18 +30,18 @@ implements the required case distinction. import ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options, - nversion, nimsets, msgs, securehash, bitsets, idents, types, os, + nversion, nimsets, msgs, std / sha1, bitsets, idents, types, os, tables, times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils, - intsets, cgmeth, lowerings + intsets, cgmeth, lowerings, sighashes, configuration from modulegraphs import ModuleGraph type - TTarget = enum - targetJS, targetPHP TJSGen = object of TPassContext module: PSym - target: TTarget + graph: ModuleGraph + config: ConfigRef + sigConflicts: CountTable[SigHash] BModule = ref TJSGen TJSTypeKind = enum # necessary JS "types" @@ -91,24 +90,20 @@ type module: BModule g: PGlobals beforeRetNeeded: bool - target: TTarget # duplicated here for faster dispatching unique: int # for temp identifier generation blocks: seq[TBlock] extraIndent: int up: PProc # up the call chain; required for closure support declaredGlobals: IntSet -template `|`(a, b: untyped): untyped {.dirty.} = - (if p.target == targetJS: a else: b) - -var indent = "\t".rope +template config*(p: PProc): ConfigRef = p.module.config proc indentLine(p: PProc, r: Rope): Rope = result = r var p = p while true: for i in countup(0, p.blocks.len - 1 + p.extraIndent): - prepend(result, indent) + prepend(result, "\t".rope) if p.up == nil or p.up.prc != p.prc.owner: break p = p.up @@ -156,11 +151,8 @@ proc newProc(globals: PGlobals, module: BModule, procDef: PNode, module: module, procDef: procDef, g: globals, - target: module.target, extraIndent: int(procDef != nil)) if procDef != nil: result.prc = procDef.sons[namePos].sym - if result.target == targetPHP: - result.declaredGlobals = initIntSet() proc declareGlobal(p: PProc; id: int; r: Rope) = if p.prc != nil and not p.declaredGlobals.containsOrIncl(id): @@ -173,7 +165,7 @@ const proc mapType(typ: PType): TJSTypeKind = let t = skipTypes(typ, abstractInst) case t.kind - of tyVar, tyRef, tyPtr: + of tyVar, tyRef, tyPtr, tyLent: if skipTypes(t.lastSon, abstractInst).kind in MappedToObject: result = etyObject else: @@ -196,20 +188,20 @@ proc mapType(typ: PType): TJSTypeKind = tyExpr, tyStmt, tyTypeDesc, tyBuiltInTypeClass, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything, tyVoid: result = etyNone - of tyGenericInst, tyInferred, tyAlias, tyUserTypeClass, tyUserTypeClassInst: + of tyGenericInst, tyInferred, tyAlias, tyUserTypeClass, tyUserTypeClassInst, + tySink: result = mapType(typ.lastSon) of tyStatic: if t.n != nil: result = mapType(lastSon t) else: result = etyNone of tyProc: result = etyProc of tyCString: result = etyString - of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("mapType") + of tyUnused, tyOptAsRef: doAssert(false, "mapType") proc mapType(p: PProc; typ: PType): TJSTypeKind = - if p.target == targetPHP: result = etyObject - else: result = mapType(typ) + result = mapType(typ) -proc mangleName(s: PSym; target: TTarget): Rope = +proc mangleName(m: BModule, s: PSym): Rope = proc validJsName(name: string): bool = result = true const reservedWords = ["abstract", "await", "boolean", "break", "byte", @@ -234,7 +226,7 @@ proc mangleName(s: PSym; target: TTarget): Rope = if result == nil: if s.kind == skField and s.name.s.validJsName: result = rope(s.name.s) - elif target == targetJS or s.kind == skTemp: + elif s.kind == skTemp: result = rope(mangle(s.name.s)) else: var x = newStringOfCap(s.name.s.len) @@ -253,8 +245,13 @@ proc mangleName(s: PSym; target: TTarget): Rope = inc i result = rope(x) if s.name.s != "this" and s.kind != skField: - add(result, "_") - add(result, rope(s.id)) + if optHotCodeReloading in m.config.options: + # When hot reloading is enabled, we must ensure that the names + # of functions and types will be preserved across rebuilds: + add(result, idOrSig(s, m.module.name.s, m.sigConflicts)) + else: + add(result, "_") + add(result, rope(s.id)) s.loc.r = result proc escapeJSString(s: string): string = @@ -291,23 +288,22 @@ proc genConstant(p: PProc, c: PSym) proc useMagic(p: PProc, name: string) = if name.len == 0: return - var s = magicsys.getCompilerProc(name) + var s = magicsys.getCompilerProc(p.module.graph, name) if s != nil: - internalAssert s.kind in {skProc, skFunc, skMethod, skConverter} + internalAssert p.config, s.kind in {skProc, skFunc, skMethod, skConverter} if not p.g.generatedSyms.containsOrIncl(s.id): let code = genProc(p, s) add(p.g.constants, code) else: - # we used to exclude the system module from this check, but for DLL - # generation support this sloppyness leads to hard to detect bugs, so - # we're picky here for the system module too: - if p.prc != nil: globalError(p.prc.info, errSystemNeeds, name) - else: rawMessage(errSystemNeeds, name) + if p.prc != nil: + globalError(p.config, p.prc.info, "system module needs: " & name) + else: + rawMessage(p.config, errGenerated, "system module needs: " & name) proc isSimpleExpr(p: PProc; n: PNode): bool = # calls all the way down --> can stay expression based - if n.kind in nkCallKinds+{nkBracketExpr, nkDotExpr, nkPar} or - (p.target == targetJS and n.kind in {nkObjConstr, nkBracket, nkCurly}): + if n.kind in nkCallKinds+{nkBracketExpr, nkDotExpr, nkPar, nkTupleConstr} or + (n.kind in {nkObjConstr, nkBracket, nkCurly}): for c in n: if not p.isSimpleExpr(c): return false result = true @@ -316,12 +312,9 @@ proc isSimpleExpr(p: PProc; n: PNode): bool = proc getTemp(p: PProc, defineInLocals: bool = true): Rope = inc(p.unique) - if p.target == targetJS: - result = "Tmp$1" % [rope(p.unique)] - if defineInLocals: - add(p.locals, p.indentLine("var $1;$n" % [result])) - else: - result = "$$Tmp$1" % [rope(p.unique)] + result = "Tmp$1" % [rope(p.unique)] + if defineInLocals: + add(p.locals, p.indentLine("var $1;$n" % [result])) proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) = assert r.kind == resNone @@ -470,15 +463,9 @@ proc unsignedTrimmerJS(size: BiggestInt): Rope = of 4: rope">>> 0" else: rope"" -proc unsignedTrimmerPHP(size: BiggestInt): Rope = - case size - of 1: rope"& 0xff" - of 2: rope"& 0xffff" - of 4: rope"& 0xffffffff" - else: rope"" template unsignedTrimmer(size: BiggestInt): Rope = - size.unsignedTrimmerJS | size.unsignedTrimmerPHP + size.unsignedTrimmerJS proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string, reassign = false) = @@ -526,46 +513,18 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = of mMulU: binaryUintExpr(p, n, r, "*") of mDivU: binaryUintExpr(p, n, r, "/") of mDivI: - if p.target == targetPHP: - var x, y: TCompRes - gen(p, n.sons[1], x) - gen(p, n.sons[2], y) - r.res = "intval($1 / $2)" % [x.rdLoc, y.rdLoc] - else: - arithAux(p, n, r, op) + arithAux(p, n, r, op) of mModI: - if p.target == targetPHP: - var x, y: TCompRes - gen(p, n.sons[1], x) - gen(p, n.sons[2], y) - r.res = "($1 % $2)" % [x.rdLoc, y.rdLoc] - else: - arithAux(p, n, r, op) + arithAux(p, n, r, op) of mShrI: var x, y: TCompRes gen(p, n.sons[1], x) gen(p, n.sons[2], y) let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size) - if p.target == targetPHP: - # XXX prevent multi evaluations - r.res = "(($1 $2) >= 0) ? (($1 $2) >> $3) : ((($1 $2) & 0x7fffffff) >> $3) | (0x40000000 >> ($3 - 1))" % [x.rdLoc, trimmer, y.rdLoc] - else: - r.res = "(($1 $2) >>> $3)" % [x.rdLoc, trimmer, y.rdLoc] + r.res = "(($1 $2) >>> $3)" % [x.rdLoc, trimmer, y.rdLoc] of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr: - if p.target == targetPHP: - if op == mEnumToStr: - var x: TCompRes - gen(p, n.sons[1], x) - r.res = "$#[$#]" % [genEnumInfoPHP(p, n.sons[1].typ), x.rdLoc] - elif op == mCharToStr: - var x: TCompRes - gen(p, n.sons[1], x) - r.res = "chr($#)" % [x.rdLoc] - else: - gen(p, n.sons[1], r) - else: - arithAux(p, n, r, op) + arithAux(p, n, r, op) else: arithAux(p, n, r, op) r.kind = resExpr @@ -584,12 +543,12 @@ proc genLineDir(p: PProc, n: PNode) = useMagic(p, "endb") lineF(p, "endb($1);$n", [rope(line)]) elif hasFrameInfo(p): - lineF(p, "F.line = $1;$n" | "$$F['line'] = $1;$n", [rope(line)]) + lineF(p, "F.line = $1;$n", [rope(line)]) proc genWhileStmt(p: PProc, n: PNode) = var cond: TCompRes - internalAssert isEmptyType(n.typ) + internalAssert p.config, isEmptyType(n.typ) genLineDir(p, n) inc(p.unique) var length = len(p.blocks) @@ -597,12 +556,12 @@ proc genWhileStmt(p: PProc, n: PNode) = p.blocks[length].id = -p.unique p.blocks[length].isLoop = true let labl = p.unique.rope - lineF(p, "L$1: while (true) {$n" | "while (true) {$n", [labl]) + lineF(p, "L$1: while (true) {$n", [labl]) p.nested: gen(p, n.sons[0], cond) - lineF(p, "if (!$1) break L$2;$n" | "if (!$1) goto L$2;$n", + lineF(p, "if (!$1) break L$2;$n", [cond.res, labl]) p.nested: genStmt(p, n.sons[1]) - lineF(p, "}$n" | "}L$#:;$n", [labl]) + lineF(p, "}$n", [labl]) setLen(p.blocks, length) proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) = @@ -646,26 +605,22 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = var i = 1 var length = sonsLen(n) var catchBranchesExist = length > 1 and n.sons[i].kind == nkExceptBranch - if catchBranchesExist and p.target == targetJS: + if catchBranchesExist: add(p.body, "++excHandler;" & tnl) var tmpFramePtr = rope"F" if optStackTrace notin p.options: tmpFramePtr = p.getTemp(true) line(p, tmpFramePtr & " = framePtr;" & tnl) lineF(p, "try {$n", []) - if p.target == targetPHP and p.globals == nil: - p.globals = "global $lastJSError; global $prevJSError;".rope var a: TCompRes gen(p, n.sons[0], a) moveInto(p, a, r) var generalCatchBranchExists = false - let dollar = rope(if p.target == targetJS: "" else: "$") - if p.target == targetJS and catchBranchesExist: + let dollar = rope("") + if catchBranchesExist: addf(p.body, "--excHandler;$n} catch (EXC) {$n var prevJSError = lastJSError;$n" & " lastJSError = EXC;$n --excHandler;$n", []) line(p, "framePtr = $1;$n" % [tmpFramePtr]) - elif p.target == targetPHP: - lineF(p, "} catch (Exception $$EXC) {$n $$prevJSError = $$lastJSError;$n $$lastJSError = $$EXC;$n", []) while i < length and n.sons[i].kind == nkExceptBranch: let blen = sonsLen(n.sons[i]) if blen == 1: @@ -680,7 +635,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = useMagic(p, "isObj") for j in countup(0, blen - 2): if n.sons[i].sons[j].kind != nkType: - internalError(n.info, "genTryStmt") + internalError(p.config, n.info, "genTryStmt") if orExpr != nil: add(orExpr, "||") addf(orExpr, "isObj($2lastJSError.m_type, $1)", [genTypeInfo(p, n.sons[i].sons[j].typ), dollar]) @@ -694,22 +649,14 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = if not generalCatchBranchExists: useMagic(p, "reraiseException") line(p, "else {" & tnl) - line(p, indent & "reraiseException();" & tnl) + line(p, "\treraiseException();" & tnl) line(p, "}" & tnl) addf(p.body, "$1lastJSError = $1prevJSError;$n", [dollar]) - if p.target == targetJS: - line(p, "} finally {" & tnl) - line(p, "framePtr = $1;$n" % [tmpFramePtr]) - if p.target == targetPHP: - # XXX ugly hack for PHP codegen - line(p, "}" & tnl) + line(p, "} finally {" & tnl) + line(p, "framePtr = $1;$n" % [tmpFramePtr]) if i < length and n.sons[i].kind == nkFinally: genStmt(p, n.sons[i].sons[0]) - if p.target == targetPHP: - # XXX ugly hack for PHP codegen - line(p, "if($lastJSError) throw($lastJSError);" & tnl) - if p.target == targetJS: - line(p, "}" & tnl) + line(p, "}" & tnl) proc genRaiseStmt(p: PProc, n: PNode) = genLineDir(p, n) @@ -730,7 +677,7 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = genLineDir(p, n) gen(p, n.sons[0], cond) let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString - if stringSwitch and p.target == targetJS: + if stringSwitch: useMagic(p, "toJSStr") lineF(p, "switch (toJSStr($1)) {$n", [cond.rdLoc]) else: @@ -755,7 +702,7 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = case e.kind of nkStrLit..nkTripleStrLit: lineF(p, "case $1:$n", [makeJSString(e.strVal, false)]) - else: internalError(e.info, "jsgen.genCaseStmt: 2") + else: internalError(p.config, e.info, "jsgen.genCaseStmt: 2") else: gen(p, e, cond) lineF(p, "case $1:$n", [cond.rdLoc]) @@ -769,7 +716,7 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = gen(p, it.sons[0], stmt) moveInto(p, stmt, r) lineF(p, "break;$n", []) - else: internalError(it.info, "jsgen.genCaseStmt") + else: internalError(p.config, it.info, "jsgen.genCaseStmt") lineF(p, "}$n", []) proc genBlock(p: PProc, n: PNode, r: var TCompRes) = @@ -777,17 +724,17 @@ proc genBlock(p: PProc, n: PNode, r: var TCompRes) = let idx = len(p.blocks) if n.sons[0].kind != nkEmpty: # named block? - if (n.sons[0].kind != nkSym): internalError(n.info, "genBlock") + if (n.sons[0].kind != nkSym): internalError(p.config, n.info, "genBlock") var sym = n.sons[0].sym sym.loc.k = locOther sym.position = idx+1 let labl = p.unique - lineF(p, "L$1: do {$n" | "", [labl.rope]) + lineF(p, "L$1: do {$n", [labl.rope]) setLen(p.blocks, idx + 1) p.blocks[idx].id = - p.unique # negative because it isn't used yet gen(p, n.sons[1], r) setLen(p.blocks, idx) - lineF(p, "} while(false);$n" | "$nL$#:;$n", [labl.rope]) + lineF(p, "} while(false);$n", [labl.rope]) proc genBreakStmt(p: PProc, n: PNode) = var idx: int @@ -803,9 +750,9 @@ proc genBreakStmt(p: PProc, n: PNode) = idx = len(p.blocks) - 1 while idx >= 0 and not p.blocks[idx].isLoop: dec idx if idx < 0 or not p.blocks[idx].isLoop: - internalError(n.info, "no loop to break") + internalError(p.config, n.info, "no loop to break") p.blocks[idx].id = abs(p.blocks[idx].id) # label is used - lineF(p, "break L$1;$n" | "goto L$1;$n", [rope(p.blocks[idx].id)]) + lineF(p, "break L$1;$n", [rope(p.blocks[idx].id)]) proc genAsmOrEmitStmt(p: PProc, n: PNode) = genLineDir(p, n) @@ -819,8 +766,7 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode) = let v = it.sym # for backwards compatibility we don't deref syms here :-( if v.kind in {skVar, skLet, skTemp, skConst, skResult, skParam, skForVar}: - if p.target == targetPHP: p.body.add "$" - p.body.add mangleName(v, p.target) + p.body.add mangleName(p.module, v) else: var r: TCompRes gen(p, it, r) @@ -861,25 +807,12 @@ proc generateHeader(p: PProc, typ: PType): Rope = var param = typ.n.sons[i].sym if isCompileTimeOnly(param.typ): continue if result != nil: add(result, ", ") - var name = mangleName(param, p.target) - if p.target == targetJS: - add(result, name) - if mapType(param.typ) == etyBaseIndex: - add(result, ", ") - add(result, name) - add(result, "_Idx") - elif not (i == 1 and param.name.s == "this"): - let k = param.typ.skipTypes({tyGenericInst, tyAlias}).kind - if k in {tyVar, tyRef, tyPtr, tyPointer}: - add(result, "&") - add(result, "$") + var name = mangleName(p.module, param) + add(result, name) + if mapType(param.typ) == etyBaseIndex: + add(result, ", ") add(result, name) - # XXX I think something like this is needed for PHP to really support - # ptr "inside" strings and seq - #if mapType(param.typ) == etyBaseIndex: - # add(result, ", $") - # add(result, name) - # add(result, "_Idx") + add(result, "_Idx") proc countJsParams(typ: PType): int = for i in countup(1, sonsLen(typ.n) - 1): @@ -893,27 +826,16 @@ proc countJsParams(typ: PType): int = const nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, - nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkObjConstr, nkStringToCString, + nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkTupleConstr, nkObjConstr, nkStringToCString, nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix, nkCommand, nkHiddenCallConv, nkCallStrLit} proc needsNoCopy(p: PProc; y: PNode): bool = result = (y.kind in nodeKindsNeedNoCopy) or - (skipTypes(y.typ, abstractInst).kind in {tyRef, tyPtr, tyVar}) or - p.target == targetPHP + (skipTypes(y.typ, abstractInst).kind in {tyRef, tyPtr, tyLent, tyVar}) proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = var a, b: TCompRes - - if p.target == targetPHP and x.kind == nkBracketExpr and - x[0].typ.skipTypes(abstractVar).kind in {tyString, tyCString}: - var c: TCompRes - gen(p, x[0], a) - gen(p, x[1], b) - gen(p, y, c) - lineF(p, "$#[$#] = chr($#);$n", [a.rdLoc, b.rdLoc, c.rdLoc]) - return - var xtyp = mapType(p, x.typ) if x.kind == nkHiddenDeref and x.sons[0].kind == nkCall and xtyp != etyObject: @@ -953,7 +875,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = elif b.typ == etyBaseIndex: lineF(p, "$# = $#;$n", [a.res, b.rdLoc]) else: - internalError(x.info, "genAsgn") + internalError(p.config, x.info, "genAsgn") else: lineF(p, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) else: @@ -961,7 +883,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = proc genAsgn(p: PProc, n: PNode) = genLineDir(p, n) - genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=p.target == targetPHP) + genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=false) proc genFastAsgn(p: PProc, n: PNode) = genLineDir(p, n) @@ -983,20 +905,18 @@ proc genSwap(p: PProc, n: PNode) = if mapType(p, skipTypes(n.sons[1].typ, abstractVar)) == etyBaseIndex: let tmp2 = p.getTemp(false) if a.typ != etyBaseIndex or b.typ != etyBaseIndex: - internalError(n.info, "genSwap") - lineF(p, "var $1 = $2; $2 = $3; $3 = $1;$n" | - "$1 = $2; $2 = $3; $3 = $1;$n", + internalError(p.config, n.info, "genSwap") + lineF(p, "var $1 = $2; $2 = $3; $3 = $1;$n", [tmp, a.address, b.address]) tmp = tmp2 - lineF(p, "var $1 = $2; $2 = $3; $3 = $1;" | - "$1 = $2; $2 = $3; $3 = $1;", + lineF(p, "var $1 = $2; $2 = $3; $3 = $1;", [tmp, a.res, b.res]) -proc getFieldPosition(f: PNode): int = +proc getFieldPosition(p: PProc; f: PNode): int = case f.kind of nkIntLit..nkUInt64Lit: result = int(f.intVal) of nkSym: result = f.sym.position - else: internalError(f.info, "genFieldPosition") + else: internalError(p.config, f.info, "genFieldPosition") proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes @@ -1004,16 +924,13 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = let b = if n.kind == nkHiddenAddr: n.sons[0] else: n gen(p, b.sons[0], a) if skipTypes(b.sons[0].typ, abstractVarRange).kind == tyTuple: - if p.target == targetJS: - r.res = makeJSString( "Field" & $getFieldPosition(b.sons[1]) ) - else: - r.res = getFieldPosition(b.sons[1]).rope + r.res = makeJSString("Field" & $getFieldPosition(p, b.sons[1])) else: - if b.sons[1].kind != nkSym: internalError(b.sons[1].info, "genFieldAddr") + if b.sons[1].kind != nkSym: internalError(p.config, b.sons[1].info, "genFieldAddr") var f = b.sons[1].sym - if f.loc.r == nil: f.loc.r = mangleName(f, p.target) + if f.loc.r == nil: f.loc.r = mangleName(p.module, f) r.res = makeJSString($f.loc.r) - internalAssert a.typ != etyBaseIndex + internalAssert p.config, a.typ != etyBaseIndex r.address = a.res r.kind = resExpr @@ -1022,26 +939,20 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[0], r) let otyp = skipTypes(n.sons[0].typ, abstractVarRange) if otyp.kind == tyTuple: - r.res = ("$1.Field$2" | "$1[$2]") % - [r.res, getFieldPosition(n.sons[1]).rope] + r.res = ("$1.Field$2") % + [r.res, getFieldPosition(p, n.sons[1]).rope] else: - if n.sons[1].kind != nkSym: internalError(n.sons[1].info, "genFieldAccess") + if n.sons[1].kind != nkSym: internalError(p.config, n.sons[1].info, "genFieldAccess") var f = n.sons[1].sym - if f.loc.r == nil: f.loc.r = mangleName(f, p.target) - if p.target == targetJS: - r.res = "$1.$2" % [r.res, f.loc.r] - else: - if {sfImportc, sfExportc} * f.flags != {}: - r.res = "$1->$2" % [r.res, f.loc.r] - else: - r.res = "$1['$2']" % [r.res, f.loc.r] + if f.loc.r == nil: f.loc.r = mangleName(p.module, f) + r.res = "$1.$2" % [r.res, f.loc.r] r.kind = resExpr proc genAddr(p: PProc, n: PNode, r: var TCompRes) proc genCheckedFieldAddr(p: PProc, n: PNode, r: var TCompRes) = let m = if n.kind == nkHiddenAddr: n.sons[0] else: n - internalAssert m.kind == nkCheckedFieldExpr + internalAssert p.config, m.kind == nkCheckedFieldExpr genAddr(p, m, r) # XXX proc genCheckedFieldAccess(p: PProc, n: PNode, r: var TCompRes) = @@ -1055,20 +966,14 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = let m = if n.kind == nkHiddenAddr: n.sons[0] else: n gen(p, m.sons[0], a) gen(p, m.sons[1], b) - internalAssert a.typ != etyBaseIndex and b.typ != etyBaseIndex + internalAssert p.config, a.typ != etyBaseIndex and b.typ != etyBaseIndex r.address = a.res var typ = skipTypes(m.sons[0].typ, abstractPtrs) if typ.kind == tyArray: first = firstOrd(typ.sons[0]) else: first = 0 if optBoundsCheck in p.options: useMagic(p, "chckIndx") - if p.target == targetPHP: - if typ.kind != tyString: - r.res = "chckIndx($1, $2, count($3)-1)-$2" % [b.res, rope(first), a.res] - else: - r.res = "chckIndx($1, $2, strlen($3))-$2" % [b.res, rope(first), a.res] - else: - r.res = "chckIndx($1, $2, $3.length+$2-1)-$2" % [b.res, rope(first), a.res] + r.res = "chckIndx($1, $2, $3.length+$2-1)-$2" % [b.res, rope(first), a.res] elif first != 0: r.res = "($1)-$2" % [b.res, rope(first)] else: @@ -1077,31 +982,17 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = var ty = skipTypes(n.sons[0].typ, abstractVarRange) - if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) + if ty.kind in {tyRef, tyPtr, tyLent}: ty = skipTypes(ty.lastSon, abstractVarRange) case ty.kind of tyArray, tyOpenArray, tySequence, tyString, tyCString, tyVarargs: genArrayAddr(p, n, r) of tyTuple: - if p.target == targetPHP: - genFieldAccess(p, n, r) - return genFieldAddr(p, n, r) - else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')') + else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')') r.typ = etyNone - if r.res == nil: internalError(n.info, "genArrayAccess") - if p.target == targetPHP: - if n.sons[0].kind in nkCallKinds+{nkStrLit..nkTripleStrLit}: - useMagic(p, "nimAt") - if ty.kind in {tyString, tyCString}: - # XXX this needs to be more like substr($1,$2) - r.res = "ord(nimAt($1, $2))" % [r.address, r.res] - else: - r.res = "nimAt($1, $2)" % [r.address, r.res] - elif ty.kind in {tyString, tyCString}: - # XXX this needs to be more like substr($1,$2) - r.res = "ord(@$1[$2])" % [r.address, r.res] - else: - r.res = "$1[$2]" % [r.address, r.res] + if r.res == nil: internalError(p.config, n.info, "genArrayAccess") + if ty.kind == tyCString: + r.res = "$1.charCodeAt($2)" % [r.address, r.res] else: r.res = "$1[$2]" % [r.address, r.res] r.address = nil @@ -1113,13 +1004,13 @@ template isIndirect(x: PSym): bool = #(mapType(v.typ) != etyObject) and {sfImportc, sfVolatile, sfExportc} * v.flags == {} and v.kind notin {skProc, skFunc, skConverter, skMethod, skIterator, - skConst, skTemp, skLet} and p.target == targetJS) + skConst, skTemp, skLet}) proc genAddr(p: PProc, n: PNode, r: var TCompRes) = case n.sons[0].kind of nkSym: let s = n.sons[0].sym - if s.loc.r == nil: internalError(n.info, "genAddr: 3") + if s.loc.r == nil: internalError(p.config, n.info, "genAddr: 3") case s.kind of skVar, skLet, skResult: r.kind = resExpr @@ -1129,8 +1020,6 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) = r.typ = etyNone if isIndirect(s): r.res = s.loc.r & "[0]" - elif p.target == targetPHP: - r.res = "&" & s.loc.r else: r.res = s.loc.r r.address = nil @@ -1143,8 +1032,8 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) = else: # 'var openArray' for instance produces an 'addr' but this is harmless: gen(p, n.sons[0], r) - #internalError(n.info, "genAddr: 4 " & renderTree(n)) - else: internalError(n.info, "genAddr: 2") + #internalError(p.config, n.info, "genAddr: 4 " & renderTree(n)) + else: internalError(p.config, n.info, "genAddr: 2") of nkCheckedFieldExpr: genCheckedFieldAddr(p, n, r) of nkDotExpr: @@ -1163,23 +1052,15 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) = genArrayAddr(p, n.sons[0], r) of tyTuple: genFieldAddr(p, n.sons[0], r) - else: internalError(n.sons[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')') + else: internalError(p.config, n.sons[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')') of nkObjDownConv: gen(p, n.sons[0], r) of nkHiddenDeref: gen(p, n.sons[0].sons[0], r) - else: internalError(n.sons[0].info, "genAddr: " & $n.sons[0].kind) + else: internalError(p.config, n.sons[0].info, "genAddr: " & $n.sons[0].kind) proc thisParam(p: PProc; typ: PType): PType = - if p.target == targetPHP: - # XXX Might be very useful for the JS backend too? - let typ = skipTypes(typ, abstractInst) - assert(typ.kind == tyProc) - if 1 < sonsLen(typ.n): - assert(typ.n.sons[1].kind == nkSym) - let param = typ.n.sons[1].sym - if param.name.s == "this": - result = param.typ.skipTypes(abstractVar) + discard proc attachProc(p: PProc; content: Rope; s: PSym) = let otyp = thisParam(p, s.typ) @@ -1210,40 +1091,28 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = case s.kind of skVar, skLet, skParam, skTemp, skResult, skForVar: if s.loc.r == nil: - internalError(n.info, "symbol has no generated name: " & s.name.s) - if p.target == targetJS: - let k = mapType(p, s.typ) - if k == etyBaseIndex: - r.typ = etyBaseIndex - if {sfAddrTaken, sfGlobal} * s.flags != {}: - r.address = "$1[0]" % [s.loc.r] - r.res = "$1[1]" % [s.loc.r] - else: - r.address = s.loc.r - r.res = s.loc.r & "_Idx" - elif isIndirect(s): - r.res = "$1[0]" % [s.loc.r] + internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) + let k = mapType(p, s.typ) + if k == etyBaseIndex: + r.typ = etyBaseIndex + if {sfAddrTaken, sfGlobal} * s.flags != {}: + r.address = "$1[0]" % [s.loc.r] + r.res = "$1[1]" % [s.loc.r] else: - r.res = s.loc.r + r.address = s.loc.r + r.res = s.loc.r & "_Idx" + elif isIndirect(s): + r.res = "$1[0]" % [s.loc.r] else: - r.res = "$" & s.loc.r - if sfGlobal in s.flags: - p.declareGlobal(s.id, r.res) + r.res = s.loc.r of skConst: genConstant(p, s) if s.loc.r == nil: - internalError(n.info, "symbol has no generated name: " & s.name.s) - if p.target == targetJS: - r.res = s.loc.r - else: - r.res = "$" & s.loc.r - p.declareGlobal(s.id, r.res) + internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) + r.res = s.loc.r of skProc, skFunc, skConverter, skMethod: - discard mangleName(s, p.target) - if p.target == targetPHP and r.kind != resCallee: - r.res = makeJsString($s.loc.r) - else: - r.res = s.loc.r + discard mangleName(p.module, s) + r.res = s.loc.r if lfNoDecl in s.loc.flags or s.magic != mNone or {sfImportc, sfInfixCall} * s.flags != {}: discard @@ -1256,7 +1125,7 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = genProcForSymIfNeeded(p, s) else: if s.loc.r == nil: - internalError(n.info, "symbol has no generated name: " & s.name.s) + internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) r.res = s.loc.r r.kind = resVal @@ -1277,7 +1146,7 @@ proc genDeref(p: PProc, n: PNode, r: var TCompRes) = elif t == etyBaseIndex: r.res = "$1[0]" % [a.res] else: - internalError(n.info, "genDeref") + internalError(p.config, n.info, "genDeref") proc genArgNoParam(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes @@ -1300,7 +1169,7 @@ proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes; emitted: ptr int = add(r.res, ", ") add(r.res, a.res) if emitted != nil: inc emitted[] - elif n.typ.kind == tyVar and n.kind in nkCallKinds and mapType(param.typ) == etyBaseIndex: + elif n.typ.kind in {tyVar, tyLent} and n.kind in nkCallKinds and mapType(param.typ) == etyBaseIndex: # this fixes bug #5608: let tmp = getTemp(p) add(r.res, "($1 = $2, $1[0]), $1[1]" % [tmp, a.rdLoc]) @@ -1337,12 +1206,15 @@ proc genArgs(p: PProc, n: PNode, r: var TCompRes; start=1) = # XXX look into this: let jsp = countJsParams(typ) if emitted != jsp and tfVarargs notin typ.flags: - localError(n.info, "wrong number of parameters emitted; expected: " & $jsp & + localError(p.config, n.info, "wrong number of parameters emitted; expected: " & $jsp & " but got: " & $emitted) r.kind = resExpr proc genOtherArg(p: PProc; n: PNode; i: int; typ: PType; generated: var int; r: var TCompRes) = + if i >= n.len: + globalError(p.config, n.info, "wrong importcpp pattern; expected parameter at position " & $i & + " but got only: " & $(n.len-1)) let it = n[i] var paramType: PNode = nil if i < sonsLen(typ): @@ -1392,10 +1264,10 @@ proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType; proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = # don't call '$' here for efficiency: let f = n[0].sym - if f.loc.r == nil: f.loc.r = mangleName(f, p.target) + if f.loc.r == nil: f.loc.r = mangleName(p.module, f) if sfInfixCall in f.flags: let pat = n.sons[0].sym.loc.r.data - internalAssert pat != nil + internalAssert p.config, pat != nil if pat.contains({'#', '(', '@'}): var typ = skipTypes(n.sons[0].typ, abstractInst) assert(typ.kind == tyProc) @@ -1405,14 +1277,12 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[1], r) if r.typ == etyBaseIndex: if r.address == nil: - globalError(n.info, "cannot invoke with infix syntax") + globalError(p.config, n.info, "cannot invoke with infix syntax") r.res = "$1[$2]" % [r.address, r.res] r.address = nil r.typ = etyNone - add(r.res, "." | "->") + add(r.res, ".") var op: TCompRes - if p.target == targetPHP: - op.kind = resCallee gen(p, n.sons[0], op) add(r.res, op.res) genArgs(p, n, r, 2) @@ -1421,28 +1291,21 @@ proc genCall(p: PProc, n: PNode, r: var TCompRes) = if n.sons[0].kind == nkSym and thisParam(p, n.sons[0].typ) != nil: genInfixCall(p, n, r) return - if p.target == targetPHP: - r.kind = resCallee gen(p, n.sons[0], r) genArgs(p, n, r) proc genEcho(p: PProc, n: PNode, r: var TCompRes) = let n = n[1].skipConv - internalAssert n.kind == nkBracket - if p.target == targetJS: - useMagic(p, "toJSStr") # Used in rawEcho - useMagic(p, "rawEcho") - elif n.len == 0: - r.kind = resExpr - add(r.res, """print("\n")""") - return - add(r.res, "rawEcho(" | "print(") + internalAssert p.config, n.kind == nkBracket + useMagic(p, "toJSStr") # Used in rawEcho + useMagic(p, "rawEcho") + add(r.res, "rawEcho(") for i in countup(0, sonsLen(n) - 1): let it = n.sons[i] if it.typ.isCompileTimeOnly: continue - if i > 0: add(r.res, ", " | ".") + if i > 0: add(r.res, ", ") genArgNoParam(p, it, r) - add(r.res, ")" | """."\n")""") + add(r.res, ")") r.kind = resExpr proc putToSeq(s: string, indirect: bool): Rope = @@ -1462,18 +1325,15 @@ proc createRecordVarAux(p: PProc, rec: PNode, excludedFieldIDs: IntSet, output: of nkSym: if rec.sym.id notin excludedFieldIDs: if output.len > 0: output.add(", ") - if p.target == targetJS: - output.addf("$#: ", [mangleName(rec.sym, p.target)]) - else: - output.addf("'$#' => ", [mangleName(rec.sym, p.target)]) + output.addf("$#: ", [mangleName(p.module, rec.sym)]) output.add(createVar(p, rec.sym.typ, false)) - else: internalError(rec.info, "createRecordVarAux") + else: internalError(p.config, rec.info, "createRecordVarAux") proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: var Rope) = var t = typ if objHasTypeField(t): if output.len > 0: output.add(", ") - addf(output, "m_type: $1" | "'m_type' => $#", [genTypeInfo(p, t)]) + addf(output, "m_type: $1", [genTypeInfo(p, t)]) while t != nil: t = t.skipTypes(skipPtrs) createRecordVarAux(p, t.n, excludedFieldIDs, output) @@ -1499,54 +1359,47 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = result = putToSeq("0", indirect) of tyFloat..tyFloat128: result = putToSeq("0.0", indirect) - of tyRange, tyGenericInst, tyAlias: + of tyRange, tyGenericInst, tyAlias, tySink: result = createVar(p, lastSon(typ), indirect) of tySet: - result = putToSeq("{}" | "array()", indirect) + result = putToSeq("{}", indirect) of tyBool: result = putToSeq("false", indirect) of tyArray: let length = int(lengthOrd(t)) let e = elemType(t) let jsTyp = arrayTypeForElemType(e) - if not jsTyp.isNil and p.target == targetJS: + if not jsTyp.isNil: result = "new $1($2)" % [rope(jsTyp), rope(length)] elif length > 32: useMagic(p, "arrayConstr") # XXX: arrayConstr depends on nimCopy. This line shouldn't be necessary. - if p.target == targetJS: useMagic(p, "nimCopy") + useMagic(p, "nimCopy") result = "arrayConstr($1, $2, $3)" % [rope(length), createVar(p, e, false), genTypeInfo(p, e)] else: - result = rope("[" | "array(") + result = rope("[") var i = 0 while i < length: if i > 0: add(result, ", ") add(result, createVar(p, e, false)) inc(i) - add(result, "]" | ")") + add(result, "]") if indirect: result = "[$1]" % [result] of tyTuple: - if p.target == targetJS: - result = rope("{") - for i in 0..<t.sonsLen: - if i > 0: add(result, ", ") - addf(result, "Field$1: $2", [i.rope, - createVar(p, t.sons[i], false)]) - add(result, "}") - if indirect: result = "[$1]" % [result] - else: - result = rope("array(") - for i in 0..<t.sonsLen: - if i > 0: add(result, ", ") - add(result, createVar(p, t.sons[i], false)) - add(result, ")") + result = rope("{") + for i in 0..<t.sonsLen: + if i > 0: add(result, ", ") + addf(result, "Field$1: $2", [i.rope, + createVar(p, t.sons[i], false)]) + add(result, "}") + if indirect: result = "[$1]" % [result] of tyObject: var initList: Rope createObjInitList(p, t, initIntSet(), initList) - result = ("{$1}" | "array($#)") % [initList] + result = ("{$1}") % [initList] if indirect: result = "[$1]" % [result] - of tyVar, tyPtr, tyRef: + of tyVar, tyPtr, tyLent, tyRef: if mapType(p, t) == etyBaseIndex: result = putToSeq("[null, 0]", indirect) else: @@ -1557,10 +1410,10 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = if t.n != nil: result = createVar(p, lastSon t, indirect) else: - internalError("createVar: " & $t.kind) + internalError(p.config, "createVar: " & $t.kind) result = nil else: - internalError("createVar: " & $t.kind) + internalError(p.config, "createVar: " & $t.kind) result = nil template returnType: untyped = @@ -1571,18 +1424,25 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = a: TCompRes s: Rope varCode: string + varName = mangleName(p.module, v) + useReloadingGuard = sfGlobal in v.flags and optHotCodeReloading in p.config.options + if v.constraint.isNil: - varCode = "var $2" + if useReloadingGuard: + lineF(p, "var $1;$n", varName) + lineF(p, "if ($1 === undefined) {$n", varName) + varCode = $varName + else: + varCode = "var $2" else: varCode = v.constraint.strVal + if n.kind == nkEmpty: - let mname = mangleName(v, p.target) - lineF(p, varCode & " = $3;$n" | "$$$2 = $3;$n", - [returnType, mname, createVar(p, v.typ, isIndirect(v))]) - if v.typ.kind in { tyVar, tyPtr, tyRef } and mapType(p, v.typ) == etyBaseIndex: - lineF(p, "var $1_Idx = 0;$n", [ mname ]) + lineF(p, varCode & " = $3;$n", + [returnType, varName, createVar(p, v.typ, isIndirect(v))]) + if v.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and mapType(p, v.typ) == etyBaseIndex: + lineF(p, "var $1_Idx = 0;$n", [varName]) else: - discard mangleName(v, p.target) gen(p, n, a) case mapType(p, v.typ) of etyObject, etySeq: @@ -1613,14 +1473,17 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = if isIndirect(v): lineF(p, varCode & " = [$3];$n", [returnType, v.loc.r, s]) else: - lineF(p, varCode & " = $3;$n" | "$$$2 = $3;$n", [returnType, v.loc.r, s]) + lineF(p, varCode & " = $3;$n", [returnType, v.loc.r, s]) + + if useReloadingGuard: + lineF(p, "}$n") proc genVarStmt(p: PProc, n: PNode) = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind != nkCommentStmt: if a.kind == nkVarTuple: - let unpacked = lowerTupleUnpacking(a, p.prc) + let unpacked = lowerTupleUnpacking(p.module.graph, a, p.prc) genStmt(p, unpacked) else: assert(a.kind == nkIdentDefs) @@ -1643,25 +1506,21 @@ proc genNew(p: PProc, n: PNode) = var a: TCompRes gen(p, n.sons[1], a) var t = skipTypes(n.sons[1].typ, abstractVar).sons[0] - if p.target == targetJS: - lineF(p, "$1 = $2;$n", [a.res, createVar(p, t, false)]) - else: - lineF(p, "$3 = $2; $1 = &$3;$n", [a.res, createVar(p, t, false), getTemp(p)]) + lineF(p, "$1 = $2;$n", [a.res, createVar(p, t, false)]) proc genNewSeq(p: PProc, n: PNode) = var x, y: TCompRes gen(p, n.sons[1], x) gen(p, n.sons[2], y) let t = skipTypes(n.sons[1].typ, abstractVar).sons[0] - lineF(p, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}" | - "$1 = array(); for ($$i=0;$$i<$2;++$$i) {$1[]=$3;}", [ + lineF(p, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}", [ x.rdLoc, y.rdLoc, createVar(p, t, false)]) proc genOrd(p: PProc, n: PNode, r: var TCompRes) = case skipTypes(n.sons[1].typ, abstractVar).kind of tyEnum, tyInt..tyUInt64, tyChar: gen(p, n.sons[1], r) of tyBool: unaryExpr(p, n, r, "", "($1 ? 1:0)") - else: internalError(n.info, "genOrd") + else: internalError(p.config, n.info, "genOrd") proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes @@ -1686,21 +1545,6 @@ proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) = else: r.res.add("$1)" % [a.res]) -proc genConStrStrPHP(p: PProc, n: PNode, r: var TCompRes) = - var a: TCompRes - gen(p, n.sons[1], a) - r.kind = resExpr - if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyChar: - r.res.add("chr($1)" % [a.res]) - else: - r.res.add(a.res) - for i in countup(2, sonsLen(n) - 1): - gen(p, n.sons[i], a) - if skipTypes(n.sons[i].typ, abstractVarRange).kind == tyChar: - r.res.add(".chr($1)" % [a.res]) - else: - r.res.add(".$1" % [a.res]) - proc genToArray(p: PProc; n: PNode; r: var TCompRes) = # we map mArray to PHP's array constructor, a mild hack: var a, b: TCompRes @@ -1710,15 +1554,15 @@ proc genToArray(p: PProc; n: PNode; r: var TCompRes) = if x.kind == nkBracket: for i in countup(0, x.len - 1): let it = x[i] - if it.kind == nkPar and it.len == 2: + if it.kind in {nkPar, nkTupleConstr} and it.len == 2: if i > 0: r.res.add(", ") gen(p, it[0], a) gen(p, it[1], b) r.res.add("$# => $#" % [a.rdLoc, b.rdLoc]) else: - localError(it.info, "'toArray' needs tuple constructors") + localError(p.config, it.info, "'toArray' needs tuple constructors") else: - localError(x.info, "'toArray' needs an array literal") + localError(p.config, x.info, "'toArray' needs an array literal") r.res.add(")") proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = nil) = @@ -1744,9 +1588,6 @@ proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = add(r.res, ")") proc genRepr(p: PProc, n: PNode, r: var TCompRes) = - if p.target == targetPHP: - localError(n.info, "'repr' not available for PHP backend") - return let t = skipTypes(n.sons[1].typ, abstractVarRange) case t.kind: of tyInt..tyInt64, tyUInt..tyUInt64: @@ -1764,7 +1605,7 @@ proc genRepr(p: PProc, n: PNode, r: var TCompRes) = of tySet: genReprAux(p, n, r, "reprSet", genTypeInfo(p, t)) of tyEmpty, tyVoid: - localError(n.info, "'repr' doesn't support 'void' type") + localError(p.config, n.info, "'repr' doesn't support 'void' type") of tyPointer: genReprAux(p, n, r, "reprPointer") of tyOpenArray, tyVarargs: @@ -1774,7 +1615,7 @@ proc genRepr(p: PProc, n: PNode, r: var TCompRes) = proc genOf(p: PProc, n: PNode, r: var TCompRes) = var x: TCompRes - let t = skipTypes(n.sons[2].typ, abstractVarRange+{tyRef, tyPtr, tyTypeDesc}) + let t = skipTypes(n.sons[2].typ, abstractVarRange+{tyRef, tyPtr, tyLent, tyTypeDesc}) gen(p, n.sons[1], x) if tfFinal in t.flags: r.res = "($1.m_type == $2)" % [x.res, genTypeInfo(p, t)] @@ -1806,57 +1647,36 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = if not (optOverflowCheck in p.options): unaryExpr(p, n, r, "", "$1 - 1") else: unaryExpr(p, n, r, "subInt", "subInt($1, 1)") of mAppendStrCh: - if p.target == targetJS: - binaryExpr(p, n, r, "addChar", - "if ($1 != null) { addChar($1, $2); } else { $1 = [$2, 0]; }") - else: - binaryExpr(p, n, r, "", "$1 .= chr($2)") + binaryExpr(p, n, r, "addChar", + "if ($1 != null) { addChar($1, $2); } else { $1 = [$2, 0]; }") of mAppendStrStr: - if p.target == targetJS: - if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString: - binaryExpr(p, n, r, "", "if ($1 != null) { $1 += $2; } else { $1 = $2; }") - else: - binaryExpr(p, n, r, "", - "if ($1 != null) { $1 = ($1.slice(0, -1)).concat($2); } else { $1 = $2;}") - # XXX: make a copy of $2, because of Javascript's sucking semantics + if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString: + binaryExpr(p, n, r, "", "if ($1 != null) { $1 += $2; } else { $1 = $2; }") else: - binaryExpr(p, n, r, "", "$1 .= $2;") + binaryExpr(p, n, r, "", + "if ($1 != null) { $1 = ($1.slice(0, -1)).concat($2); } else { $1 = $2;}") + # XXX: make a copy of $2, because of Javascript's sucking semantics of mAppendSeqElem: - if p.target == targetJS: - var x, y: TCompRes - gen(p, n.sons[1], x) - gen(p, n.sons[2], y) - if needsNoCopy(p, n[2]): - r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, y.rdLoc] - else: - useMagic(p, "nimCopy") - let c = getTemp(p, defineInLocals=false) - lineF(p, "var $1 = nimCopy(null, $2, $3);$n", - [c, y.rdLoc, genTypeInfo(p, n[2].typ)]) - r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, c] - r.kind = resExpr + var x, y: TCompRes + gen(p, n.sons[1], x) + gen(p, n.sons[2], y) + if needsNoCopy(p, n[2]): + r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, y.rdLoc] else: - binaryExpr(p, n, r, "", "$1[] = $2") + useMagic(p, "nimCopy") + let c = getTemp(p, defineInLocals=false) + lineF(p, "var $1 = nimCopy(null, $2, $3);$n", + [c, y.rdLoc, genTypeInfo(p, n[2].typ)]) + r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, c] + r.kind = resExpr of mConStrStr: - if p.target == targetJS: - genConStrStr(p, n, r) - else: - genConStrStrPHP(p, n, r) + genConStrStr(p, n, r) of mEqStr: - if p.target == targetJS: - binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)") - else: - binaryExpr(p, n, r, "", "($1 == $2)") + binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)") of mLeStr: - if p.target == targetJS: - binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)") - else: - binaryExpr(p, n, r, "", "($1 <= $2)") + binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)") of mLtStr: - if p.target == targetJS: - binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)") - else: - binaryExpr(p, n, r, "", "($1 < $2)") + binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)") of mIsNil: unaryExpr(p, n, r, "", "($1 === null)") of mEnumToStr: genRepr(p, n, r) of mNew, mNewFinalize: genNew(p, n) @@ -1864,24 +1684,20 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mChr, mArrToSeq: gen(p, n.sons[1], r) # nothing to do of mOrd: genOrd(p, n, r) of mLengthStr: - if p.target == targetJS and n.sons[1].typ.skipTypes(abstractInst).kind == tyCString: + if n.sons[1].typ.skipTypes(abstractInst).kind == tyCString: unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)") else: - unaryExpr(p, n, r, "", "($1 != null ? $1.length-1 : 0)" | - "strlen($1)") - of mXLenStr: unaryExpr(p, n, r, "", "$1.length-1" | "strlen($1)") + unaryExpr(p, n, r, "", "($1 != null ? $1.length-1 : 0)") + of mXLenStr: unaryExpr(p, n, r, "", "$1.length-1") of mLengthSeq, mLengthOpenArray, mLengthArray: - unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)" | - "count($1)") + unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)") of mXLenSeq: - unaryExpr(p, n, r, "", "$1.length" | "count($1)") + unaryExpr(p, n, r, "", "$1.length") of mHigh: if skipTypes(n.sons[1].typ, abstractVar).kind == tyString: - unaryExpr(p, n, r, "", "($1 != null ? ($1.length-2) : -1)" | - "(strlen($1)-1)") + unaryExpr(p, n, r, "", "($1 != null ? ($1.length-2) : -1)") else: - unaryExpr(p, n, r, "", "($1 != null ? ($1.length-1) : -1)" | - "(count($1)-1)") + unaryExpr(p, n, r, "", "($1 != null ? ($1.length-1) : -1)") of mInc: if n[1].typ.skipTypes(abstractRange).kind in tyUInt .. tyUInt64: binaryUintExpr(p, n, r, "+", true) @@ -1895,7 +1711,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2") else: binaryExpr(p, n, r, "subInt", "$1 = subInt($1, $2)") of mSetLengthStr: - binaryExpr(p, n, r, "", "$1.length = $2+1; $1[$1.length-1] = 0" | "$1 = substr($1, 0, $2)") + binaryExpr(p, n, r, "", "$1.length = $2+1; $1[$1.length-1] = 0") of mSetLengthSeq: var x, y: TCompRes gen(p, n.sons[1], x) @@ -1912,50 +1728,23 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mPlusSet: binaryExpr(p, n, r, "SetPlus", "SetPlus($1, $2)") of mMinusSet: binaryExpr(p, n, r, "SetMinus", "SetMinus($1, $2)") of mIncl: binaryExpr(p, n, r, "", "$1[$2] = true") - of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]" | "unset $1[$2]") + of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]") of mInSet: - if p.target == targetJS: - binaryExpr(p, n, r, "", "($1[$2] != undefined)") - else: - let s = n.sons[1] - if s.kind == nkCurly: - var a, b, x: TCompRes - gen(p, n.sons[2], x) - r.res = rope("(") - r.kind = resExpr - for i in countup(0, sonsLen(s) - 1): - if i > 0: add(r.res, " || ") - var it = s.sons[i] - if it.kind == nkRange: - gen(p, it.sons[0], a) - gen(p, it.sons[1], b) - addf(r.res, "($1 >= $2 && $1 <= $3)", [x.res, a.res, b.res,]) - else: - gen(p, it, a) - addf(r.res, "($1 == $2)", [x.res, a.res]) - add(r.res, ")") - else: - binaryExpr(p, n, r, "", "isset($1[$2])") + binaryExpr(p, n, r, "", "($1[$2] != undefined)") of mNewSeq: genNewSeq(p, n) - of mNewSeqOfCap: unaryExpr(p, n, r, "", "[]" | "array()") + of mNewSeqOfCap: unaryExpr(p, n, r, "", "[]") of mOf: genOf(p, n, r) of mReset: genReset(p, n) of mEcho: genEcho(p, n, r) of mNLen..mNError, mSlurp, mStaticExec: - localError(n.info, errXMustBeCompileTime, n.sons[0].sym.name.s) + localError(p.config, n.info, errXMustBeCompileTime % n.sons[0].sym.name.s) of mCopyStr: - binaryExpr(p, n, r, "", "($1.slice($2))" | "substr($1, $2)") + binaryExpr(p, n, r, "", "($1.slice($2))") of mCopyStrLast: - if p.target == targetJS: - ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))") - else: - ternaryExpr(p, n, r, "nimSubstr", "nimSubstr($#, $#, $#)") + ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))") of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)") of mNewStringOfCap: - if p.target == targetJS: - unaryExpr(p, n, r, "mnewString", "mnewString(0)") - else: - unaryExpr(p, n, r, "", "''") + unaryExpr(p, n, r, "mnewString", "mnewString(0)") of mDotDot: genProcForSymIfNeeded(p, n.sons[0].sym) genCall(p, n, r) @@ -1963,11 +1752,10 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = useMagic(p, "nimParseBiggestFloat") genCall(p, n, r) of mArray: - if p.target == targetPHP: genToArray(p, n, r) - else: genCall(p, n, r) + genCall(p, n, r) else: genCall(p, n, r) - #else internalError(e.info, 'genMagic: ' + magicToStr[op]); + #else internalError(p.config, e.info, 'genMagic: ' + magicToStr[op]); proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) = var @@ -1981,13 +1769,13 @@ proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) = if it.kind == nkRange: gen(p, it.sons[0], a) gen(p, it.sons[1], b) - addf(r.res, "[$1, $2]" | "array($#,$#)", [a.res, b.res]) + addf(r.res, "[$1, $2]", [a.res, b.res]) else: gen(p, it, a) add(r.res, a.res) add(r.res, ")") # emit better code for constant sets: - if p.target == targetJS and isDeepConstExpr(n): + if isDeepConstExpr(n): inc(p.g.unique) let tmp = rope("ConstSet") & rope(p.g.unique) addf(p.g.constants, "var $1 = $2;$n", [tmp, r.res]) @@ -1995,25 +1783,25 @@ proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) = proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes - r.res = rope("[" | "array(") + r.res = rope("[") r.kind = resExpr for i in countup(0, sonsLen(n) - 1): if i > 0: add(r.res, ", ") gen(p, n.sons[i], a) add(r.res, a.res) - add(r.res, "]" | ")") + add(r.res, "]") proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes - r.res = rope("{" | "array(") + r.res = rope("{") r.kind = resExpr for i in countup(0, sonsLen(n) - 1): if i > 0: add(r.res, ", ") var it = n.sons[i] if it.kind == nkExprColonExpr: it = it.sons[1] gen(p, it, a) - addf(r.res, "Field$#: $#" | "$2", [i.rope, a.res]) - r.res.add("}" | ")") + addf(r.res, "Field$#: $#", [i.rope, a.res]) + r.res.add("}") proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes @@ -2023,11 +1811,11 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = for i in countup(1, sonsLen(n) - 1): if i > 1: add(initList, ", ") var it = n.sons[i] - internalAssert it.kind == nkExprColonExpr + internalAssert p.config, it.kind == nkExprColonExpr let val = it.sons[1] gen(p, val, a) var f = it.sons[0].sym - if f.loc.r == nil: f.loc.r = mangleName(f, p.target) + if f.loc.r == nil: f.loc.r = mangleName(p.module, f) fieldIDs.incl(f.id) let typ = val.typ.skipTypes(abstractInst) @@ -2037,10 +1825,10 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = else: useMagic(p, "nimCopy") a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] - addf(initList, "$#: $#" | "'$#' => $#" , [f.loc.r, a.res]) + addf(initList, "$#: $#", [f.loc.r, a.res]) let t = skipTypes(n.typ, abstractInst + skipPtrs) createObjInitList(p, t, fieldIDs, initList) - r.res = ("{$1}" | "array($#)") % [initList] + r.res = ("{$1}") % [initList] proc genConv(p: PProc, n: PNode, r: var TCompRes) = var dest = skipTypes(n.typ, abstractVarRange) @@ -2079,7 +1867,7 @@ proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[0].sons[0], r) else: gen(p, n.sons[0], r) - if r.res == nil: internalError(n.info, "convStrToCStr") + if r.res == nil: internalError(p.config, n.info, "convStrToCStr") useMagic(p, "toJSStr") r.res = "toJSStr($1)" % [r.res] r.kind = resExpr @@ -2091,30 +1879,29 @@ proc convCStrToStr(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[0].sons[0], r) else: gen(p, n.sons[0], r) - if r.res == nil: internalError(n.info, "convCStrToStr") + if r.res == nil: internalError(p.config, n.info, "convCStrToStr") useMagic(p, "cstrToNimstr") r.res = "cstrToNimstr($1)" % [r.res] r.kind = resExpr proc genReturnStmt(p: PProc, n: PNode) = - if p.procDef == nil: internalError(n.info, "genReturnStmt") + if p.procDef == nil: internalError(p.config, n.info, "genReturnStmt") p.beforeRetNeeded = true if n.sons[0].kind != nkEmpty: genStmt(p, n.sons[0]) else: genLineDir(p, n) - lineF(p, "break BeforeRet;$n" | "goto BeforeRet;$n", []) + lineF(p, "break BeforeRet;$n", []) proc frameCreate(p: PProc; procname, filename: Rope): Rope = let frameFmt = - "var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" | - "global $$framePtr; $$F=array('procname'=>$#,'prev'=>$$framePtr,'filename'=>$#,'line'=>0);$n" + "var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" result = p.indentLine(frameFmt % [procname, filename]) - result.add p.indentLine(ropes.`%`("framePtr = F;$n" | "$$framePtr = &$$F;$n", [])) + result.add p.indentLine(ropes.`%`("framePtr = F;$n", [])) proc frameDestroy(p: PProc): Rope = - result = p.indentLine rope(("framePtr = F.prev;" | "$framePtr = $F['prev'];") & tnl) + result = p.indentLine rope(("framePtr = F.prev;") & tnl) proc genProcBody(p: PProc, prc: PSym): Rope = if hasFrameInfo(p): @@ -2124,15 +1911,12 @@ proc genProcBody(p: PProc, prc: PSym): Rope = else: result = nil if p.beforeRetNeeded: - if p.target == targetJS: - result.add p.indentLine(~"BeforeRet: do {$n") - result.add p.body - result.add p.indentLine(~"} while (false);$n") - else: - addF(result, "$# BeforeRet:;$n", [p.body]) + result.add p.indentLine(~"BeforeRet: do {$n") + result.add p.body + result.add p.indentLine(~"} while (false);$n") else: add(result, p.body) - if prc.typ.callConv == ccSysCall and p.target == targetJS: + if prc.typ.callConv == ccSysCall: result = ("try {$n$1} catch (e) {$n" & " alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}") % [result] if hasFrameInfo(p): @@ -2154,14 +1938,15 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = p.up = oldProc var returnStmt: Rope = nil var resultAsgn: Rope = nil - let name = mangleName(prc, p.target) + var name = mangleName(p.module, prc) let header = generateHeader(p, prc.typ) if prc.typ.sons[0] != nil and sfPure notin prc.flags: resultSym = prc.ast.sons[resultPos].sym - let mname = mangleName(resultSym, p.target) + let mname = mangleName(p.module, resultSym) let resVar = createVar(p, resultSym.typ, isIndirect(resultSym)) - resultAsgn = p.indentLine(("var $# = $#;$n" | "$$$# = $#;$n") % [mname, resVar]) - if resultSym.typ.kind in { tyVar, tyPtr, tyRef } and mapType(p, resultSym.typ) == etyBaseIndex: + resultAsgn = p.indentLine(("var $# = $#;$n") % [mname, resVar]) + if resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and + mapType(p, resultSym.typ) == etyBaseIndex: resultAsgn.add p.indentLine("var $#_Idx = 0;$n" % [mname]) gen(p, prc.ast.sons[resultPos], a) if mapType(p, resultSym.typ) == etyBaseIndex: @@ -2183,6 +1968,18 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = optionaLine(genProcBody(p, prc)), optionaLine(p.indentLine(returnStmt))] else: + result = ~tnl + + if optHotCodeReloading in p.config.options: + # Here, we introduce thunks that create the equivalent of a jump table + # for all global functions, because references to them may be stored + # in JavaScript variables. The added indirection ensures that such + # references will end up calling the reloaded code. + var thunkName = name + name = name & "IMLP" + result.add("function $#() { return $#.apply(this, arguments); }$n" % + [thunkName, name]) + def = "function $#($#) {$n$#$#$#$#$#" % [ name, header, @@ -2193,7 +1990,6 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = optionaLine(p.indentLine(returnStmt))] dec p.extraIndent - result = ~tnl result.add p.indentLine(def) result.add p.indentLine(~"}$n") @@ -2218,10 +2014,10 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) = if dest.kind == src.kind: # no-op conversion return - let toInt = (dest.kind in tyInt .. tyInt32) - let toUint = (dest.kind in tyUInt .. tyUInt32) - let fromInt = (src.kind in tyInt .. tyInt32) - let fromUint = (src.kind in tyUInt .. tyUInt32) + let toInt = (dest.kind in tyInt..tyInt32) + let toUint = (dest.kind in tyUInt..tyUInt32) + let fromInt = (src.kind in tyInt..tyInt32) + let fromUint = (src.kind in tyUInt..tyUInt32) if toUint and (fromInt or fromUint): let trimmer = unsignedTrimmer(dest.size) @@ -2233,8 +2029,7 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) = elif fromUint: if src.size == 4 and dest.size == 4: # XXX prevent multi evaluations - r.res = "($1|0)" % [r.res] | - "($1>(float)2147483647?(int)$1-4294967296:$1)" % [r.res] + r.res = "($1|0)" % [r.res] else: let trimmer = unsignedTrimmer(dest.size) let minuend = case dest.size @@ -2270,8 +2065,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = r.res = rope"null" r.kind = resExpr of nkStrLit..nkTripleStrLit: - if skipTypes(n.typ, abstractVarRange).kind == tyString and - p.target == targetJS: + if skipTypes(n.typ, abstractVarRange).kind == tyString: useMagic(p, "makeNimstrLit") r.res = "makeNimstrLit($1)" % [makeJSString(n.strVal)] else: @@ -2279,11 +2073,17 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = r.kind = resExpr of nkFloatLit..nkFloat64Lit: let f = n.floatVal - if f != f: r.res = rope"NaN" - elif f == 0.0: r.res = rope"0.0" - elif f == 0.5 * f: - if f > 0.0: r.res = rope"Infinity" - else: r.res = rope"-Infinity" + case classify(f) + of fcNaN: + r.res = rope"NaN" + of fcNegZero: + r.res = rope"-0.0" + of fcZero: + r.res = rope"0.0" + of fcInf: + r.res = rope"Infinity" + of fcNegInf: + r.res = rope"-Infinity" else: r.res = rope(f.toStrMaxPrecision) r.kind = resExpr of nkCallKinds: @@ -2298,14 +2098,11 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkClosure: gen(p, n[0], r) of nkCurly: genSetConstr(p, n, r) of nkBracket: genArrayConstr(p, n, r) - of nkPar: genTupleConstr(p, n, r) + of nkPar, nkTupleConstr: genTupleConstr(p, n, r) of nkObjConstr: genObjConstr(p, n, r) of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, r) of nkAddr, nkHiddenAddr: - if p.target == targetJS: - genAddr(p, n, r) - else: - gen(p, n.sons[0], r) + genAddr(p, n, r) of nkDerefExpr, nkHiddenDeref: genDeref(p, n, r) of nkBracketExpr: genArrayAccess(p, n, r) of nkDotExpr: genFieldAccess(p, n, r) @@ -2321,7 +2118,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkEmpty: discard of nkLambdaKinds: let s = n.sons[namePos].sym - discard mangleName(s, p.target) + discard mangleName(p.module, s) r.res = s.loc.r if lfNoDecl in s.loc.flags or s.magic != mNone: discard elif not p.g.generatedSyms.containsOrIncl(s.id): @@ -2344,7 +2141,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkVarSection, nkLetSection: genVarStmt(p, n) of nkConstSection: discard of nkForStmt, nkParForStmt: - internalError(n.info, "for statement not eliminated") + internalError(p.config, n.info, "for statement not eliminated") of nkCaseStmt: genCaseJS(p, n, r) of nkReturnStmt: genReturnStmt(p, n) of nkBreakStmt: genBreakStmt(p, n) @@ -2367,45 +2164,37 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = genSym(p, n.sons[namePos], r) r.res = nil of nkGotoState, nkState: - internalError(n.info, "first class iterators not implemented") + internalError(p.config, n.info, "first class iterators not implemented") of nkPragmaBlock: gen(p, n.lastSon, r) of nkComesFrom: discard "XXX to implement for better stack traces" - else: internalError(n.info, "gen: unknown node type: " & $n.kind) + else: internalError(p.config, n.info, "gen: unknown node type: " & $n.kind) -var globals: PGlobals +var globals: PGlobals # XXX global variable here proc newModule(module: PSym): BModule = new(result) result.module = module + result.sigConflicts = initCountTable[SigHash]() if globals == nil: globals = newGlobals() -proc genHeader(target: TTarget): Rope = - if target == targetJS: - result = ( - "/* Generated by the Nim Compiler v$1 */$n" & - "/* (c) 2017 Andreas Rumpf */$n$n" & - "var framePtr = null;$n" & - "var excHandler = 0;$n" & - "var lastJSError = null;$n" & - "if (typeof Int8Array === 'undefined') Int8Array = Array;$n" & - "if (typeof Int16Array === 'undefined') Int16Array = Array;$n" & - "if (typeof Int32Array === 'undefined') Int32Array = Array;$n" & - "if (typeof Uint8Array === 'undefined') Uint8Array = Array;$n" & - "if (typeof Uint16Array === 'undefined') Uint16Array = Array;$n" & - "if (typeof Uint32Array === 'undefined') Uint32Array = Array;$n" & - "if (typeof Float32Array === 'undefined') Float32Array = Array;$n" & - "if (typeof Float64Array === 'undefined') Float64Array = Array;$n") % - [rope(VersionAsString)] - else: - result = ("<?php$n" & - "/* Generated by the Nim Compiler v$1 */$n" & - "/* (c) 2017 Andreas Rumpf */$n$n" & - "$$framePtr = null;$n" & - "$$excHandler = 0;$n" & - "$$lastJSError = null;$n") % - [rope(VersionAsString)] +proc genHeader(): Rope = + result = ( + "/* Generated by the Nim Compiler v$1 */$n" & + "/* (c) " & copyrightYear & " Andreas Rumpf */$n$n" & + "var framePtr = null;$n" & + "var excHandler = 0;$n" & + "var lastJSError = null;$n" & + "if (typeof Int8Array === 'undefined') Int8Array = Array;$n" & + "if (typeof Int16Array === 'undefined') Int16Array = Array;$n" & + "if (typeof Int32Array === 'undefined') Int32Array = Array;$n" & + "if (typeof Uint8Array === 'undefined') Uint8Array = Array;$n" & + "if (typeof Uint16Array === 'undefined') Uint16Array = Array;$n" & + "if (typeof Uint32Array === 'undefined') Uint32Array = Array;$n" & + "if (typeof Float32Array === 'undefined') Float32Array = Array;$n" & + "if (typeof Float64Array === 'undefined') Float64Array = Array;$n") % + [rope(VersionAsString)] proc genModule(p: PProc, n: PNode) = if optStackTrace in p.options: @@ -2417,10 +2206,10 @@ proc genModule(p: PProc, n: PNode) = add(p.body, frameDestroy(p)) proc myProcess(b: PPassContext, n: PNode): PNode = - if passes.skipCodegen(n): return n result = n - var m = BModule(b) - if m.module == nil: internalError(n.info, "myProcess") + let m = BModule(b) + if passes.skipCodegen(m.config, n): return n + if m.module == nil: internalError(m.config, n.info, "myProcess") var p = newProc(globals, m, nil, m.module.options) p.unique = globals.unique genModule(p, n) @@ -2447,11 +2236,11 @@ proc getClassName(t: PType): Rope = if s.isNil or sfAnon in s.flags: s = skipTypes(t, abstractPtrs).sym if s.isNil or sfAnon in s.flags: - internalError("cannot retrieve class name") + doAssert(false, "cannot retrieve class name") if s.loc.r != nil: result = s.loc.r else: result = rope(s.name.s) -proc genClass(obj: PType; content: Rope; ext: string) = +proc genClass(conf: ConfigRef; obj: PType; content: Rope; ext: string) = let cls = getClassName(obj) let t = skipTypes(obj, abstractPtrs) let extends = if t.kind == tyObject and t.sons[0] != nil: @@ -2459,40 +2248,41 @@ proc genClass(obj: PType; content: Rope; ext: string) = else: nil let result = ("<?php$n" & "/* Generated by the Nim Compiler v$# */$n" & - "/* (c) 2017 Andreas Rumpf */$n$n" & + "/* (c) " & copyrightYear & " Andreas Rumpf */$n$n" & "require_once \"nimsystem.php\";$n" & "class $#$# {$n$#$n}$n") % [rope(VersionAsString), cls, extends, content] - let outfile = changeFileExt(completeCFilePath($cls), ext) + let outfile = changeFileExt(completeCFilePath(conf, $cls), ext) discard writeRopeIfNotEqual(result, outfile) proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = - if passes.skipCodegen(n): return n result = myProcess(b, n) var m = BModule(b) + if passes.skipCodegen(m.config, n): return n if sfMainModule in m.module.flags: - let ext = if m.target == targetJS: "js" else: "php" - let f = if globals.classes.len == 0: m.module.filename + let ext = "js" + let f = if globals.classes.len == 0: toFilename(FileIndex m.module.position) else: "nimsystem" let code = wholeCode(graph, m) let outfile = - if options.outFile.len > 0: - if options.outFile.isAbsolute: options.outFile - else: getCurrentDir() / options.outFile + if m.config.outFile.len > 0: + if m.config.outFile.isAbsolute: m.config.outFile + else: getCurrentDir() / m.config.outFile else: - changeFileExt(completeCFilePath(f), ext) - discard writeRopeIfNotEqual(genHeader(m.target) & code, outfile) + changeFileExt(completeCFilePath(m.config, f), ext) + discard writeRopeIfNotEqual(genHeader() & code, outfile) for obj, content in items(globals.classes): - genClass(obj, content, ext) + genClass(m.config, obj, content, ext) proc myOpenCached(graph: ModuleGraph; s: PSym, rd: PRodReader): PPassContext = - internalError("symbol files are not possible with the JS code generator") + internalError(graph.config, "symbol files are not possible with the JS code generator") result = nil proc myOpen(graph: ModuleGraph; s: PSym; cache: IdentCache): PPassContext = var r = newModule(s) - r.target = if gCmd == cmdCompileToPHP: targetPHP else: targetJS + r.graph = graph + r.config = graph.config result = r const JSgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose) diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim index d9df04e4b..f9e4246eb 100644 --- a/compiler/jstypes.nim +++ b/compiler/jstypes.nim @@ -34,11 +34,11 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope = s = genTypeInfo(p, field.typ) result = ("{kind: 1, offset: \"$1\", len: 0, " & "typ: $2, name: $3, sons: null}") % - [mangleName(field, p.target), s, + [mangleName(p.module, field), s, makeJSString(field.name.s)] of nkRecCase: length = sonsLen(n) - if (n.sons[0].kind != nkSym): internalError(n.info, "genObjectFields") + if (n.sons[0].kind != nkSym): internalError(p.config, n.info, "genObjectFields") field = n.sons[0].sym s = genTypeInfo(p, field.typ) for i in countup(1, length - 1): @@ -47,7 +47,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope = case b.kind of nkOfBranch: if sonsLen(b) < 2: - internalError(b.info, "genObjectFields; nkOfBranch broken") + internalError(p.config, b.info, "genObjectFields; nkOfBranch broken") for j in countup(0, sonsLen(b) - 2): if u != nil: add(u, ", ") if b.sons[j].kind == nkRange: @@ -57,15 +57,15 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope = add(u, rope(getOrdValue(b.sons[j]))) of nkElse: u = rope(lengthOrd(field.typ)) - else: internalError(n.info, "genObjectFields(nkRecCase)") + else: internalError(p.config, n.info, "genObjectFields(nkRecCase)") if result != nil: add(result, ", " & tnl) addf(result, "[setConstr($1), $2]", [u, genObjectFields(p, typ, lastSon(b))]) result = ("{kind: 3, offset: \"$1\", len: $3, " & "typ: $2, name: $4, sons: [$5]}") % [ - mangleName(field, p.target), s, + mangleName(p.module, field), s, rope(lengthOrd(field.typ)), makeJSString(field.name.s), result] - else: internalError(n.info, "genObjectFields") + else: internalError(p.config, n.info, "genObjectFields") proc objHasTypeField(t: PType): bool {.inline.} = tfInheritable in t.flags or t.sons[0] != nil @@ -104,7 +104,7 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) = let length = sonsLen(typ.n) var s: Rope = nil 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(p.config, typ.n.info, "genEnumInfo") let field = typ.n.sons[i].sym if i > 0: add(s, ", " & tnl) let extName = if field.ast == nil: field.name.s else: field.ast.strVal @@ -121,27 +121,8 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) = addf(p.g.typeInfo, "$1.base = $2;$n", [name, genTypeInfo(p, typ.sons[0])]) -proc genEnumInfoPHP(p: PProc; t: PType): Rope = - let t = t.skipTypes({tyGenericInst, tyDistinct, tyAlias}) - result = "$$NTI$1" % [rope(t.id)] - p.declareGlobal(t.id, result) - if containsOrIncl(p.g.typeInfoGenerated, t.id): return - - let length = sonsLen(t.n) - var s: Rope = nil - for i in countup(0, length - 1): - if (t.n.sons[i].kind != nkSym): internalError(t.n.info, "genEnumInfo") - let field = t.n.sons[i].sym - if i > 0: add(s, ", " & tnl) - let extName = if field.ast == nil: field.name.s else: field.ast.strVal - addf(s, "$# => $#$n", - [rope(field.position), makeJSString(extName)]) - prepend(p.g.typeInfo, "$$$# = $#;$n" % [result, s]) - proc genTypeInfo(p: PProc, typ: PType): Rope = - if p.target == targetPHP: - return makeJSString(typeToString(typ, preferModuleInfo)) - let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias}) + let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink}) result = "NTI$1" % [rope(t.id)] if containsOrIncl(p.g.typeInfoGenerated, t.id): return case t.kind @@ -152,7 +133,7 @@ proc genTypeInfo(p: PProc, typ: PType): Rope = "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" % [result, rope(ord(t.kind))] prepend(p.g.typeInfo, s) - of tyVar, tyRef, tyPtr, tySequence, tyRange, tySet: + of tyVar, tyLent, tyRef, tyPtr, tySequence, tyRange, tySet: var s = "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" % [result, rope(ord(t.kind))] @@ -171,5 +152,5 @@ proc genTypeInfo(p: PProc, typ: PType): Rope = of tyTuple: genTupleInfo(p, t, result) of tyStatic: if t.n != nil: result = genTypeInfo(p, lastSon t) - else: internalError("genTypeInfo(" & $t.kind & ')') - else: internalError("genTypeInfo(" & $t.kind & ')') + else: internalError(p.config, "genTypeInfo(" & $t.kind & ')') + else: internalError(p.config, "genTypeInfo(" & $t.kind & ')') diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index cf43ba15d..40e5bb6b0 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -7,11 +7,11 @@ # distribution, for details about the copyright. # -# This include file implements lambda lifting for the transformator. +# This file implements lambda lifting for the transformator. import - intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os, - idents, renderer, types, magicsys, rodread, lowerings, tables + intsets, strutils, options, ast, astalgo, trees, treetab, msgs, + idents, renderer, types, magicsys, rodread, lowerings, tables, modulegraphs discard """ The basic approach is that captured vars need to be put on the heap and @@ -125,32 +125,32 @@ proc newCall(a: PSym, b: PNode): PNode = result.add newSymNode(a) result.add b -proc createStateType(iter: PSym): PType = +proc createClosureIterStateType*(g: ModuleGraph; iter: PSym): PType = var n = newNodeI(nkRange, iter.info) addSon(n, newIntNode(nkIntLit, -1)) addSon(n, newIntNode(nkIntLit, 0)) result = newType(tyRange, iter) result.n = n - var intType = nilOrSysInt() + var intType = nilOrSysInt(g) if intType.isNil: intType = newType(tyInt, iter) rawAddSon(result, intType) -proc createStateField(iter: PSym): PSym = +proc createStateField(g: ModuleGraph; iter: PSym): PSym = result = newSym(skField, getIdent(":state"), iter, iter.info) - result.typ = createStateType(iter) + result.typ = createClosureIterStateType(g, iter) -proc createEnvObj(owner: PSym; info: TLineInfo): PType = +proc createEnvObj(g: ModuleGraph; owner: PSym; info: TLineInfo): PType = # YYY meh, just add the state field for every closure for now, it's too # hard to figure out if it comes from a closure iterator: - result = createObj(owner, info, final=false) - rawAddField(result, createStateField(owner)) + result = createObj(g, owner, info, final=false) + rawAddField(result, createStateField(g, owner)) -proc getIterResult(iter: PSym): PSym = +proc getClosureIterResult*(iter: PSym): PSym = if resultPos < iter.ast.len: result = iter.ast.sons[resultPos].sym else: # XXX a bit hacky: - result = newSym(skResult, getIdent":result", iter, iter.info) + result = newSym(skResult, getIdent":result", iter, iter.info, {}) result.typ = iter.typ.sons[0] incl(result.flags, sfUsed) iter.ast.add newSymNode(result) @@ -166,7 +166,7 @@ proc addHiddenParam(routine: PSym, param: PSym) = assert sfFromGeneric in param.flags #echo "produced environment: ", param.id, " for ", routine.id -proc getHiddenParam(routine: PSym): PSym = +proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym = let params = routine.ast.sons[paramsPos] let hidden = lastSon(params) if hidden.kind == nkSym and hidden.sym.kind == skParam and hidden.sym.name.s == paramName: @@ -174,7 +174,7 @@ proc getHiddenParam(routine: PSym): PSym = assert sfFromGeneric in result.flags else: # writeStackTrace() - localError(routine.info, "internal error: could not find env param for " & routine.name.s) + localError(g.config, routine.info, "internal error: could not find env param for " & routine.name.s) result = routine proc getEnvParam*(routine: PSym): PSym = @@ -186,11 +186,12 @@ proc getEnvParam*(routine: PSym): PSym = proc interestingVar(s: PSym): bool {.inline.} = result = s.kind in {skVar, skLet, skTemp, skForVar, skParam, skResult} and - sfGlobal notin s.flags + sfGlobal notin s.flags and + s.typ.kind notin {tyStatic, tyTypeDesc} proc illegalCapture(s: PSym): bool {.inline.} = result = skipTypes(s.typ, abstractInst).kind in - {tyVar, tyOpenArray, tyVarargs} or + {tyVar, tyOpenArray, tyVarargs, tyLent} or s.kind == skResult proc isInnerProc(s: PSym): bool = @@ -207,14 +208,14 @@ proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode = result.sons[0] = le result.sons[1] = ri -proc makeClosure*(prc: PSym; env: PNode; info: TLineInfo): PNode = +proc makeClosure*(g: ModuleGraph; prc: PSym; env: PNode; info: TLineInfo): PNode = result = newNodeIT(nkClosure, info, prc.typ) result.add(newSymNode(prc)) if env == nil: - result.add(newNodeIT(nkNilLit, info, getSysType(tyNil))) + result.add(newNodeIT(nkNilLit, info, getSysType(g, info, tyNil))) else: if env.skipConv.kind == nkClosure: - localError(info, "internal error: taking closure of closure") + localError(g.config, info, "internal error: taking closure of closure") result.add(env) proc interestingIterVar(s: PSym): bool {.inline.} = @@ -226,23 +227,23 @@ proc interestingIterVar(s: PSym): bool {.inline.} = template isIterator*(owner: PSym): bool = owner.kind == skIterator and owner.typ.callConv == ccClosure -proc liftingHarmful(owner: PSym): bool {.inline.} = +proc liftingHarmful(conf: ConfigRef; owner: PSym): bool {.inline.} = ## lambda lifting can be harmful for JS-like code generators. let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro - result = gCmd in {cmdCompileToPHP, cmdCompileToJS} and not isCompileTime + result = conf.cmd == cmdCompileToJS and not isCompileTime -proc liftIterSym*(n: PNode; owner: PSym): PNode = +proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode = # transforms (iter) to (let env = newClosure[iter](); (iter, env)) - if liftingHarmful(owner): return n + if liftingHarmful(g.config, owner): return n let iter = n.sym assert iter.isIterator result = newNodeIT(nkStmtListExpr, n.info, n.typ) - let hp = getHiddenParam(iter) + let hp = getHiddenParam(g, iter) var env: PNode if owner.isIterator: - let it = getHiddenParam(owner) + let it = getHiddenParam(g, owner) addUniqueField(it.typ.sons[0], hp) env = indirectAccess(newSymNode(it), hp, hp.info) else: @@ -254,11 +255,11 @@ proc liftIterSym*(n: PNode; owner: PSym): PNode = addVar(v, env) result.add(v) # add 'new' statement: - result.add newCall(getSysSym"internalNew", env) - result.add makeClosure(iter, env, n.info) + result.add newCall(getSysSym(g, n.info, "internalNew"), env) + result.add makeClosure(g, iter, env, n.info) -proc freshVarForClosureIter*(s, owner: PSym): PNode = - let envParam = getHiddenParam(owner) +proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode = + let envParam = getHiddenParam(g, owner) let obj = envParam.typ.lastSon addField(obj, s) @@ -268,15 +269,19 @@ proc freshVarForClosureIter*(s, owner: PSym): PNode = if field != nil: result = rawIndirectAccess(access, field, s.info) else: - localError(s.info, "internal error: cannot generate fresh variable") + localError(g.config, s.info, "internal error: cannot generate fresh variable") result = access # ------------------ new stuff ------------------------------------------- -proc markAsClosure(owner: PSym; n: PNode) = +proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) = let s = n.sym - if illegalCapture(s) or owner.typ.callConv notin {ccClosure, ccDefault}: - localError(n.info, errIllegalCaptureX, s.name.s) + if illegalCapture(s): + localError(g.config, n.info, "illegal capture '$1' of type <$2> which is declared here: $3" % + [s.name.s, typeToString(s.typ), $s.info]) + elif owner.typ.callConv notin {ccClosure, ccDefault}: + localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" % + [s.name.s, owner.name.s, CallingConvToStr[owner.typ.callConv]]) incl(owner.typ.flags, tfCapturesEnv) owner.typ.callConv = ccClosure @@ -285,12 +290,14 @@ type processed, capturedVars: IntSet ownerToType: Table[int, PType] somethingToDo: bool + graph: ModuleGraph -proc initDetectionPass(fn: PSym): DetectionPass = +proc initDetectionPass(g: ModuleGraph; fn: PSym): DetectionPass = result.processed = initIntSet() result.capturedVars = initIntSet() result.ownerToType = initTable[int, PType]() result.processed.incl(fn.id) + result.graph = g discard """ proc outer = @@ -307,7 +314,7 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym; result = c.ownerToType.getOrDefault(owner.id) if result.isNil: result = newType(tyRef, owner) - let obj = createEnvObj(owner, info) + let obj = createEnvObj(c.graph, owner, info) rawAddSon(result, obj) c.ownerToType[owner.id] = result @@ -316,13 +323,13 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) = let obj = refObj.lastSon let fieldType = c.getEnvTypeForOwner(dep, info) #getHiddenParam(dep).typ if refObj == fieldType: - localError(dep.info, "internal error: invalid up reference computed") + localError(c.graph.config, dep.info, "internal error: invalid up reference computed") let upIdent = getIdent(upName) let upField = lookupInRecord(obj.n, upIdent) if upField != nil: if upField.typ != fieldType: - localError(dep.info, "internal error: up references do not agree") + localError(c.graph.config, dep.info, "internal error: up references do not agree") else: let result = newSym(skField, upIdent, obj.owner, obj.owner.info) result.typ = fieldType @@ -364,7 +371,7 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) = cp.typ = t addHiddenParam(fn, cp) elif cp.typ != t and fn.kind != skIterator: - localError(fn.info, "internal error: inconsistent environment type") + localError(c.graph.config, fn.info, "internal error: inconsistent environment type") #echo "adding closure to ", fn.name.s proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = @@ -390,9 +397,13 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = addClosureParam(c, owner, n.info) if interestingIterVar(s): if not c.capturedVars.containsOrIncl(s.id): - let obj = getHiddenParam(owner).typ.lastSon + let obj = getHiddenParam(c.graph, owner).typ.lastSon #let obj = c.getEnvTypeForOwner(s.owner).lastSon - addField(obj, s) + + if s.name == getIdent(":state"): + obj.n[0].sym.id = -s.id + else: + addField(obj, s) # but always return because the rest of the proc is only relevant when # ow != owner: return @@ -410,7 +421,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = """ # mark 'owner' as taking a closure: c.somethingToDo = true - markAsClosure(owner, n) + markAsClosure(c.graph, owner, n) addClosureParam(c, owner, n.info) #echo "capturing ", n.info # variable 's' is actually captured: @@ -435,7 +446,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = """ let up = w.skipGenericOwner #echo "up for ", w.name.s, " up ", up.name.s - markAsClosure(w, n) + markAsClosure(c.graph, w, n) addClosureParam(c, w, n.info) # , ow createUpField(c, w, up, n.info) w = up @@ -462,10 +473,10 @@ proc initLiftingPass(fn: PSym): LiftingPass = result.processed.incl(fn.id) result.envVars = initTable[int, PNode]() -proc accessViaEnvParam(n: PNode; owner: PSym): PNode = +proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode = let s = n.sym # Type based expression construction for simplicity: - let envParam = getHiddenParam(owner) + let envParam = getHiddenParam(g, owner) if not envParam.isNil: var access = newSymNode(envParam) while true: @@ -477,7 +488,7 @@ proc accessViaEnvParam(n: PNode; owner: PSym): PNode = let upField = lookupInRecord(obj.n, getIdent(upName)) if upField == nil: break access = rawIndirectAccess(access, upField, n.info) - localError(n.info, "internal error: environment misses: " & s.name.s) + localError(g.config, n.info, "internal error: environment misses: " & s.name.s) result = n proc newEnvVar(owner: PSym; typ: PType): PNode = @@ -496,22 +507,22 @@ proc newEnvVar(owner: PSym; typ: PType): PNode = proc setupEnvVar(owner: PSym; d: DetectionPass; c: var LiftingPass): PNode = if owner.isIterator: - return getHiddenParam(owner).newSymNode + return getHiddenParam(d.graph, owner).newSymNode result = c.envvars.getOrDefault(owner.id) if result.isNil: let envVarType = d.ownerToType.getOrDefault(owner.id) if envVarType.isNil: - localError owner.info, "internal error: could not determine closure type" + localError d.graph.config, owner.info, "internal error: could not determine closure type" result = newEnvVar(owner, envVarType) c.envVars[owner.id] = result -proc getUpViaParam(owner: PSym): PNode = - let p = getHiddenParam(owner) +proc getUpViaParam(g: ModuleGraph; owner: PSym): PNode = + let p = getHiddenParam(g, owner) result = p.newSymNode if owner.isIterator: let upField = lookupInRecord(p.typ.lastSon.n, getIdent(upName)) if upField == nil: - localError(owner.info, "could not find up reference for closure iter") + localError(g.config, owner.info, "could not find up reference for closure iter") else: result = rawIndirectAccess(result, upField, p.info) @@ -521,7 +532,7 @@ proc rawClosureCreation(owner: PSym; var env: PNode if owner.isIterator: - env = getHiddenParam(owner).newSymNode + env = getHiddenParam(d.graph, owner).newSymNode else: env = setupEnvVar(owner, d, c) if env.kind == nkSym: @@ -529,7 +540,7 @@ proc rawClosureCreation(owner: PSym; addVar(v, env) result.add(v) # add 'new' statement: - result.add(newCall(getSysSym"internalNew", env)) + result.add(newCall(getSysSym(d.graph, env.info, "internalNew"), env)) # add assignment statements for captured parameters: for i in 1..<owner.typ.n.len: let local = owner.typ.n[i].sym @@ -540,7 +551,7 @@ proc rawClosureCreation(owner: PSym; let upField = lookupInRecord(env.typ.lastSon.n, getIdent(upName)) if upField != nil: - let up = getUpViaParam(owner) + let up = getUpViaParam(d.graph, owner) if up != nil and upField.typ == up.typ: result.add(newAsgnStmt(rawIndirectAccess(env, upField, env.info), up, env.info)) @@ -548,7 +559,7 @@ proc rawClosureCreation(owner: PSym; # result.add(newAsgnStmt(rawIndirectAccess(env, upField, env.info), # oldenv, env.info)) else: - localError(env.info, "internal error: cannot create up reference") + localError(d.graph.config, env.info, "internal error: cannot create up reference") proc closureCreationForIter(iter: PNode; d: DetectionPass; c: var LiftingPass): PNode = @@ -556,10 +567,10 @@ proc closureCreationForIter(iter: PNode; let owner = iter.sym.skipGenericOwner var v = newSym(skVar, getIdent(envName), owner, iter.info) incl(v.flags, sfShadowed) - v.typ = getHiddenParam(iter.sym).typ + v.typ = getHiddenParam(d.graph, iter.sym).typ var vnode: PNode if owner.isIterator: - let it = getHiddenParam(owner) + let it = getHiddenParam(d.graph, owner) addUniqueField(it.typ.sons[0], v) vnode = indirectAccess(newSymNode(it), v, v.info) else: @@ -567,7 +578,7 @@ proc closureCreationForIter(iter: PNode; var vs = newNodeI(nkVarSection, iter.info) addVar(vs, vnode) result.add(vs) - result.add(newCall(getSysSym"internalNew", vnode)) + result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode)) let upField = lookupInRecord(v.typ.lastSon.n, getIdent(upName)) if upField != nil: @@ -576,8 +587,8 @@ proc closureCreationForIter(iter: PNode; result.add(newAsgnStmt(rawIndirectAccess(vnode, upField, iter.info), u, iter.info)) else: - localError(iter.info, "internal error: cannot create up reference for iter") - result.add makeClosure(iter.sym, vnode, iter.info) + localError(d.graph.config, iter.info, "internal error: cannot create up reference for iter") + result.add makeClosure(d.graph, iter.sym, vnode, iter.info) proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass; c: var LiftingPass): PNode = @@ -587,11 +598,11 @@ proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass; if field != nil: result = rawIndirectAccess(access, field, n.info) else: - localError(n.info, "internal error: not part of closure object type") + localError(d.graph.config, n.info, "internal error: not part of closure object type") result = n -proc getStateField(owner: PSym): PSym = - getHiddenParam(owner).typ.sons[0].n.sons[0].sym +proc getStateField*(g: ModuleGraph; owner: PSym): PSym = + getHiddenParam(g, owner).typ.sons[0].n.sons[0].sym proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; c: var LiftingPass): PNode @@ -599,8 +610,8 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; proc transformYield(n: PNode; owner: PSym; d: DetectionPass; c: var LiftingPass): PNode = if c.inContainer > 0: - localError(n.info, "invalid control flow: 'yield' within a constructor") - let state = getStateField(owner) + localError(d.graph.config, n.info, "invalid control flow: 'yield' within a constructor") + let state = getStateField(d.graph, owner) assert state != nil assert state.typ != nil assert state.typ.n != nil @@ -610,20 +621,22 @@ proc transformYield(n: PNode; owner: PSym; d: DetectionPass; var stateAsgnStmt = newNodeI(nkAsgn, n.info) stateAsgnStmt.add(rawIndirectAccess(newSymNode(getEnvParam(owner)), state, n.info)) - stateAsgnStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt))) + stateAsgnStmt.add(newIntTypeNode(nkIntLit, stateNo, + getSysType(d.graph, n.info, tyInt))) var retStmt = newNodeI(nkReturnStmt, n.info) if n.sons[0].kind != nkEmpty: var a = newNodeI(nkAsgn, n.sons[0].info) var retVal = liftCapturedVars(n.sons[0], owner, d, c) - addSon(a, newSymNode(getIterResult(owner))) + addSon(a, newSymNode(getClosureIterResult(owner))) addSon(a, retVal) retStmt.add(a) else: retStmt.add(emptyNode) var stateLabelStmt = newNodeI(nkState, n.info) - stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt))) + stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo, + getSysType(d.graph, n.info, tyInt))) result = newNodeI(nkStmtList, n.info) result.add(stateAsgnStmt) @@ -632,16 +645,16 @@ proc transformYield(n: PNode; owner: PSym; d: DetectionPass; proc transformReturn(n: PNode; owner: PSym; d: DetectionPass; c: var LiftingPass): PNode = - let state = getStateField(owner) + let state = getStateField(d.graph, owner) result = newNodeI(nkStmtList, n.info) var stateAsgnStmt = newNodeI(nkAsgn, n.info) stateAsgnStmt.add(rawIndirectAccess(newSymNode(getEnvParam(owner)), state, n.info)) - stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt))) + stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(d.graph, n.info, tyInt))) result.add(stateAsgnStmt) result.add(n) -proc wrapIterBody(n: PNode; owner: PSym): PNode = +proc wrapIterBody(g: ModuleGraph; n: PNode; owner: PSym): PNode = if not owner.isIterator: return n when false: # unfortunately control flow is still convoluted and we can end up @@ -655,7 +668,7 @@ proc wrapIterBody(n: PNode; owner: PSym): PNode = let info = n.info result = newNodeI(nkStmtList, info) var gs = newNodeI(nkGotoState, info) - gs.add(rawIndirectAccess(newSymNode(owner.getHiddenParam), getStateField(owner), info)) + gs.add(rawIndirectAccess(newSymNode(getHiddenParam(g, owner)), getStateField(g, owner), info)) result.add(gs) var state0 = newNodeI(nkState, info) state0.add(newIntNode(nkIntLit, 0)) @@ -664,9 +677,9 @@ proc wrapIterBody(n: PNode; owner: PSym): PNode = result.add(n) var stateAsgnStmt = newNodeI(nkAsgn, info) - stateAsgnStmt.add(rawIndirectAccess(newSymNode(owner.getHiddenParam), - getStateField(owner), info)) - stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt))) + stateAsgnStmt.add(rawIndirectAccess(newSymNode(getHiddenParam(g, owner)), + getStateField(g, owner), info)) + stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(g, info, tyInt))) result.add(stateAsgnStmt) proc symToClosure(n: PNode; owner: PSym; d: DetectionPass; @@ -674,25 +687,25 @@ proc symToClosure(n: PNode; owner: PSym; d: DetectionPass; let s = n.sym if s == owner: # recursive calls go through (lambda, hiddenParam): - let available = getHiddenParam(owner) - result = makeClosure(s, available.newSymNode, n.info) + let available = getHiddenParam(d.graph, owner) + result = makeClosure(d.graph, s, available.newSymNode, n.info) elif s.isIterator: result = closureCreationForIter(n, d, c) elif s.skipGenericOwner == owner: # direct dependency, so use the outer's env variable: - result = makeClosure(s, setupEnvVar(owner, d, c), n.info) + result = makeClosure(d.graph, s, setupEnvVar(owner, d, c), n.info) else: - let available = getHiddenParam(owner) - let wanted = getHiddenParam(s).typ + let available = getHiddenParam(d.graph, owner) + let wanted = getHiddenParam(d.graph, s).typ # ugh: call through some other inner proc; var access = newSymNode(available) while true: if access.typ == wanted: - return makeClosure(s, access, n.info) + return makeClosure(d.graph, s, access, n.info) let obj = access.typ.sons[0] let upField = lookupInRecord(obj.n, getIdent(upName)) if upField == nil: - localError(n.info, "internal error: no environment found") + localError(d.graph.config, n.info, "internal error: no environment found") return n access = rawIndirectAccess(access, upField, n.info) @@ -708,7 +721,9 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; # echo renderTree(s.getBody, {renderIds}) let oldInContainer = c.inContainer c.inContainer = 0 - let body = wrapIterBody(liftCapturedVars(s.getBody, s, d, c), s) + var body = liftCapturedVars(s.getBody, s, d, c) + if oldIterTransf in d.graph.config.features: + body = wrapIterBody(d.graph, body, s) if c.envvars.getOrDefault(s.id).isNil: s.ast.sons[bodyPos] = body else: @@ -718,9 +733,9 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; result = symToClosure(n, owner, d, c) elif s.id in d.capturedVars: if s.owner != owner: - result = accessViaEnvParam(n, owner) + result = accessViaEnvParam(d.graph, n, owner) elif owner.isIterator and interestingIterVar(s): - result = accessViaEnvParam(n, owner) + result = accessViaEnvParam(d.graph, n, owner) else: result = accessViaEnvVar(n, owner, d, c) of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom, @@ -751,9 +766,9 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; if n[1].kind == nkClosure: result = n[1] else: if owner.isIterator: - if n.kind == nkYieldStmt: + if oldIterTransf in d.graph.config.features and n.kind == nkYieldStmt: return transformYield(n, owner, d, c) - elif n.kind == nkReturnStmt: + elif oldIterTransf in d.graph.config.features and n.kind == nkReturnStmt: return transformReturn(n, owner, d, c) elif nfLL in n.flags: # special case 'when nimVm' due to bug #3636: @@ -786,8 +801,8 @@ proc semCaptureSym*(s, owner: PSym) = # since the analysis is not entirely correct, we don't set 'tfCapturesEnv' # here -proc liftIterToProc*(fn: PSym; body: PNode; ptrType: PType): PNode = - var d = initDetectionPass(fn) +proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType): PNode = + var d = initDetectionPass(g, fn) var c = initLiftingPass(fn) # pretend 'fn' is a closure iterator for the analysis: let oldKind = fn.kind @@ -796,11 +811,11 @@ proc liftIterToProc*(fn: PSym; body: PNode; ptrType: PType): PNode = fn.typ.callConv = ccClosure d.ownerToType[fn.id] = ptrType detectCapturedVars(body, fn, d) - result = wrapIterBody(liftCapturedVars(body, fn, d, c), fn) + result = wrapIterBody(g, liftCapturedVars(body, fn, d, c), fn) fn.kind = oldKind fn.typ.callConv = oldCC -proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode = +proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PNode = # XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs # the transformation even when compiling to JS ... @@ -808,23 +823,26 @@ proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode = let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro if body.kind == nkEmpty or ( - gCmd in {cmdCompileToPHP, cmdCompileToJS} and not isCompileTime) or + g.config.cmd == cmdCompileToJS and not isCompileTime) or fn.skipGenericOwner.kind != skModule: + # ignore forward declaration: result = body tooEarly = true else: - var d = initDetectionPass(fn) + var d = initDetectionPass(g, fn) detectCapturedVars(body, fn, d) if not d.somethingToDo and fn.isIterator: addClosureParam(d, fn, body.info) d.somethingToDo = true if d.somethingToDo: var c = initLiftingPass(fn) - var newBody = liftCapturedVars(body, fn, d, c) + result = liftCapturedVars(body, fn, d, c) if c.envvars.getOrDefault(fn.id) != nil: - newBody = newTree(nkStmtList, rawClosureCreation(fn, d, c), newBody) - result = wrapIterBody(newBody, fn) + result = newTree(nkStmtList, rawClosureCreation(fn, d, c), result) + + if oldIterTransf in g.config.features: + result = wrapIterBody(g, result, fn) else: result = body #if fn.name.s == "get2": @@ -832,15 +850,12 @@ proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode = # echo renderTree(result, {renderIds}) proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode = - if body.kind == nkEmpty or gCmd == cmdCompileToJS: - result = body - else: - # XXX implement it properly - result = body + # XXX implement it properly + result = body # ------------------- iterator transformation -------------------------------- -proc liftForLoop*(body: PNode; owner: PSym): PNode = +proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode = # problem ahead: the iterator could be invoked indirectly, but then # we don't know what environment to create here: # @@ -865,13 +880,14 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode = cl = createClosure() while true: let i = foo(cl) - nkBreakState(cl.state) + if (nkBreakState(cl.state)): + break ... """ - if liftingHarmful(owner): return body + if liftingHarmful(g.config, owner): return body var L = body.len if not (body.kind == nkForStmt and body[L-2].kind in nkCallKinds): - localError(body.info, "ignored invalid for loop") + localError(g.config, body.info, "ignored invalid for loop") return body var call = body[L-2] @@ -884,7 +900,7 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode = # createClosure() let iter = op.sym - let hp = getHiddenParam(iter) + let hp = getHiddenParam(g, iter) env = newSym(skLet, iter.name, owner, body.info) env.typ = hp.typ env.flags = hp.flags @@ -893,7 +909,7 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode = addVar(v, newSymNode(env)) result.add(v) # add 'new' statement: - result.add(newCall(getSysSym"internalNew", env.newSymNode)) + result.add(newCall(getSysSym(g, env.info, "internalNew"), env.newSymNode)) elif op.kind == nkStmtListExpr: let closure = op.lastSon if closure.kind == nkClosure: @@ -903,7 +919,7 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode = var loopBody = newNodeI(nkStmtList, body.info, 3) var whileLoop = newNodeI(nkWhileStmt, body.info, 2) - whileLoop.sons[0] = newIntTypeNode(nkIntLit, 1, getSysType(tyBool)) + whileLoop.sons[0] = newIntTypeNode(nkIntLit, 1, getSysType(g, body.info, tyBool)) whileLoop.sons[1] = loopBody result.add whileLoop @@ -918,12 +934,23 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode = addSon(vpart, ast.emptyNode) # no explicit type if not env.isNil: - call.sons[0] = makeClosure(call.sons[0].sym, env.newSymNode, body.info) + call.sons[0] = makeClosure(g, call.sons[0].sym, env.newSymNode, body.info) addSon(vpart, call) addSon(v2, vpart) loopBody.sons[0] = v2 var bs = newNodeI(nkBreakState, body.info) bs.addSon(call.sons[0]) - loopBody.sons[1] = bs + + let ibs = newNode(nkIfStmt) + let elifBranch = newNode(nkElifBranch) + elifBranch.add(bs) + + let br = newNode(nkBreakStmt) + br.add(emptyNode) + + elifBranch.add(br) + ibs.add(elifBranch) + + loopBody.sons[1] = ibs loopBody.sons[2] = body[L-1] diff --git a/compiler/lexer.nim b/compiler/lexer.nim index bca07e500..591561987 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -17,7 +17,7 @@ import hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream, - wordrecg + wordrecg, configuration const MaxLineLength* = 80 # lines longer than this lead to a warning @@ -60,7 +60,7 @@ type tkCurlyDotLe, tkCurlyDotRi, # {. and .} tkParDotLe, tkParDotRi, # (. and .) tkComma, tkSemiColon, - tkColon, tkColonColon, tkEquals, tkDot, tkDotDot, + tkColon, tkColonColon, tkEquals, tkDot, tkDotDot, tkBracketLeColon, tkOpr, tkComment, tkAccent, tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr @@ -98,7 +98,7 @@ const "tkTripleStrLit", "tkGStrLit", "tkGTripleStrLit", "tkCharLit", "(", ")", "[", "]", "{", "}", "[.", ".]", "{.", ".}", "(.", ".)", ",", ";", - ":", "::", "=", ".", "..", + ":", "::", "=", ".", "..", "[:", "tkOpr", "tkComment", "`", "tkSpaces", "tkInfixOpr", "tkPrefixOpr", "tkPostfixOpr"] @@ -131,9 +131,9 @@ type # like 0b01 or r"\L" are unaffected commentOffsetA*, commentOffsetB*: int - TErrorHandler* = proc (info: TLineInfo; msg: TMsgKind; arg: string) + TErrorHandler* = proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) TLexer* = object of TBaseLexer - fileIdx*: int32 + fileIdx*: FileIndex indentAhead*: int # if > 0 an indendation has already been read # this is needed because scanning comments # needs so much look-ahead @@ -144,13 +144,12 @@ type cache*: IdentCache when defined(nimsuggest): previousToken: TLineInfo + config*: ConfigRef when defined(nimpretty): var gIndentationWidth*: int -var gLinesCompiled*: int # all lines that have been compiled - proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} = result = newLineInfo(L.fileIdx, tok.line, tok.col) when defined(nimpretty): @@ -165,13 +164,12 @@ proc isKeyword*(kind: TTokType): bool = template ones(n): untyped = ((1 shl n)-1) # for utf-8 conversion proc isNimIdentifier*(s: string): bool = - if s[0] in SymStartChars: + let sLen = s.len + if sLen > 0 and s[0] in SymStartChars: var i = 1 - var sLen = s.len while i < sLen: - if s[i] == '_': - inc(i) - if s[i] notin SymChars: return + if s[i] == '_': inc(i) + if i < sLen and s[i] notin SymChars: return inc(i) result = true @@ -192,8 +190,8 @@ proc prettyTok*(tok: TToken): string = if isKeyword(tok.tokType): result = "keyword " & tok.ident.s else: result = tokToStr(tok) -proc printTok*(tok: TToken) = - msgWriteln($tok.line & ":" & $tok.col & "\t" & +proc printTok*(conf: ConfigRef; tok: TToken) = + msgWriteln(conf, $tok.line & ":" & $tok.col & "\t" & TokTypeToStr[tok.tokType] & " " & tokToStr(tok)) proc initToken*(L: var TToken) = @@ -222,8 +220,8 @@ proc fillToken(L: var TToken) = L.commentOffsetA = 0 L.commentOffsetB = 0 -proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream; - cache: IdentCache) = +proc openLexer*(lex: var TLexer, fileIdx: FileIndex, inputstream: PLLStream; + cache: IdentCache; config: ConfigRef) = openBaseLexer(lex, inputstream) lex.fileIdx = fileidx lex.indentAhead = - 1 @@ -232,13 +230,15 @@ proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream; lex.cache = cache when defined(nimsuggest): lex.previousToken.fileIndex = fileIdx + lex.config = config proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream; - cache: IdentCache) = - openLexer(lex, filename.fileInfoIdx, inputstream, cache) + cache: IdentCache; config: ConfigRef) = + openLexer(lex, fileInfoIdx(config, filename), inputstream, cache, config) proc closeLexer*(lex: var TLexer) = - inc(gLinesCompiled, lex.lineNumber) + if lex.config != nil: + inc(lex.config.linesCompiled, lex.lineNumber) closeBaseLexer(lex) proc getLineInfo(L: TLexer): TLineInfo = @@ -246,9 +246,9 @@ proc getLineInfo(L: TLexer): TLineInfo = proc dispMessage(L: TLexer; info: TLineInfo; msg: TMsgKind; arg: string) = if L.errorHandler.isNil: - msgs.message(info, msg, arg) + msgs.message(L.config, info, msg, arg) else: - L.errorHandler(info, msg, arg) + L.errorHandler(L.config, info, msg, arg) proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") = L.dispMessage(getLineInfo(L), msg, arg) @@ -274,7 +274,7 @@ template tokenEnd(tok, pos) {.dirty.} = when defined(nimsuggest): let colB = getColNumber(L, pos)+1 if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and - L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}: + L.lineNumber == gTrackPos.line.int and L.config.ideCmd in {ideSug, ideCon}: L.cursor = CursorPosition.InToken gTrackPos.col = colA.int16 colA = 0 @@ -285,9 +285,9 @@ template tokenEndIgnore(tok, pos) = when defined(nimsuggest): let colB = getColNumber(L, pos) if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and - L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}: + L.lineNumber == gTrackPos.line.int and L.config.ideCmd in {ideSug, ideCon}: gTrackPos.fileIndex = trackPosInvalidFileIdx - gTrackPos.line = -1 + gTrackPos.line = 0'u16 colA = 0 when defined(nimpretty): tok.offsetB = L.offsetBase + pos @@ -299,7 +299,7 @@ template tokenEndPrevious(tok, pos) = # the cursor in a string literal or comment: let colB = getColNumber(L, pos) if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and - L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}: + L.lineNumber == gTrackPos.line.int and L.config.ideCmd in {ideSug, ideCon}: L.cursor = CursorPosition.BeforeToken gTrackPos = L.previousToken gTrackPosAttached = true @@ -311,12 +311,12 @@ template tokenEndPrevious(tok, pos) = # 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'}: + if i < s.len and s[i] in {'0'..'9'}: b = 0 - while s[i] in {'0'..'9'}: + while i < s.len and 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 + while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored result = i - start {.pop.} # overflowChecks @@ -341,7 +341,8 @@ proc getNumber(L: var TLexer, result: var TToken) = break if buf[pos] == '_': if buf[pos+1] notin chars: - lexMessage(L, errInvalidToken, "_") + lexMessage(L, errGenerated, + "only single underscores may occur in a token: '__' is invalid") break add(tok.literal, '_') inc(pos) @@ -355,7 +356,7 @@ proc getNumber(L: var TLexer, result: var TToken) = inc(pos) L.bufpos = pos - proc lexMessageLitNum(L: var TLexer, msg: TMsgKind, startpos: int) = + proc lexMessageLitNum(L: var TLexer, msg: string, startpos: int) = # Used to get slightly human friendlier err messages. # Note: the erroneous 'O' char in the character set is intentional const literalishChars = {'A'..'F', 'a'..'f', '0'..'9', 'X', 'x', 'o', 'O', @@ -376,7 +377,7 @@ proc getNumber(L: var TLexer, result: var TToken) = add(t.literal, L.buf[L.bufpos]) matchChars(L, t, {'0'..'9'}) L.bufpos = msgPos - lexMessage(L, msg, t.literal) + lexMessage(L, errGenerated, msg % t.literal) var startpos, endpos: int @@ -398,7 +399,7 @@ proc getNumber(L: var TLexer, result: var TToken) = eatChar(L, result, '0') case L.buf[L.bufpos] of 'O': - lexMessageLitNum(L, errInvalidNumberOctalCode, startpos) + lexMessageLitNum(L, "$1 is not a valid number; did you mean octal? Then use one of '0o', '0c' or '0C'.", startpos) of 'x', 'X': eatChar(L, result, 'x') matchUnderscoreChars(L, result, {'0'..'9', 'a'..'f', 'A'..'F'}) @@ -409,7 +410,7 @@ proc getNumber(L: var TLexer, result: var TToken) = eatChar(L, result, 'b') matchUnderscoreChars(L, result, {'0'..'1'}) else: - internalError(getLineInfo(L), "getNumber") + internalError(L.config, getLineInfo(L), "getNumber") else: matchUnderscoreChars(L, result, {'0'..'9'}) if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}): @@ -464,7 +465,7 @@ proc getNumber(L: var TLexer, result: var TToken) = result.tokType = tkInt8Lit inc(postPos) else: - lexMessageLitNum(L, errInvalidNumber, startpos) + lexMessageLitNum(L, "invalid number: '$1'", startpos) of 'u', 'U': inc(postPos) if (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'): @@ -482,12 +483,12 @@ proc getNumber(L: var TLexer, result: var TToken) = else: result.tokType = tkUIntLit else: - lexMessageLitNum(L, errInvalidNumber, startpos) + lexMessageLitNum(L, "invalid number: '$1'", startpos) # Is there still a literalish char awaiting? Then it's an error! if L.buf[postPos] in literalishChars or (L.buf[postPos] == '.' and L.buf[postPos + 1] in {'0'..'9'}): - lexMessageLitNum(L, errInvalidNumber, startpos) + lexMessageLitNum(L, "invalid number: '$1'", startpos) # Third stage, extract actual number L.bufpos = startpos # restore position @@ -528,7 +529,7 @@ proc getNumber(L: var TLexer, result: var TToken) = else: break else: - internalError(getLineInfo(L), "getNumber") + internalError(L.config, getLineInfo(L), "getNumber") case result.tokType of tkIntLit, tkInt64Lit: result.iNumber = xi @@ -545,7 +546,7 @@ proc getNumber(L: var TLexer, result: var TToken) = # XXX: Test this on big endian machine! of tkFloat64Lit, tkFloatLit: result.fNumber = (cast[PFloat64](addr(xi)))[] - else: internalError(getLineInfo(L), "getNumber") + else: internalError(L.config, getLineInfo(L), "getNumber") # Bounds checks. Non decimal literals are allowed to overflow the range of # the datatype as long as their pattern don't overflow _bitwise_, hence @@ -561,7 +562,7 @@ proc getNumber(L: var TLexer, result: var TToken) = if outOfRange: #echo "out of range num: ", result.iNumber, " vs ", xi - lexMessageLitNum(L, errNumberOutOfRange, startpos) + lexMessageLitNum(L, "number out of range: '$1'", startpos) else: case result.tokType @@ -577,19 +578,20 @@ proc getNumber(L: var TLexer, result: var TToken) = result.iNumber = parseBiggestInt(result.literal) # Explicit bounds checks - let outOfRange = case result.tokType: - of tkInt8Lit: (result.iNumber < int8.low or result.iNumber > int8.high) - of tkUInt8Lit: (result.iNumber < BiggestInt(uint8.low) or - result.iNumber > BiggestInt(uint8.high)) - of tkInt16Lit: (result.iNumber < int16.low or result.iNumber > int16.high) - of tkUInt16Lit: (result.iNumber < BiggestInt(uint16.low) or - result.iNumber > BiggestInt(uint16.high)) - of tkInt32Lit: (result.iNumber < int32.low or result.iNumber > int32.high) - of tkUInt32Lit: (result.iNumber < BiggestInt(uint32.low) or - result.iNumber > BiggestInt(uint32.high)) - else: false - - if outOfRange: lexMessageLitNum(L, errNumberOutOfRange, startpos) + let outOfRange = + case result.tokType + of tkInt8Lit: (result.iNumber < int8.low or result.iNumber > int8.high) + of tkUInt8Lit: (result.iNumber < BiggestInt(uint8.low) or + result.iNumber > BiggestInt(uint8.high)) + of tkInt16Lit: (result.iNumber < int16.low or result.iNumber > int16.high) + of tkUInt16Lit: (result.iNumber < BiggestInt(uint16.low) or + result.iNumber > BiggestInt(uint16.high)) + of tkInt32Lit: (result.iNumber < int32.low or result.iNumber > int32.high) + of tkUInt32Lit: (result.iNumber < BiggestInt(uint32.low) or + result.iNumber > BiggestInt(uint32.high)) + else: false + + if outOfRange: lexMessageLitNum(L, "number out of range: '$1'", startpos) # Promote int literal to int64? Not always necessary, but more consistent if result.tokType == tkIntLit: @@ -597,9 +599,9 @@ proc getNumber(L: var TLexer, result: var TToken) = result.tokType = tkInt64Lit except ValueError: - lexMessageLitNum(L, errInvalidNumber, startpos) + lexMessageLitNum(L, "invalid number: '$1'", startpos) except OverflowError, RangeError: - lexMessageLitNum(L, errNumberOutOfRange, startpos) + lexMessageLitNum(L, "number out of range: '$1'", startpos) tokenEnd(result, postPos-1) L.bufpos = postPos @@ -625,7 +627,16 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) = inc(L.bufpos) # skip '\' case L.buf[L.bufpos] of 'n', 'N': - if tok.tokType == tkCharLit: lexMessage(L, errNnotAllowedInCharacter) + if L.config.oldNewlines: + if tok.tokType == tkCharLit: + lexMessage(L, errGenerated, "\\n not allowed in character literal") + add(tok.literal, tnl) + else: + add(tok.literal, '\L') + inc(L.bufpos) + of 'p', 'P': + if tok.tokType == tkCharLit: + lexMessage(L, errGenerated, "\\p not allowed in character literal") add(tok.literal, tnl) inc(L.bufpos) of 'r', 'R', 'c', 'C': @@ -687,8 +698,8 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) = var xi = 0 handleDecChars(L, xi) if (xi <= 255): add(tok.literal, chr(xi)) - else: lexMessage(L, errInvalidCharacterConstant) - else: lexMessage(L, errInvalidCharacterConstant) + else: lexMessage(L, errGenerated, "invalid character constant") + else: lexMessage(L, errGenerated, "invalid character constant") proc newString(s: cstring, len: int): string = ## XXX, how come there is no support for this? @@ -703,7 +714,7 @@ proc handleCRLF(L: var TLexer, pos: int): int = if col > MaxLineLength: lexMessagePos(L, hintLineTooLong, pos) - if optEmbedOrigSrc in gGlobalOptions: + if optEmbedOrigSrc in L.config.globalOptions: let lineStart = cast[ByteAddress](L.buf) + L.lineStart let line = newString(cast[cstring](lineStart), col) addSourceLine(L.fileIdx, line) @@ -747,12 +758,12 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) = tokenEndIgnore(tok, pos) pos = handleCRLF(L, pos) buf = L.buf - add(tok.literal, tnl) + add(tok.literal, "\n") of nimlexbase.EndOfFile: tokenEndIgnore(tok, pos) var line2 = L.lineNumber L.lineNumber = line - lexMessagePos(L, errClosingTripleQuoteExpected, L.lineStart) + lexMessagePos(L, errGenerated, L.lineStart, "closing \"\"\" expected, but end of file reached") L.lineNumber = line2 L.bufpos = pos break @@ -775,7 +786,7 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) = break elif c in {CR, LF, nimlexbase.EndOfFile}: tokenEndIgnore(tok, pos) - lexMessage(L, errClosingQuoteExpected) + lexMessage(L, errGenerated, "closing \" expected") break elif (c == '\\') and not rawMode: L.bufpos = pos @@ -791,12 +802,13 @@ proc getCharacter(L: var TLexer, tok: var TToken) = inc(L.bufpos) # skip ' var c = L.buf[L.bufpos] case c - of '\0'..pred(' '), '\'': lexMessage(L, errInvalidCharacterConstant) + of '\0'..pred(' '), '\'': lexMessage(L, errGenerated, "invalid character literal") of '\\': getEscapedChar(L, tok) else: tok.literal = $c inc(L.bufpos) - if L.buf[L.bufpos] != '\'': lexMessage(L, errMissingFinalQuote) + if L.buf[L.bufpos] != '\'': + lexMessage(L, errGenerated, "missing closing ' for character literal") tokenEndIgnore(tok, L.bufpos) inc(L.bufpos) # skip ' @@ -817,7 +829,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) = inc(pos) of '_': if buf[pos+1] notin SymChars: - lexMessage(L, errInvalidToken, "_") + lexMessage(L, errGenerated, "invalid token: trailing underscore") break inc(pos) else: break @@ -995,13 +1007,17 @@ proc skip(L: var TLexer, tok: var TToken) = var buf = L.buf tokenBegin(tok, pos) tok.strongSpaceA = 0 + when defined(nimpretty): + var hasComment = false + tok.commentOffsetA = L.offsetBase + pos + tok.commentOffsetB = tok.commentOffsetA while true: case buf[pos] of ' ': inc(pos) inc(tok.strongSpaceA) of '\t': - if not L.allowTabs: lexMessagePos(L, errTabulatorsAreNotAllowed, pos) + if not L.allowTabs: lexMessagePos(L, errGenerated, pos, "tabulators are not allowed") inc(pos) of CR, LF: tokenEndPrevious(tok, pos) @@ -1013,6 +1029,7 @@ proc skip(L: var TLexer, tok: var TToken) = inc(pos) inc(indent) elif buf[pos] == '#' and buf[pos+1] == '[': + when defined(nimpretty): hasComment = true skipMultiLineComment(L, tok, pos+2, false) pos = L.bufpos buf = L.buf @@ -1026,14 +1043,11 @@ proc skip(L: var TLexer, tok: var TToken) = of '#': # do not skip documentation comment: if buf[pos+1] == '#': break - when defined(nimpretty): - tok.commentOffsetA = L.offsetBase + pos + when defined(nimpretty): hasComment = true if buf[pos+1] == '[': skipMultiLineComment(L, tok, pos+2, false) pos = L.bufpos buf = L.buf - when defined(nimpretty): - tok.commentOffsetB = L.offsetBase + pos else: tokenBegin(tok, pos) while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos) @@ -1045,6 +1059,9 @@ proc skip(L: var TLexer, tok: var TToken) = tokenEndPrevious(tok, pos-1) L.bufpos = pos when defined(nimpretty): + if hasComment: + tok.commentOffsetB = L.offsetBase + pos + tok.tokType = tkComment if gIndentationWidth <= 0: gIndentationWidth = tok.indent @@ -1053,7 +1070,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = when defined(nimsuggest): # we attach the cursor to the last *strong* token if tok.tokType notin weakTokens: - L.previousToken.line = tok.line.int16 + L.previousToken.line = tok.line.uint16 L.previousToken.col = tok.col.int16 when defined(nimsuggest): @@ -1066,6 +1083,10 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = else: tok.indent = -1 skip(L, tok) + when defined(nimpretty): + if tok.tokType == tkComment: + L.indentAhead = L.currLineIndent + return var c = L.buf[L.bufpos] tok.line = L.lineNumber tok.col = getColNumber(L, L.bufpos) @@ -1101,7 +1122,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = tok.tokType = tkParLe when defined(nimsuggest): if L.fileIdx == gTrackPos.fileIndex and tok.col < gTrackPos.col and - tok.line == gTrackPos.line and gIdeCmd == ideCon: + tok.line == gTrackPos.line.int and L.config.ideCmd == ideCon: gTrackPos.col = tok.col.int16 of ')': tok.tokType = tkParRi @@ -1111,6 +1132,9 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = if L.buf[L.bufpos] == '.' and L.buf[L.bufpos+1] != '.': tok.tokType = tkBracketDotLe inc(L.bufpos) + elif L.buf[L.bufpos] == ':': + tok.tokType = tkBracketLeColon + inc(L.bufpos) else: tok.tokType = tkBracketLe of ']': @@ -1119,7 +1143,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = of '.': when defined(nimsuggest): if L.fileIdx == gTrackPos.fileIndex and tok.col+1 == gTrackPos.col and - tok.line == gTrackPos.line and gIdeCmd == ideSug: + tok.line == gTrackPos.line.int and L.config.ideCmd == ideSug: tok.tokType = tkDot L.cursor = CursorPosition.InToken gTrackPos.col = tok.col.int16 @@ -1161,7 +1185,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = else: tok.literal = $c tok.tokType = tkInvalid - lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')') + lexMessage(L, errGenerated, "invalid token: " & c & " (\\" & $(ord(c)) & ')') of '\"': # check for extended raw string literal: var rawMode = L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars @@ -1178,7 +1202,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = getNumber(L, tok) let c = L.buf[L.bufpos] if c in SymChars+{'_'}: - lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')') + lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier") else: if c in OpChars: getOperator(L, tok) @@ -1188,6 +1212,6 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = else: tok.literal = $c tok.tokType = tkInvalid - lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')') + lexMessage(L, errGenerated, "invalid token: " & c & " (\\" & $(ord(c)) & ')') inc(L.bufpos) atTokenEnd() diff --git a/compiler/liftlocals.nim b/compiler/liftlocals.nim index 3610a1486..4603d357b 100644 --- a/compiler/liftlocals.nim +++ b/compiler/liftlocals.nim @@ -52,17 +52,17 @@ proc lookupParam(params, dest: PNode): PSym = if params[i].kind == nkSym and params[i].sym.name.id == dest.ident.id: return params[i].sym -proc liftLocalsIfRequested*(prc: PSym; n: PNode): PNode = +proc liftLocalsIfRequested*(prc: PSym; n: PNode; conf: ConfigRef): PNode = let liftDest = getPragmaVal(prc.ast, wLiftLocals) if liftDest == nil: return n let partialParam = lookupParam(prc.typ.n, liftDest) if partialParam.isNil: - localError(liftDest.info, "'$1' is not a parameter of '$2'" % + localError(conf, liftDest.info, "'$1' is not a parameter of '$2'" % [$liftDest, prc.name.s]) return n let objType = partialParam.typ.skipTypes(abstractPtrs) if objType.kind != tyObject or tfPartial notin objType.flags: - localError(liftDest.info, "parameter '$1' is not a pointer to a partial object" % $liftDest) + localError(conf, liftDest.info, "parameter '$1' is not a pointer to a partial object" % $liftDest) return n var c = Ctx(partialParam: partialParam, objType: objType) let w = newTree(nkStmtList, n) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index c409acc59..b6d63d0bd 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -11,23 +11,23 @@ import intsets, ast, astalgo, idents, semdata, types, msgs, options, rodread, - renderer, wordrecg, idgen, nimfix.prettybase + renderer, wordrecg, idgen, nimfix.prettybase, configuration, strutils -proc ensureNoMissingOrUnusedSymbols(scope: PScope) +proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) -proc noidentError(n, origin: PNode) = +proc noidentError(conf: ConfigRef; n, origin: PNode) = var m = "" if origin != nil: m.add "in expression '" & origin.renderTree & "': " m.add "identifier expected, but found '" & n.renderTree & "'" - localError(n.info, m) + localError(conf, n.info, m) -proc considerQuotedIdent*(n: PNode, origin: PNode = nil): PIdent = +proc considerQuotedIdent*(conf: ConfigRef; n: PNode, origin: PNode = nil): PIdent = ## Retrieve a PIdent from a PNode, taking into account accent nodes. ## ``origin`` can be nil. If it is not nil, it is used for a better ## error message. template handleError(n, origin: PNode) = - noidentError(n, origin) + noidentError(conf, n, origin) result = getIdent"<Error>" case n.kind @@ -36,7 +36,7 @@ proc considerQuotedIdent*(n: PNode, origin: PNode = nil): PIdent = of nkAccQuoted: case n.len of 0: handleError(n, origin) - of 1: result = considerQuotedIdent(n.sons[0], origin) + of 1: result = considerQuotedIdent(conf, n.sons[0], origin) else: var id = "" for i in 0..<n.len: @@ -44,6 +44,7 @@ proc considerQuotedIdent*(n: PNode, origin: PNode = nil): PIdent = case x.kind of nkIdent: id.add(x.ident.s) of nkSym: id.add(x.sym.name.s) + of nkLiterals - nkFloatLiterals: id.add(x.renderTree) else: handleError(n, origin) result = getIdent(id) of nkOpenSymChoice, nkClosedSymChoice: @@ -70,7 +71,7 @@ proc rawCloseScope*(c: PContext) = c.currentScope = c.currentScope.parent proc closeScope*(c: PContext) = - ensureNoMissingOrUnusedSymbols(c.currentScope) + ensureNoMissingOrUnusedSymbols(c, c.currentScope) rawCloseScope(c) iterator walkScopes*(scope: PScope): PScope = @@ -79,15 +80,15 @@ iterator walkScopes*(scope: PScope): PScope = yield current current = current.parent -proc skipAlias*(s: PSym; n: PNode): PSym = +proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym = if s == nil or s.kind != skAlias: result = s else: result = s.owner - if gCmd == cmdPretty: + if conf.cmd == cmdPretty: prettybase.replaceDeprecated(n.info, s, result) else: - message(n.info, warnDeprecated, "use " & result.name.s & " instead; " & + message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " & s.name.s) proc localSearchInScope*(c: PContext, s: PIdent): PSym = @@ -124,14 +125,14 @@ proc errorSym*(c: PContext, n: PNode): PSym = # ensure that 'considerQuotedIdent' can't fail: if m.kind == nkDotExpr: m = m.sons[1] let ident = if m.kind in {nkIdent, nkSym, nkAccQuoted}: - considerQuotedIdent(m) + considerQuotedIdent(c.config, m) else: getIdent("err:" & renderTree(m)) - result = newSym(skError, ident, getCurrOwner(c), n.info) + result = newSym(skError, ident, getCurrOwner(c), n.info, {}) result.typ = errorType(c) incl(result.flags, sfDiscardable) # pretend it's imported from some unknown module to prevent cascading errors: - if gCmd != cmdInteractive and c.compilesContextId == 0: + if c.config.cmd != cmdInteractive and c.compilesContextId == 0: c.importTable.addSym(result) type @@ -153,7 +154,7 @@ proc getSymRepr*(s: PSym): string = else: result = s.name.s -proc ensureNoMissingOrUnusedSymbols(scope: PScope) = +proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) = # check if all symbols have been used and defined: var it: TTabIter var s = initTabIter(it, scope.symbols) @@ -163,54 +164,53 @@ proc ensureNoMissingOrUnusedSymbols(scope: PScope) = # too many 'implementation of X' errors are annoying # and slow 'suggest' down: if missingImpls == 0: - localError(s.info, errImplOfXexpected, getSymRepr(s)) + localError(c.config, s.info, "implementation of '$1' expected" % getSymRepr(s)) inc missingImpls elif {sfUsed, sfExported} * s.flags == {} and optHints in s.options: - # BUGFIX: check options in s! if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}: # XXX: implicit type params are currently skTypes # maybe they can be made skGenericParam as well. if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and s.typ.kind != tyGenericParam: - message(s.info, hintXDeclaredButNotUsed, getSymRepr(s)) + message(c.config, s.info, hintXDeclaredButNotUsed, getSymRepr(s)) s = nextIter(it, scope.symbols) -proc wrongRedefinition*(info: TLineInfo, s: string) = - if gCmd != cmdInteractive: - localError(info, errAttemptToRedefine, s) +proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string) = + if c.config.cmd != cmdInteractive: + localError(c.config, info, "redefinition of '$1'" % s) proc addDecl*(c: PContext, sym: PSym, info: TLineInfo) = if not c.currentScope.addUniqueSym(sym): - wrongRedefinition(info, sym.name.s) + wrongRedefinition(c, info, sym.name.s) proc addDecl*(c: PContext, sym: PSym) = if not c.currentScope.addUniqueSym(sym): - wrongRedefinition(sym.info, sym.name.s) + wrongRedefinition(c, sym.info, sym.name.s) proc addPrelimDecl*(c: PContext, sym: PSym) = discard c.currentScope.addUniqueSym(sym) -proc addDeclAt*(scope: PScope, sym: PSym) = +proc addDeclAt*(c: PContext; scope: PScope, sym: PSym) = if not scope.addUniqueSym(sym): - wrongRedefinition(sym.info, sym.name.s) + wrongRedefinition(c, sym.info, sym.name.s) proc addInterfaceDeclAux(c: PContext, sym: PSym) = if sfExported in sym.flags: # add to interface: if c.module != nil: strTableAdd(c.module.tab, sym) - else: internalError(sym.info, "addInterfaceDeclAux") + else: internalError(c.config, sym.info, "addInterfaceDeclAux") proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) = - addDeclAt(scope, sym) + addDeclAt(c, scope, sym) addInterfaceDeclAux(c, sym) -proc addOverloadableSymAt*(scope: PScope, fn: PSym) = +proc addOverloadableSymAt*(c: PContext; scope: PScope, fn: PSym) = if fn.kind notin OverloadableSyms: - internalError(fn.info, "addOverloadableSymAt") + internalError(c.config, fn.info, "addOverloadableSymAt") return let check = strTableGet(scope.symbols, fn.name) if check != nil and check.kind notin OverloadableSyms: - wrongRedefinition(fn.info, fn.name.s) + wrongRedefinition(c, fn.info, fn.name.s) else: scope.addSym(fn) @@ -221,12 +221,10 @@ proc addInterfaceDecl*(c: PContext, sym: PSym) = proc addInterfaceOverloadableSymAt*(c: PContext, scope: PScope, sym: PSym) = # it adds the symbol to the interface if appropriate - addOverloadableSymAt(scope, sym) + addOverloadableSymAt(c, scope, sym) addInterfaceDeclAux(c, sym) when defined(nimfix): - import strutils - # when we cannot find the identifier, retry with a changed identifer: proc altSpelling(x: PIdent): PIdent = case x.s[0] @@ -254,7 +252,7 @@ proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) = err.add candidate.owner.name.s & "." & candidate.name.s candidate = nextIdentIter(ti, c.importTable.symbols) inc i - localError(info, errGenerated, err) + localError(c.config, info, errGenerated, err) proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string) = var err = "undeclared identifier: '" & name & "'" @@ -263,13 +261,13 @@ proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string) = err.add c.recursiveDep # prevent excessive errors for 'nim check' c.recursiveDep = nil - localError(info, errGenerated, err) + localError(c.config, info, errGenerated, err) proc lookUp*(c: PContext, n: PNode): PSym = # Looks up a symbol. Generates an error in case of nil. case n.kind of nkIdent: - result = searchInScopes(c, n.ident).skipAlias(n) + result = searchInScopes(c, n.ident).skipAlias(n, c.config) if result == nil: fixSpelling(n, n.ident, searchInScopes) errorUndeclaredIdentifier(c, n.info, n.ident.s) @@ -277,14 +275,14 @@ proc lookUp*(c: PContext, n: PNode): PSym = of nkSym: result = n.sym of nkAccQuoted: - var ident = considerQuotedIdent(n) - result = searchInScopes(c, ident).skipAlias(n) + var ident = considerQuotedIdent(c.config, n) + result = searchInScopes(c, ident).skipAlias(n, c.config) if result == nil: fixSpelling(n, ident, searchInScopes) errorUndeclaredIdentifier(c, n.info, ident.s) result = errorSym(c, n) else: - internalError(n.info, "lookUp") + internalError(c.config, n.info, "lookUp") return if contains(c.ambiguousSymbols, result.id): errorUseQualifier(c, n.info, result) @@ -298,11 +296,11 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = const allExceptModule = {low(TSymKind)..high(TSymKind)}-{skModule,skPackage} case n.kind of nkIdent, nkAccQuoted: - var ident = considerQuotedIdent(n) + var ident = considerQuotedIdent(c.config, n) if checkModule in flags: - result = searchInScopes(c, ident).skipAlias(n) + result = searchInScopes(c, ident).skipAlias(n, c.config) else: - result = searchInScopes(c, ident, allExceptModule).skipAlias(n) + result = searchInScopes(c, ident, allExceptModule).skipAlias(n, c.config) if result == nil and checkPureEnumFields in flags: result = strTableGet(c.pureEnumFields, ident) if result == nil and checkUndeclared in flags: @@ -324,12 +322,12 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = if n.sons[1].kind == nkIdent: ident = n.sons[1].ident elif n.sons[1].kind == nkAccQuoted: - ident = considerQuotedIdent(n.sons[1]) + ident = considerQuotedIdent(c.config, n.sons[1]) if ident != nil: if m == c.module: - result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n) + result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n, c.config) else: - result = strTableGet(m.tab, ident).skipAlias(n) + result = strTableGet(m.tab, ident).skipAlias(n, c.config) if result == nil and checkUndeclared in flags: fixSpelling(n.sons[1], ident, searchInScopes) errorUndeclaredIdentifier(c, n.sons[1].info, ident.s) @@ -338,7 +336,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = result = n.sons[1].sym elif checkUndeclared in flags and n.sons[1].kind notin {nkOpenSymChoice, nkClosedSymChoice}: - localError(n.sons[1].info, errIdentifierExpected, + localError(c.config, n.sons[1].info, "identifier expected, but got: " & renderTree(n.sons[1])) result = errorSym(c, n.sons[1]) else: @@ -348,11 +346,11 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = case n.kind of nkIdent, nkAccQuoted: - var ident = considerQuotedIdent(n) + var ident = considerQuotedIdent(c.config, n) o.scope = c.currentScope o.mode = oimNoQualifier while true: - result = initIdentIter(o.it, o.scope.symbols, ident).skipAlias(n) + result = initIdentIter(o.it, o.scope.symbols, ident).skipAlias(n, c.config) if result != nil: break else: @@ -369,17 +367,17 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = if n.sons[1].kind == nkIdent: ident = n.sons[1].ident elif n.sons[1].kind == nkAccQuoted: - ident = considerQuotedIdent(n.sons[1], n) + ident = considerQuotedIdent(c.config, n.sons[1], n) if ident != nil: if o.m == c.module: # a module may access its private members: result = initIdentIter(o.it, c.topLevelScope.symbols, - ident).skipAlias(n) + ident).skipAlias(n, c.config) o.mode = oimSelfModule else: - result = initIdentIter(o.it, o.m.tab, ident).skipAlias(n) + result = initIdentIter(o.it, o.m.tab, ident).skipAlias(n, c.config) else: - noidentError(n.sons[1], n) + noidentError(c.config, n.sons[1], n) result = errorSym(c, n.sons[1]) of nkClosedSymChoice, nkOpenSymChoice: o.mode = oimSymChoice @@ -407,18 +405,18 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = result = nil of oimNoQualifier: if o.scope != nil: - result = nextIdentIter(o.it, o.scope.symbols).skipAlias(n) + result = nextIdentIter(o.it, o.scope.symbols).skipAlias(n, c.config) while result == nil: o.scope = o.scope.parent if o.scope == nil: break - result = initIdentIter(o.it, o.scope.symbols, o.it.name).skipAlias(n) + result = initIdentIter(o.it, o.scope.symbols, o.it.name).skipAlias(n, c.config) # BUGFIX: o.it.name <-> n.ident else: result = nil of oimSelfModule: - result = nextIdentIter(o.it, c.topLevelScope.symbols).skipAlias(n) + result = nextIdentIter(o.it, c.topLevelScope.symbols).skipAlias(n, c.config) of oimOtherModule: - result = nextIdentIter(o.it, o.m.tab).skipAlias(n) + result = nextIdentIter(o.it, o.m.tab).skipAlias(n, c.config) of oimSymChoice: if o.symChoiceIndex < sonsLen(n): result = n.sons[o.symChoiceIndex].sym @@ -429,19 +427,19 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = o.mode = oimSymChoiceLocalLookup o.scope = c.currentScope result = firstIdentExcluding(o.it, o.scope.symbols, - n.sons[0].sym.name, o.inSymChoice).skipAlias(n) + n.sons[0].sym.name, o.inSymChoice).skipAlias(n, c.config) while result == nil: o.scope = o.scope.parent if o.scope == nil: break result = firstIdentExcluding(o.it, o.scope.symbols, - n.sons[0].sym.name, o.inSymChoice).skipAlias(n) + n.sons[0].sym.name, o.inSymChoice).skipAlias(n, c.config) of oimSymChoiceLocalLookup: - result = nextIdentExcluding(o.it, o.scope.symbols, o.inSymChoice).skipAlias(n) + result = nextIdentExcluding(o.it, o.scope.symbols, o.inSymChoice).skipAlias(n, c.config) while result == nil: o.scope = o.scope.parent if o.scope == nil: break result = firstIdentExcluding(o.it, o.scope.symbols, - n.sons[0].sym.name, o.inSymChoice).skipAlias(n) + n.sons[0].sym.name, o.inSymChoice).skipAlias(n, c.config) if result != nil and result.kind == skStub: loadStub(result) @@ -455,5 +453,3 @@ proc pickSym*(c: PContext, n: PNode; kinds: set[TSymKind]; else: return nil # ambiguous a = nextOverloadIter(o, c, n) -proc isInfixAs*(n: PNode): bool = - return n.kind == nkInfix and considerQuotedIdent(n[0]).s == "as" \ No newline at end of file diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 9612ff0ab..13336f00e 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -12,18 +12,18 @@ const genPrefix* = ":tmp" # prefix for generated names -import ast, astalgo, types, idents, magicsys, msgs, options +import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs 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*(g: ModuleGraph; tup: PNode, i: int): PNode = result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes( abstractInst).sons[i]) addSon(result, copyTree(tup)) - var lit = newNodeIT(nkIntLit, tup.info, getSysType(tyInt)) + var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt)) lit.intVal = i addSon(result, lit) @@ -44,12 +44,12 @@ proc newFastAsgnStmt(le, ri: PNode): PNode = result.sons[0] = le result.sons[1] = ri -proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode = +proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; owner: PSym): PNode = assert n.kind == nkVarTuple let value = n.lastSon result = newNodeI(nkStmtList, n.info) - var temp = newSym(skTemp, getIdent(genPrefix), owner, value.info) + var temp = newSym(skTemp, getIdent(genPrefix), owner, value.info, g.config.options) temp.typ = skipTypes(value.typ, abstractInst) incl(temp.flags, sfFromGeneric) @@ -61,7 +61,7 @@ proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode = result.add newAsgnStmt(tempAsNode, value) for i in 0 .. n.len-3: if n.sons[i].kind == nkSym: v.addVar(n.sons[i]) - result.add newAsgnStmt(n.sons[i], newTupleAccess(tempAsNode, i)) + result.add newAsgnStmt(n.sons[i], newTupleAccess(g, tempAsNode, i)) proc newTupleAccessRaw*(tup: PNode, i: int): PNode = result = newNodeI(nkBracketExpr, tup.info) @@ -77,7 +77,7 @@ proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode = let value = n.lastSon result = newNodeI(nkStmtList, n.info) - var temp = newSym(skLet, getIdent("_"), owner, value.info) + var temp = newSym(skLet, getIdent("_"), owner, value.info, owner.options) var v = newNodeI(nkLetSection, value.info) let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info) @@ -95,7 +95,7 @@ proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode = proc lowerSwap*(n: PNode; owner: PSym): PNode = result = newNodeI(nkStmtList, n.info) # note: cannot use 'skTemp' here cause we really need the copy for the VM :-( - var temp = newSym(skVar, getIdent(genPrefix), owner, n.info) + var temp = newSym(skVar, getIdent(genPrefix), owner, n.info, owner.options) temp.typ = n.sons[1].typ incl(temp.flags, sfFromGeneric) @@ -112,16 +112,16 @@ proc lowerSwap*(n: PNode; owner: PSym): PNode = result.add newFastAsgnStmt(n[1], n[2]) result.add newFastAsgnStmt(n[2], tempAsNode) -proc createObj*(owner: PSym, info: TLineInfo; final=true): PType = +proc createObj*(g: ModuleGraph; owner: PSym, info: TLineInfo; final=true): PType = result = newType(tyObject, owner) if final: rawAddSon(result, nil) incl result.flags, tfFinal else: - rawAddSon(result, getCompilerProc("RootObj").typ) + rawAddSon(result, getCompilerProc(g, "RootObj").typ) result.n = newNodeI(nkRecList, info) let s = newSym(skType, getIdent("Env_" & info.toFilename), - owner, info) + owner, info, owner.options) incl s.flags, sfAnon s.typ = result result.sym = s @@ -174,7 +174,8 @@ proc lookupInRecord(n: PNode, id: int): PSym = proc addField*(obj: PType; s: PSym) = # because of 'gensym' support, we have to mangle the name with its ID. # This is hacky but the clean solution is much more complex than it looks. - var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info) + var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info, + s.options) field.id = -s.id let t = skipIntLit(s.typ) field.typ = t @@ -185,7 +186,8 @@ proc addField*(obj: PType; s: PSym) = proc addUniqueField*(obj: PType; s: PSym): PSym {.discardable.} = result = lookupInRecord(obj.n, s.id) if result == nil: - var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info) + var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info, + s.options) field.id = -s.id let t = skipIntLit(s.typ) field.typ = t @@ -218,7 +220,7 @@ proc indirectAccess*(a: PNode, b: int, info: TLineInfo): PNode = #if field == nil: # echo "FIELD ", b # debug deref.typ - internalAssert field != nil + assert field != nil addSon(deref, a) result = newNodeI(nkDotExpr, info) addSon(result, deref) @@ -242,7 +244,7 @@ proc indirectAccess(a: PNode, b: string, info: TLineInfo): PNode = #if field == nil: # echo "FIELD ", b # debug deref.typ - internalAssert field != nil + assert field != nil addSon(deref, a) result = newNodeI(nkDotExpr, info) addSon(result, deref) @@ -278,12 +280,12 @@ proc genDeref*(n: PNode): PNode = n.typ.skipTypes(abstractInst).sons[0]) result.add n -proc callCodegenProc*(name: string, arg1: PNode; +proc callCodegenProc*(g: ModuleGraph; name: string, arg1: PNode; arg2, arg3, optionalArgs: PNode = nil): PNode = result = newNodeI(nkCall, arg1.info) - let sym = magicsys.getCompilerProc(name) + let sym = magicsys.getCompilerProc(g, name) if sym == nil: - localError(arg1.info, errSystemNeeds, name) + localError(g.config, arg1.info, "system module needs: " & name) else: result.add newSymNode(sym) result.add arg1 @@ -330,12 +332,13 @@ proc typeNeedsNoDeepCopy(t: PType): bool = # 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] + if t.kind in {tyVar, tyLent, tySequence}: t = t.lastSon result = not containsGarbageCollectedRef(t) -proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType; +proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: PType; v: PNode; useShallowCopy=false): PSym = - result = newSym(skTemp, getIdent(genPrefix), owner, varSection.info) + result = newSym(skTemp, getIdent(genPrefix), owner, varSection.info, + owner.options) result.typ = typ incl(result.flags, sfFromGeneric) @@ -349,7 +352,7 @@ proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType; varInit.add newFastAsgnStmt(newSymNode(result), v) else: let deepCopyCall = newNodeI(nkCall, varInit.info, 3) - deepCopyCall.sons[0] = newSymNode(getSysMagic("deepCopy", mDeepCopy)) + deepCopyCall.sons[0] = newSymNode(getSysMagic(g, varSection.info, "deepCopy", mDeepCopy)) deepCopyCall.sons[1] = newSymNode(result) deepCopyCall.sons[2] = v varInit.add deepCopyCall @@ -384,23 +387,23 @@ stmtList: """ -proc createWrapperProc(f: PNode; threadParam, argsParam: PSym; +proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym; varSection, varInit, call, barrier, fv: PNode; spawnKind: TSpawnResult): PSym = var body = newNodeI(nkStmtList, f.info) var threadLocalBarrier: PSym if barrier != nil: var varSection2 = newNodeI(nkVarSection, barrier.info) - threadLocalBarrier = addLocalVar(varSection2, nil, argsParam.owner, + threadLocalBarrier = addLocalVar(g, varSection2, nil, argsParam.owner, barrier.typ, barrier) body.add varSection2 - body.add callCodegenProc("barrierEnter", threadLocalBarrier.newSymNode) + body.add callCodegenProc(g, "barrierEnter", threadLocalBarrier.newSymNode) var threadLocalProm: PSym if spawnKind == srByVar: - threadLocalProm = addLocalVar(varSection, nil, argsParam.owner, fv.typ, fv) + threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv) elif fv != nil: - internalAssert fv.typ.kind == tyGenericInst - threadLocalProm = addLocalVar(varSection, nil, argsParam.owner, fv.typ, fv) + internalAssert g.config, fv.typ.kind == tyGenericInst + threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv) body.add varSection body.add varInit if fv != nil and spawnKind != srByVar: @@ -409,30 +412,30 @@ proc createWrapperProc(f: PNode; threadParam, argsParam: PSym; body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode, "owner", fv.info), threadParam.newSymNode) - body.add callCodegenProc("nimArgsPassingDone", threadParam.newSymNode) + body.add callCodegenProc(g, "nimArgsPassingDone", threadParam.newSymNode) if spawnKind == srByVar: body.add newAsgnStmt(genDeref(threadLocalProm.newSymNode), call) elif fv != nil: let fk = fv.typ.sons[1].flowVarKind if fk == fvInvalid: - localError(f.info, "cannot create a flowVar of type: " & + localError(g.config, f.info, "cannot create a flowVar of type: " & typeToString(fv.typ.sons[1])) body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode, if fk == fvGC: "data" else: "blob", fv.info), call) if fk == fvGC: let incRefCall = newNodeI(nkCall, fv.info, 2) - incRefCall.sons[0] = newSymNode(getSysMagic("GCref", mGCref)) + incRefCall.sons[0] = newSymNode(getSysMagic(g, fv.info, "GCref", mGCref)) incRefCall.sons[1] = indirectAccess(threadLocalProm.newSymNode, "data", fv.info) body.add incRefCall if barrier == nil: # by now 'fv' is shared and thus might have beeen overwritten! we need # to use the thread-local view instead: - body.add callCodegenProc("nimFlowVarSignal", threadLocalProm.newSymNode) + body.add callCodegenProc(g, "nimFlowVarSignal", threadLocalProm.newSymNode) else: body.add call if barrier != nil: - body.add callCodegenProc("barrierLeave", threadLocalBarrier.newSymNode) + body.add callCodegenProc(g, "barrierLeave", threadLocalBarrier.newSymNode) var params = newNodeI(nkFormalParams, f.info) params.add emptyNode @@ -449,7 +452,8 @@ proc createWrapperProc(f: PNode; threadParam, argsParam: PSym; t.n.add argsParam.newSymNode let name = (if f.kind == nkSym: f.sym.name.s else: genPrefix) & "Wrapper" - result = newSym(skProc, getIdent(name), argsParam.owner, f.info) + result = newSym(skProc, getIdent(name), argsParam.owner, f.info, + argsParam.options) result.ast = newProcNode(nkProcDef, f.info, body, params, newSymNode(result)) result.typ = t @@ -460,7 +464,7 @@ proc createCastExpr(argsParam: PSym; objType: PType): PNode = result.typ = newType(tyPtr, objType.owner) result.typ.rawAddSon(objType) -proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym, +proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym, castExpr, call, varSection, varInit, result: PNode) = let formals = n[0].typ.n @@ -469,18 +473,18 @@ proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym, # we pick n's type here, which hopefully is 'tyArray' and not # 'tyOpenArray': var argType = n[i].typ.skipTypes(abstractInst) - if i < formals.len and formals[i].typ.kind == tyVar: - localError(n[i].info, "'spawn'ed function cannot have a 'var' parameter") + if i < formals.len and formals[i].typ.kind in {tyVar, tyLent}: + localError(g.config, n[i].info, "'spawn'ed function cannot have a 'var' parameter") #elif containsTyRef(argType): # localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure") let fieldname = if i < formals.len: formals[i].sym.name else: tmpName - var field = newSym(skField, fieldname, objType.owner, n.info) + var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options) field.typ = argType objType.addField(field) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i]) - let temp = addLocalVar(varSection, varInit, objType.owner, argType, + let temp = addLocalVar(g, varSection, varInit, objType.owner, argType, indirectAccess(castExpr, field, n.info)) call.add(newSymNode(temp)) @@ -501,20 +505,20 @@ proc getRoot*(n: PNode): PSym = if getMagic(n) == mSlice: result = getRoot(n.sons[1]) else: discard -proc newIntLit*(value: BiggestInt): PNode = +proc newIntLit*(g: ModuleGraph; info: TLineInfo; value: BiggestInt): PNode = result = nkIntLit.newIntNode(value) - result.typ = getSysType(tyInt) + result.typ = getSysType(g, info, tyInt) -proc genHigh*(n: PNode): PNode = +proc genHigh*(g: ModuleGraph; n: PNode): PNode = if skipTypes(n.typ, abstractVar).kind == tyArray: - result = newIntLit(lastOrd(skipTypes(n.typ, abstractVar))) + result = newIntLit(g, n.info, lastOrd(skipTypes(n.typ, abstractVar))) else: result = newNodeI(nkCall, n.info, 2) - result.typ = getSysType(tyInt) - result.sons[0] = newSymNode(getSysMagic("high", mHigh)) + result.typ = getSysType(g, n.info, tyInt) + result.sons[0] = newSymNode(getSysMagic(g, n.info, "high", mHigh)) result.sons[1] = n -proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym; +proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym; castExpr, call, varSection, varInit, result: PNode) = let formals = n[0].typ.n @@ -529,16 +533,16 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym; # localError(n.info, "'spawn'ed function cannot refer to 'ref'/closure") let fieldname = if i < formals.len: formals[i].sym.name else: tmpName - var field = newSym(skField, fieldname, objType.owner, n.info) + var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options) if argType.kind in {tyVarargs, tyOpenArray}: # important special case: we always create a zero-copy slice: let slice = newNodeI(nkCall, n.info, 4) slice.typ = n.typ - slice.sons[0] = newSymNode(createMagic("slice", mSlice)) - slice.sons[0].typ = getSysType(tyInt) # fake type - var fieldB = newSym(skField, tmpName, objType.owner, n.info) - fieldB.typ = getSysType(tyInt) + slice.sons[0] = newSymNode(createMagic(g, "slice", mSlice)) + slice.sons[0].typ = getSysType(g, n.info, tyInt) # fake type + var fieldB = newSym(skField, tmpName, objType.owner, n.info, g.config.options) + fieldB.typ = getSysType(g, n.info, tyInt) objType.addField(fieldB) if getMagic(n) == mSlice: @@ -547,13 +551,13 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym; objType.addField(field) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a) - var fieldA = newSym(skField, tmpName, objType.owner, n.info) - fieldA.typ = getSysType(tyInt) + var fieldA = newSym(skField, tmpName, objType.owner, n.info, g.config.options) + fieldA.typ = getSysType(g, n.info, tyInt) objType.addField(fieldA) result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2]) result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3]) - let threadLocal = addLocalVar(varSection,nil, objType.owner, fieldA.typ, + let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldA.typ, indirectAccess(castExpr, fieldA, n.info), useShallowCopy=true) slice.sons[2] = threadLocal.newSymNode @@ -562,13 +566,13 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym; field.typ = a.typ objType.addField(field) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a) - result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(n)) + result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(g, n)) - slice.sons[2] = newIntLit(0) + slice.sons[2] = newIntLit(g, n.info, 0) # the array itself does not need to go through a thread local variable: slice.sons[1] = genDeref(indirectAccess(castExpr, field, n.info)) - let threadLocal = addLocalVar(varSection,nil, objType.owner, fieldB.typ, + let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldB.typ, indirectAccess(castExpr, fieldB, n.info), useShallowCopy=true) slice.sons[3] = threadLocal.newSymNode @@ -580,7 +584,7 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym; field.typ = a.typ objType.addField(field) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a) - let threadLocal = addLocalVar(varSection,nil, objType.owner, field.typ, + let threadLocal = addLocalVar(g, varSection,nil, objType.owner, field.typ, indirectAccess(castExpr, field, n.info), useShallowCopy=true) call.add(genDeref(threadLocal.newSymNode)) @@ -589,13 +593,13 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym; field.typ = argType objType.addField(field) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n) - let threadLocal = addLocalVar(varSection, varInit, + let threadLocal = addLocalVar(g, varSection, varInit, objType.owner, field.typ, indirectAccess(castExpr, field, n.info), useShallowCopy=true) call.add(threadLocal.newSymNode) -proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType; +proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: PType; barrier, dest: PNode = nil): PNode = # if 'barrier' != nil, then it is in a 'parallel' section and we # generate quite different code @@ -603,35 +607,35 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType; let spawnKind = spawnResult(retType, barrier!=nil) case spawnKind of srVoid: - internalAssert dest == nil + internalAssert g.config, dest == nil result = newNodeI(nkStmtList, n.info) of srFlowVar: - internalAssert dest == nil + internalAssert g.config, dest == nil result = newNodeIT(nkStmtListExpr, n.info, retType) of srByVar: - if dest == nil: localError(n.info, "'spawn' must not be discarded") + if dest == nil: localError(g.config, n.info, "'spawn' must not be discarded") result = newNodeI(nkStmtList, n.info) if n.kind notin nkCallKinds: - localError(n.info, "'spawn' takes a call expression") + localError(g.config, n.info, "'spawn' takes a call expression") return - if optThreadAnalysis in gGlobalOptions: + if optThreadAnalysis in g.config.globalOptions: if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}: - localError(n.info, "'spawn' takes a GC safe call expression") + localError(g.config, n.info, "'spawn' takes a GC safe call expression") var - threadParam = newSym(skParam, getIdent"thread", owner, n.info) - argsParam = newSym(skParam, getIdent"args", owner, n.info) + threadParam = newSym(skParam, getIdent"thread", owner, n.info, g.config.options) + argsParam = newSym(skParam, getIdent"args", owner, n.info, g.config.options) block: - let ptrType = getSysType(tyPointer) + let ptrType = getSysType(g, n.info, tyPointer) threadParam.typ = ptrType argsParam.typ = ptrType argsParam.position = 1 - var objType = createObj(owner, n.info) + var objType = createObj(g, owner, n.info) incl(objType.flags, tfFinal) let castExpr = createCastExpr(argsParam, objType) - var scratchObj = newSym(skVar, getIdent"scratch", owner, n.info) + var scratchObj = newSym(skVar, getIdent"scratch", owner, n.info, g.config.options) block: scratchObj.typ = objType incl(scratchObj.flags, sfFromGeneric) @@ -644,36 +648,36 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType; # templates and macros are in fact valid here due to the nature of # the transformation: if fn.kind == nkClosure: - localError(n.info, "closure in spawn environment is not allowed") + localError(g.config, n.info, "closure in spawn environment is not allowed") if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro, skFunc, skMethod, skConverter}): # for indirect calls we pass the function pointer in the scratchObj var argType = n[0].typ.skipTypes(abstractInst) - var field = newSym(skField, getIdent"fn", owner, n.info) + var field = newSym(skField, getIdent"fn", owner, n.info, g.config.options) field.typ = argType objType.addField(field) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0]) fn = indirectAccess(castExpr, field, n.info) elif fn.kind == nkSym and fn.sym.kind == skIterator: - localError(n.info, "iterator in spawn environment is not allowed") + localError(g.config, n.info, "iterator in spawn environment is not allowed") elif fn.typ.callConv == ccClosure: - localError(n.info, "closure in spawn environment is not allowed") + localError(g.config, n.info, "closure in spawn environment is not allowed") call.add(fn) var varSection = newNodeI(nkVarSection, n.info) var varInit = newNodeI(nkStmtList, n.info) if barrier.isNil: - setupArgsForConcurrency(n, objType, scratchObj, castExpr, call, + setupArgsForConcurrency(g, n, objType, scratchObj, castExpr, call, varSection, varInit, result) else: - setupArgsForParallelism(n, objType, scratchObj, castExpr, call, + setupArgsForParallelism(g, n, objType, scratchObj, castExpr, call, varSection, varInit, result) var barrierAsExpr: PNode = nil if barrier != nil: let typ = newType(tyPtr, owner) - typ.rawAddSon(magicsys.getCompilerProc("Barrier").typ) - var field = newSym(skField, getIdent"barrier", owner, n.info) + typ.rawAddSon(magicsys.getCompilerProc(g, "Barrier").typ) + var field = newSym(skField, getIdent"barrier", owner, n.info, g.config.options) field.typ = typ objType.addField(field) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier) @@ -681,7 +685,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType; var fvField, fvAsExpr: PNode = nil if spawnKind == srFlowVar: - var field = newSym(skField, getIdent"fv", owner, n.info) + var field = newSym(skField, getIdent"fv", owner, n.info, g.config.options) field.typ = retType objType.addField(field) fvField = newDotExpr(scratchObj, field) @@ -689,20 +693,20 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType; # create flowVar: result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1])) if barrier == nil: - result.add callCodegenProc("nimFlowVarCreateSemaphore", fvField) + result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField) elif spawnKind == srByVar: - var field = newSym(skField, getIdent"fv", owner, n.info) + var field = newSym(skField, getIdent"fv", owner, n.info, g.config.options) field.typ = newType(tyPtr, objType.owner) field.typ.rawAddSon(retType) objType.addField(field) fvAsExpr = indirectAccess(castExpr, field, n.info) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest)) - let wrapper = createWrapperProc(fn, threadParam, argsParam, + let wrapper = createWrapperProc(g, fn, threadParam, argsParam, varSection, varInit, call, barrierAsExpr, fvAsExpr, spawnKind) - result.add callCodegenProc("nimSpawn" & $spawnExpr.len, wrapper.newSymNode, + result.add callCodegenProc(g, "nimSpawn" & $spawnExpr.len, wrapper.newSymNode, genAddrOf(scratchObj.newSymNode), nil, spawnExpr) if spawnKind == srFlowVar: result.add fvField diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index 6a9d69082..b5577d961 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -10,63 +10,62 @@ # Built-in types and compilerprocs are registered here. import - ast, astalgo, hashes, msgs, platform, nversion, times, idents, rodread + ast, astalgo, hashes, msgs, platform, nversion, times, idents, rodread, + modulegraphs -var systemModule*: PSym +export createMagic -var - gSysTypes: array[TTypeKind, PType] - compilerprocs: TStrTable - exposed: TStrTable +proc nilOrSysInt*(g: ModuleGraph): PType = g.sysTypes[tyInt] -proc nilOrSysInt*: PType = gSysTypes[tyInt] +proc registerSysType*(g: ModuleGraph; t: PType) = + if g.sysTypes[t.kind] == nil: g.sysTypes[t.kind] = t -proc registerSysType*(t: PType) = - if gSysTypes[t.kind] == nil: gSysTypes[t.kind] = t - -proc newSysType(kind: TTypeKind, size: int): PType = - result = newType(kind, systemModule) +proc newSysType(g: ModuleGraph; kind: TTypeKind, size: int): PType = + result = newType(kind, g.systemModule) result.size = size result.align = size.int16 -proc getSysSym*(name: string): PSym = - result = strTableGet(systemModule.tab, getIdent(name)) +proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym = + result = strTableGet(g.systemModule.tab, getIdent(name)) if result == nil: - rawMessage(errSystemNeeds, name) - result = newSym(skError, getIdent(name), systemModule, systemModule.info) - result.typ = newType(tyError, systemModule) + localError(g.config, info, "system module needs: " & name) + result = newSym(skError, getIdent(name), g.systemModule, g.systemModule.info, {}) + result.typ = newType(tyError, g.systemModule) if result.kind == skStub: loadStub(result) if result.kind == skAlias: result = result.owner -proc createMagic*(name: string, m: TMagic): PSym = - result = newSym(skProc, getIdent(name), nil, unknownLineInfo()) - result.magic = m +when false: + proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym = + result = newSym(skProc, getIdent(name), nil, unknownLineInfo()) + result.magic = m -let - opNot* = createMagic("not", mNot) - opContains* = createMagic("contains", mInSet) +when false: + let + opNot* = createMagic("not", mNot) + opContains* = createMagic("contains", mInSet) -proc getSysMagic*(name: string, m: TMagic): PSym = +proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSym = var ti: TIdentIter let id = getIdent(name) - var r = initIdentIter(ti, systemModule.tab, id) + var r = initIdentIter(ti, g.systemModule.tab, id) while r != nil: if r.kind == skStub: loadStub(r) if r.magic == m: # prefer the tyInt variant: if r.typ.sons[0] != nil and r.typ.sons[0].kind == tyInt: return r result = r - r = nextIdentIter(ti, systemModule.tab) + r = nextIdentIter(ti, g.systemModule.tab) if result != nil: return result - rawMessage(errSystemNeeds, name) - result = newSym(skError, id, systemModule, systemModule.info) - result.typ = newType(tyError, systemModule) + localError(g.config, info, "system module needs: " & name) + result = newSym(skError, id, g.systemModule, g.systemModule.info, {}) + result.typ = newType(tyError, g.systemModule) -proc sysTypeFromName*(name: string): PType = - result = getSysSym(name).typ +proc sysTypeFromName*(g: ModuleGraph; info: TLineInfo; name: string): PType = + result = getSysSym(g, info, name).typ -proc getSysType*(kind: TTypeKind): PType = - result = gSysTypes[kind] +proc getSysType*(g: ModuleGraph; info: TLineInfo; kind: TTypeKind): PType = + template sysTypeFromName(s: string): untyped = sysTypeFromName(g, info, s) + result = g.sysTypes[kind] if result == nil: case kind of tyInt: result = sysTypeFromName("int") @@ -88,51 +87,49 @@ proc getSysType*(kind: TTypeKind): PType = of tyString: result = sysTypeFromName("string") of tyCString: result = sysTypeFromName("cstring") of tyPointer: result = sysTypeFromName("pointer") - of tyNil: result = newSysType(tyNil, ptrSize) - else: internalError("request for typekind: " & $kind) - gSysTypes[kind] = result + of tyNil: result = newSysType(g, tyNil, ptrSize) + else: internalError(g.config, "request for typekind: " & $kind) + g.sysTypes[kind] = result if result.kind != kind: - internalError("wanted: " & $kind & " got: " & $result.kind) - if result == nil: internalError("type not found: " & $kind) - -var - intTypeCache: array[-5..64, PType] + internalError(g.config, "wanted: " & $kind & " got: " & $result.kind) + if result == nil: internalError(g.config, "type not found: " & $kind) -proc resetSysTypes* = - systemModule = nil - initStrTable(compilerprocs) - initStrTable(exposed) - for i in low(gSysTypes)..high(gSysTypes): - gSysTypes[i] = nil +proc resetSysTypes*(g: ModuleGraph) = + g.systemModule = nil + initStrTable(g.compilerprocs) + initStrTable(g.exposed) + for i in low(g.sysTypes)..high(g.sysTypes): + g.sysTypes[i] = nil - for i in low(intTypeCache)..high(intTypeCache): - intTypeCache[i] = nil + for i in low(g.intTypeCache)..high(g.intTypeCache): + g.intTypeCache[i] = nil -proc getIntLitType*(literal: PNode): PType = +proc getIntLitType*(g: ModuleGraph; literal: PNode): PType = # we cache some common integer literal types for performance: let value = literal.intVal - if value >= low(intTypeCache) and value <= high(intTypeCache): - result = intTypeCache[value.int] + if value >= low(g.intTypeCache) and value <= high(g.intTypeCache): + result = g.intTypeCache[value.int] if result == nil: - let ti = getSysType(tyInt) + let ti = getSysType(g, literal.info, tyInt) result = copyType(ti, ti.owner, false) result.n = literal - intTypeCache[value.int] = result + g.intTypeCache[value.int] = result else: - let ti = getSysType(tyInt) + let ti = getSysType(g, literal.info, tyInt) result = copyType(ti, ti.owner, false) result.n = literal -proc getFloatLitType*(literal: PNode): PType = +proc getFloatLitType*(g: ModuleGraph; literal: PNode): PType = # for now we do not cache these: - result = newSysType(tyFloat, size=8) + result = newSysType(g, tyFloat, size=8) result.n = literal proc skipIntLit*(t: PType): PType {.inline.} = - if t.n != nil: - if t.kind in {tyInt, tyFloat}: - return getSysType(t.kind) - result = t + if t.n != nil and t.kind in {tyInt, tyFloat}: + result = copyType(t, t.owner, false) + result.n = nil + else: + result = t proc addSonSkipIntLit*(father, son: PType) = if isNil(father.sons): father.sons = @[] @@ -140,60 +137,59 @@ proc addSonSkipIntLit*(father, son: PType) = add(father.sons, s) propagateToOwner(father, s) -proc setIntLitType*(result: PNode) = +proc setIntLitType*(g: ModuleGraph; result: PNode) = let i = result.intVal case platform.intSize - of 8: result.typ = getIntLitType(result) + of 8: result.typ = getIntLitType(g, result) of 4: if i >= low(int32) and i <= high(int32): - result.typ = getIntLitType(result) + result.typ = getIntLitType(g, result) else: - result.typ = getSysType(tyInt64) + result.typ = getSysType(g, result.info, tyInt64) of 2: if i >= low(int16) and i <= high(int16): - result.typ = getIntLitType(result) + result.typ = getIntLitType(g, result) elif i >= low(int32) and i <= high(int32): - result.typ = getSysType(tyInt32) + result.typ = getSysType(g, result.info, tyInt32) else: - result.typ = getSysType(tyInt64) + result.typ = getSysType(g, result.info, tyInt64) of 1: # 8 bit CPUs are insane ... if i >= low(int8) and i <= high(int8): - result.typ = getIntLitType(result) + result.typ = getIntLitType(g, result) elif i >= low(int16) and i <= high(int16): - result.typ = getSysType(tyInt16) + result.typ = getSysType(g, result.info, tyInt16) elif i >= low(int32) and i <= high(int32): - result.typ = getSysType(tyInt32) + result.typ = getSysType(g, result.info, tyInt32) else: - result.typ = getSysType(tyInt64) - else: internalError(result.info, "invalid int size") + result.typ = getSysType(g, result.info, tyInt64) + else: + internalError(g.config, result.info, "invalid int size") -proc getCompilerProc*(name: string): PSym = +proc getCompilerProc*(g: ModuleGraph; name: string): PSym = let ident = getIdent(name) - result = strTableGet(compilerprocs, ident) - if result == nil: - result = strTableGet(rodCompilerprocs, ident) - if result != nil: - strTableAdd(compilerprocs, result) - if result.kind == skStub: loadStub(result) - if result.kind == skAlias: result = result.owner + result = strTableGet(g.compilerprocs, ident) + when false: + if result == nil: + result = strTableGet(g.rodCompilerprocs, ident) + if result != nil: + strTableAdd(g.compilerprocs, result) + if result.kind == skStub: loadStub(result) + if result.kind == skAlias: result = result.owner -proc registerCompilerProc*(s: PSym) = - strTableAdd(compilerprocs, s) +proc registerCompilerProc*(g: ModuleGraph; s: PSym) = + strTableAdd(g.compilerprocs, s) -proc registerNimScriptSymbol*(s: PSym) = +proc registerNimScriptSymbol*(g: ModuleGraph; s: PSym) = # Nimscript symbols must be al unique: - let conflict = strTableGet(exposed, s.name) + let conflict = strTableGet(g.exposed, s.name) if conflict == nil: - strTableAdd(exposed, s) + strTableAdd(g.exposed, s) else: - localError(s.info, "symbol conflicts with other .exportNims symbol at: " & - $conflict.info) - -proc getNimScriptSymbol*(name: string): PSym = - strTableGet(exposed, getIdent(name)) + localError(g.config, s.info, + "symbol conflicts with other .exportNims symbol at: " & $conflict.info) -proc resetNimScriptSymbols*() = initStrTable(exposed) +proc getNimScriptSymbol*(g: ModuleGraph; name: string): PSym = + strTableGet(g.exposed, getIdent(name)) -initStrTable(compilerprocs) -initStrTable(exposed) +proc resetNimScriptSymbols*(g: ModuleGraph) = initStrTable(g.exposed) diff --git a/compiler/main.nim b/compiler/main.nim index 9bf8bb7c0..e3f00db9e 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -16,12 +16,12 @@ import cgen, jsgen, json, nversion, platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen, docgen2, service, parser, modules, ccgutils, sigmatch, ropes, - modulegraphs, tables + modulegraphs, tables, rod, configuration -from magicsys import systemModule, resetSysTypes +from magicsys import resetSysTypes -proc rodPass = - if gSymbolFiles in {enabledSf, writeOnlySf}: +proc rodPass(g: ModuleGraph) = + if g.config.symbolFiles in {enabledSf, writeOnlySf}: registerPass(rodwritePass) proc codegenPass = @@ -35,7 +35,7 @@ proc writeDepsFile(g: ModuleGraph; project: string) = let f = open(changeFileExt(project, "deps"), fmWrite) for m in g.modules: if m != nil: - f.writeLine(toFullPath(m.position.int32)) + f.writeLine(toFullPath(m.position.FileIndex)) for k in g.inclToMod.keys: if g.getModule(k).isNil: # don't repeat includes which are also modules f.writeLine(k.toFullPath) @@ -46,55 +46,55 @@ proc commandGenDepend(graph: ModuleGraph; cache: IdentCache) = registerPass(gendependPass) #registerPass(cleanupPass) compileProject(graph, cache) - writeDepsFile(graph, gProjectFull) - generateDot(gProjectFull) - execExternalProgram("dot -Tpng -o" & changeFileExt(gProjectFull, "png") & - ' ' & changeFileExt(gProjectFull, "dot")) + let project = graph.config.projectFull + writeDepsFile(graph, project) + generateDot(project) + execExternalProgram(graph.config, "dot -Tpng -o" & changeFileExt(project, "png") & + ' ' & changeFileExt(project, "dot")) proc commandCheck(graph: ModuleGraph; cache: IdentCache) = - msgs.gErrorMax = high(int) # do not stop after first error - defineSymbol("nimcheck") + graph.config.errorMax = high(int) # do not stop after first error + defineSymbol(graph.config.symbols, "nimcheck") semanticPasses() # use an empty backend for semantic checking only - rodPass() + rodPass(graph) compileProject(graph, cache) proc commandDoc2(graph: ModuleGraph; cache: IdentCache; json: bool) = - msgs.gErrorMax = high(int) # do not stop after first error + graph.config.errorMax = high(int) # do not stop after first error semanticPasses() if json: registerPass(docgen2JsonPass) else: registerPass(docgen2Pass) #registerPass(cleanupPass()) compileProject(graph, cache) - finishDoc2Pass(gProjectName) + finishDoc2Pass(graph.config.projectName) proc commandCompileToC(graph: ModuleGraph; cache: IdentCache) = - extccomp.initVars() + let conf = graph.config + extccomp.initVars(conf) semanticPasses() registerPass(cgenPass) - rodPass() + rodPass(graph) #registerPass(cleanupPass()) compileProject(graph, cache) - cgenWriteModules(graph.backend, graph.config) - if gCmd != cmdRun: - let proj = changeFileExt(gProjectFull, "") - extccomp.callCCompiler(proj) - extccomp.writeJsonBuildInstructions(proj) - if optGenScript in gGlobalOptions: - writeDepsFile(graph, toGeneratedFile(proj, "")) + cgenWriteModules(graph.backend, conf) + if conf.cmd != cmdRun: + let proj = changeFileExt(conf.projectFull, "") + extccomp.callCCompiler(conf, proj) + extccomp.writeJsonBuildInstructions(conf, proj) + if optGenScript in graph.config.globalOptions: + writeDepsFile(graph, toGeneratedFile(conf, proj, "")) proc commandJsonScript(graph: ModuleGraph; cache: IdentCache) = - let proj = changeFileExt(gProjectFull, "") - extccomp.runJsonBuildInstructions(proj) + let proj = changeFileExt(graph.config.projectFull, "") + extccomp.runJsonBuildInstructions(graph.config, proj) proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) = #incl(gGlobalOptions, optSafeCode) setTarget(osJS, cpuJS) #initDefines() - defineSymbol("nimrod") # 'nimrod' is always defined - defineSymbol("ecmascript") # For backward compatibility - defineSymbol("js") - if gCmd == cmdCompileToPHP: defineSymbol("nimphp") + defineSymbol(graph.config.symbols, "ecmascript") # For backward compatibility + defineSymbol(graph.config.symbols, "js") semanticPasses() registerPass(JSgenPass) compileProject(graph, cache) @@ -102,19 +102,19 @@ proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) = proc interactivePasses(graph: ModuleGraph; cache: IdentCache) = #incl(gGlobalOptions, optSafeCode) #setTarget(osNimrodVM, cpuNimrodVM) - initDefines() - defineSymbol("nimscript") - when hasFFI: defineSymbol("nimffi") + initDefines(graph.config.symbols) + defineSymbol(graph.config.symbols, "nimscript") + when hasFFI: defineSymbol(graph.config.symbols, "nimffi") registerPass(verbosePass) registerPass(semPass) registerPass(evalPass) proc commandInteractive(graph: ModuleGraph; cache: IdentCache) = - msgs.gErrorMax = high(int) # do not stop after first error + graph.config.errorMax = high(int) # do not stop after first error interactivePasses(graph, cache) compileSystemModule(graph, cache) - if commandArgs.len > 0: - discard graph.compileModule(fileInfoIdx(gProjectFull), cache, {}) + if graph.config.commandArgs.len > 0: + discard graph.compileModule(fileInfoIdx(graph.config, graph.config.projectFull), cache, {}) else: var m = graph.makeStdinModule() incl(m.flags, sfMainModule) @@ -126,178 +126,171 @@ proc evalNim(graph: ModuleGraph; nodes: PNode, module: PSym; cache: IdentCache) carryPasses(graph, nodes, module, cache, evalPasses) proc commandEval(graph: ModuleGraph; cache: IdentCache; exp: string) = - if systemModule == nil: + if graph.systemModule == nil: interactivePasses(graph, cache) compileSystemModule(graph, cache) let echoExp = "echo \"eval\\t\", " & "repr(" & exp & ")" - evalNim(graph, echoExp.parseString(cache), makeStdinModule(graph), cache) + evalNim(graph, echoExp.parseString(cache, graph.config), + makeStdinModule(graph), cache) -proc commandScan(cache: IdentCache) = - var f = addFileExt(mainCommandArg(), NimExt) +proc commandScan(cache: IdentCache, config: ConfigRef) = + var f = addFileExt(mainCommandArg(config), NimExt) var stream = llStreamOpen(f, fmRead) if stream != nil: var L: TLexer tok: TToken initToken(tok) - openLexer(L, f, stream, cache) + openLexer(L, f, stream, cache, config) while true: rawGetTok(L, tok) - printTok(tok) + printTok(config, tok) if tok.tokType == tkEof: break closeLexer(L) else: - rawMessage(errCannotOpenFile, f) + rawMessage(config, errGenerated, "cannot open file: " & f) const - SimulateCaasMemReset = false PrintRopeCacheStats = false proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = - when SimulateCaasMemReset: - gGlobalOptions.incl(optCaasEnabled) + let conf = graph.config + setupModuleCache() # In "nim serve" scenario, each command must reset the registered passes clearPasses() - gLastCmdTime = epochTime() - searchPaths.add(options.libpath) - when false: # gProjectFull.len != 0: - # current path is always looked first for modules - prependStr(searchPaths, gProjectPath) + conf.lastCmdTime = epochTime() + conf.searchPaths.add(conf.libpath) setId(100) - case command.normalize + case conf.command.normalize of "c", "cc", "compile", "compiletoc": # compile means compileToC currently - gCmd = cmdCompileToC + conf.cmd = cmdCompileToC commandCompileToC(graph, cache) of "cpp", "compiletocpp": - gCmd = cmdCompileToCpp - defineSymbol("cpp") + conf.cmd = cmdCompileToCpp + defineSymbol(graph.config.symbols, "cpp") commandCompileToC(graph, cache) of "objc", "compiletooc": - gCmd = cmdCompileToOC - defineSymbol("objc") + conf.cmd = cmdCompileToOC + defineSymbol(graph.config.symbols, "objc") commandCompileToC(graph, cache) of "run": - gCmd = cmdRun + conf.cmd = cmdRun when hasTinyCBackend: extccomp.setCC("tcc") commandCompileToC(graph, cache) else: - rawMessage(errInvalidCommandX, command) + rawMessage(conf, errGenerated, "'run' command not available; rebuild with -d:tinyc") of "js", "compiletojs": - gCmd = cmdCompileToJS - commandCompileToJS(graph, cache) - of "php": - gCmd = cmdCompileToPHP + conf.cmd = cmdCompileToJS commandCompileToJS(graph, cache) of "doc0": - wantMainModule() - gCmd = cmdDoc - loadConfigs(DocConfig, cache) - commandDoc() + wantMainModule(conf) + conf.cmd = cmdDoc + loadConfigs(DocConfig, cache, conf) + commandDoc(conf) of "doc2", "doc": - gCmd = cmdDoc - loadConfigs(DocConfig, cache) - defineSymbol("nimdoc") + conf.cmd = cmdDoc + loadConfigs(DocConfig, cache, conf) + defineSymbol(conf.symbols, "nimdoc") commandDoc2(graph, cache, false) of "rst2html": - gCmd = cmdRst2html - loadConfigs(DocConfig, cache) - commandRst2Html() + conf.cmd = cmdRst2html + loadConfigs(DocConfig, cache, conf) + commandRst2Html(conf) of "rst2tex": - gCmd = cmdRst2tex - loadConfigs(DocTexConfig, cache) - commandRst2TeX() - of "jsondoc": - wantMainModule() - gCmd = cmdDoc - loadConfigs(DocConfig, cache) - wantMainModule() - defineSymbol("nimdoc") - commandJson() - of "jsondoc2": - gCmd = cmdDoc - loadConfigs(DocConfig, cache) - wantMainModule() - defineSymbol("nimdoc") + conf.cmd = cmdRst2tex + loadConfigs(DocTexConfig, cache, conf) + commandRst2TeX(conf) + of "jsondoc0": + wantMainModule(conf) + conf.cmd = cmdDoc + loadConfigs(DocConfig, cache, conf) + wantMainModule(conf) + defineSymbol(conf.symbols, "nimdoc") + commandJson(conf) + of "jsondoc2", "jsondoc": + conf.cmd = cmdDoc + loadConfigs(DocConfig, cache, conf) + wantMainModule(conf) + defineSymbol(conf.symbols, "nimdoc") commandDoc2(graph, cache, true) of "ctags": - wantMainModule() - gCmd = cmdDoc - loadConfigs(DocConfig, cache) - wantMainModule() - defineSymbol("nimdoc") - commandTags() + wantMainModule(conf) + conf.cmd = cmdDoc + loadConfigs(DocConfig, cache, conf) + defineSymbol(conf.symbols, "nimdoc") + commandTags(conf) of "buildindex": - gCmd = cmdDoc - loadConfigs(DocConfig, cache) - commandBuildIndex() + conf.cmd = cmdDoc + loadConfigs(DocConfig, cache, conf) + commandBuildIndex(conf) of "gendepend": - gCmd = cmdGenDepend + conf.cmd = cmdGenDepend commandGenDepend(graph, cache) of "dump": - gCmd = cmdDump - if getConfigVar("dump.format") == "json": - wantMainModule() + conf.cmd = cmdDump + if getConfigVar(conf, "dump.format") == "json": + wantMainModule(conf) var definedSymbols = newJArray() - for s in definedSymbolNames(): definedSymbols.elems.add(%s) + for s in definedSymbolNames(conf.symbols): definedSymbols.elems.add(%s) var libpaths = newJArray() - for dir in searchPaths: libpaths.elems.add(%dir) + for dir in conf.searchPaths: libpaths.elems.add(%dir) var dumpdata = % [ (key: "version", val: %VersionAsString), - (key: "project_path", val: %gProjectFull), + (key: "project_path", val: %conf.projectFull), (key: "defined_symbols", val: definedSymbols), (key: "lib_paths", val: libpaths) ] - msgWriteln($dumpdata, {msgStdout, msgSkipHook}) + msgWriteln(conf, $dumpdata, {msgStdout, msgSkipHook}) else: - msgWriteln("-- list of currently defined symbols --", + msgWriteln(conf, "-- list of currently defined symbols --", {msgStdout, msgSkipHook}) - for s in definedSymbolNames(): msgWriteln(s, {msgStdout, msgSkipHook}) - msgWriteln("-- end of list --", {msgStdout, msgSkipHook}) + for s in definedSymbolNames(conf.symbols): msgWriteln(conf, s, {msgStdout, msgSkipHook}) + msgWriteln(conf, "-- end of list --", {msgStdout, msgSkipHook}) - for it in searchPaths: msgWriteln(it) + for it in conf.searchPaths: msgWriteln(conf, it) of "check": - gCmd = cmdCheck + conf.cmd = cmdCheck commandCheck(graph, cache) of "parse": - gCmd = cmdParse - wantMainModule() - discard parseFile(gProjectMainIdx, cache) + conf.cmd = cmdParse + wantMainModule(conf) + discard parseFile(FileIndex conf.projectMainIdx, cache, conf) of "scan": - gCmd = cmdScan - wantMainModule() - commandScan(cache) - msgWriteln("Beware: Indentation tokens depend on the parser's state!") + conf.cmd = cmdScan + wantMainModule(conf) + commandScan(cache, conf) + msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!") of "secret": - gCmd = cmdInteractive + conf.cmd = cmdInteractive commandInteractive(graph, cache) of "e": - commandEval(graph, cache, mainCommandArg()) + commandEval(graph, cache, mainCommandArg(conf)) of "nop", "help": # prevent the "success" message: - gCmd = cmdDump + conf.cmd = cmdDump of "jsonscript": - gCmd = cmdJsonScript + conf.cmd = cmdJsonScript commandJsonScript(graph, cache) else: - rawMessage(errInvalidCommandX, command) + rawMessage(conf, errGenerated, "invalid command: " & conf.command) - if msgs.gErrorCounter == 0 and - gCmd notin {cmdInterpret, cmdRun, cmdDump}: + if conf.errorCounter == 0 and + conf.cmd notin {cmdInterpret, cmdRun, cmdDump}: when declared(system.getMaxMem): let usedMem = formatSize(getMaxMem()) & " peakmem" else: let usedMem = formatSize(getTotalMem()) - rawMessage(hintSuccessX, [$gLinesCompiled, - formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3), + rawMessage(conf, hintSuccessX, [$conf.linesCompiled, + formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3), usedMem, - if condSyms.isDefined("release"): "Release Build" + if isDefined(conf, "release"): "Release Build" else: "Debug Build"]) when PrintRopeCacheStats: @@ -308,9 +301,6 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = echo " efficiency: ", formatFloat(1-(gCacheMisses.float/gCacheTries.float), ffDecimal, 3) - when SimulateCaasMemReset: - resetMemory() - - resetAttributes() + resetAttributes(conf) -proc mainCommand*() = mainCommand(newModuleGraph(newConfigRef()), newIdentCache()) +#proc mainCommand*() = mainCommand(newModuleGraph(newConfigRef()), newIdentCache()) diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index c081a099a..460d0b4a5 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -25,7 +25,7 @@ ## - Its dependent module stays the same. ## -import ast, intsets, tables, options +import ast, intsets, tables, options, rod, msgs, hashes, idents type ModuleGraph* = ref object @@ -34,67 +34,88 @@ type deps*: IntSet # the dependency graph or potentially its transitive closure. suggestMode*: bool # whether we are in nimsuggest mode or not. invalidTransitiveClosure: bool - inclToMod*: Table[int32, int32] # mapping of include file to the - # first module that included it - importStack*: seq[int32] # The current import stack. Used for detecting recursive - # module dependencies. + inclToMod*: Table[FileIndex, FileIndex] # mapping of include file to the + # first module that included it + importStack*: seq[FileIndex] # The current import stack. Used for detecting recursive + # module dependencies. backend*: RootRef # minor hack so that a backend can extend this easily config*: ConfigRef doStopCompile*: proc(): bool {.closure.} usageSym*: PSym # for nimsuggest owners*: seq[PSym] methods*: seq[tuple[methods: TSymSeq, dispatcher: PSym]] + systemModule*: PSym + sysTypes*: array[TTypeKind, PType] + compilerprocs*: TStrTable + exposed*: TStrTable + intTypeCache*: array[-5..64, PType] + opContains*, opNot*: PSym + +proc hash*(x: FileIndex): Hash {.borrow.} {.this: g.} proc stopCompile*(g: ModuleGraph): bool {.inline.} = result = doStopCompile != nil and doStopCompile() +proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym = + result = newSym(skProc, getIdent(name), nil, unknownLineInfo(), {}) + result.magic = m + proc newModuleGraph*(config: ConfigRef = nil): ModuleGraph = result = ModuleGraph() initStrTable(result.packageSyms) result.deps = initIntSet() result.modules = @[] result.importStack = @[] - result.inclToMod = initTable[int32, int32]() + result.inclToMod = initTable[FileIndex, FileIndex]() if config.isNil: result.config = newConfigRef() else: result.config = config result.owners = @[] result.methods = @[] + initStrTable(result.compilerprocs) + initStrTable(result.exposed) + result.opNot = createMagic(result, "not", mNot) + result.opContains = createMagic(result, "contains", mInSet) proc resetAllModules*(g: ModuleGraph) = initStrTable(packageSyms) deps = initIntSet() modules = @[] importStack = @[] - inclToMod = initTable[int32, int32]() + inclToMod = initTable[FileIndex, FileIndex]() usageSym = nil owners = @[] methods = @[] + initStrTable(compilerprocs) + initStrTable(exposed) -proc getModule*(g: ModuleGraph; fileIdx: int32): PSym = - if fileIdx >= 0 and fileIdx < modules.len: - result = modules[fileIdx] +proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym = + if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len: + result = modules[fileIdx.int32] proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b -proc addDep*(g: ModuleGraph; m: PSym, dep: int32) = +proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) = + assert m.position == m.info.fileIndex.int32 + addModuleDep(m.info.fileIndex, dep, isIncludeFile = false) if suggestMode: - deps.incl m.position.dependsOn(dep) + deps.incl m.position.dependsOn(dep.int) # we compute the transitive closure later when quering the graph lazily. # this improve efficiency quite a lot: #invalidTransitiveClosure = true -proc addIncludeDep*(g: ModuleGraph; module, includeFile: int32) = +proc addIncludeDep*(g: ModuleGraph; module, includeFile: FileIndex) = + addModuleDep(module, includeFile, isIncludeFile = true) discard hasKeyOrPut(inclToMod, includeFile, module) -proc parentModule*(g: ModuleGraph; fileIdx: int32): int32 = +proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex = ## returns 'fileIdx' if the file belonging to this index is ## directly used as a module or else the module that first ## references this include file. - if fileIdx >= 0 and fileIdx < modules.len and modules[fileIdx] != nil: + if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len and modules[fileIdx.int32] != nil: result = fileIdx else: result = inclToMod.getOrDefault(fileIdx) @@ -108,11 +129,11 @@ proc transitiveClosure(g: var IntSet; n: int) = if g.contains(i.dependsOn(k)) and g.contains(k.dependsOn(j)): g.incl i.dependsOn(j) -proc markDirty*(g: ModuleGraph; fileIdx: int32) = +proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) = let m = getModule fileIdx if m != nil: incl m.flags, sfDirty -proc markClientsDirty*(g: ModuleGraph; fileIdx: int32) = +proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) = # we need to mark its dependent modules D as dirty right away because after # nimsuggest is done with this module, the module's dirty flag will be # cleared but D still needs to be remembered as 'dirty'. @@ -123,7 +144,7 @@ proc markClientsDirty*(g: ModuleGraph; fileIdx: int32) = # every module that *depends* on this file is also dirty: for i in 0i32..<modules.len.int32: let m = modules[i] - if m != nil and deps.contains(i.dependsOn(fileIdx)): + if m != nil and deps.contains(i.dependsOn(fileIdx.int)): incl m.flags, sfDirty proc isDirty*(g: ModuleGraph; m: PSym): bool = diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim index 5d112c6b9..daa55c2ba 100644 --- a/compiler/modulepaths.nim +++ b/compiler/modulepaths.nim @@ -11,38 +11,39 @@ import ast, renderer, strutils, msgs, options, idents, os import nimblecmd -const - considerParentDirs = not defined(noParentProjects) - considerNimbleDirs = not defined(noNimbleDirs) +when false: + const + considerParentDirs = not defined(noParentProjects) + considerNimbleDirs = not defined(noNimbleDirs) -proc findInNimbleDir(pkg, subdir, dir: string): string = - var best = "" - var bestv = "" - for k, p in os.walkDir(dir, relative=true): - if k == pcDir and p.len > pkg.len+1 and - p[pkg.len] == '-' and p.startsWith(pkg): - let (_, a) = getPathVersion(p) - if bestv.len == 0 or bestv < a: - bestv = a - best = dir / p + proc findInNimbleDir(pkg, subdir, dir: string): string = + var best = "" + var bestv = "" + for k, p in os.walkDir(dir, relative=true): + if k == pcDir and p.len > pkg.len+1 and + p[pkg.len] == '-' and p.startsWith(pkg): + let (_, a) = getPathVersion(p) + if bestv.len == 0 or bestv < a: + bestv = a + best = dir / p - if best.len > 0: - var f: File - if open(f, best / changeFileExt(pkg, ".nimble-link")): - # the second line contains what we're interested in, see: - # https://github.com/nim-lang/nimble#nimble-link - var override = "" - discard readLine(f, override) - discard readLine(f, override) - close(f) - if not override.isAbsolute(): - best = best / override - else: - best = override - let f = if subdir.len == 0: pkg else: subdir - let res = addFileExt(best / f, "nim") - if best.len > 0 and fileExists(res): - result = res + if best.len > 0: + var f: File + if open(f, best / changeFileExt(pkg, ".nimble-link")): + # the second line contains what we're interested in, see: + # https://github.com/nim-lang/nimble#nimble-link + var override = "" + discard readLine(f, override) + discard readLine(f, override) + close(f) + if not override.isAbsolute(): + best = best / override + else: + best = override + let f = if subdir.len == 0: pkg else: subdir + let res = addFileExt(best / f, "nim") + if best.len > 0 and fileExists(res): + result = res const stdlibDirs = [ "pure", "core", "arch", @@ -51,76 +52,77 @@ const stdlibDirs = [ "wrappers", "wrappers/linenoise", "windows", "posix", "js"] -proc resolveDollar(project, source, pkg, subdir: string; info: TLineInfo): string = - template attempt(a) = - let x = addFileExt(a, "nim") - if fileExists(x): return x +when false: + proc resolveDollar(project, source, pkg, subdir: string; info: TLineInfo): string = + template attempt(a) = + let x = addFileExt(a, "nim") + if fileExists(x): return x - case pkg - of "stdlib": - if subdir.len == 0: - return options.libpath - else: - for candidate in stdlibDirs: - attempt(options.libpath / candidate / subdir) - of "root": - let root = project.splitFile.dir - if subdir.len == 0: - return root + case pkg + of "stdlib": + if subdir.len == 0: + return options.libpath + else: + for candidate in stdlibDirs: + attempt(options.libpath / candidate / subdir) + of "root": + let root = project.splitFile.dir + if subdir.len == 0: + return root + else: + attempt(root / subdir) else: - attempt(root / subdir) - else: - when considerParentDirs: - var p = parentDir(source.splitFile.dir) - # support 'import $karax': - let f = if subdir.len == 0: pkg else: subdir + when considerParentDirs: + var p = parentDir(source.splitFile.dir) + # support 'import $karax': + let f = if subdir.len == 0: pkg else: subdir - while p.len > 0: - let dir = p / pkg - if dirExists(dir): - attempt(dir / f) - # 2nd attempt: try to use 'karax/karax' - attempt(dir / pkg / f) - # 3rd attempt: try to use 'karax/src/karax' - attempt(dir / "src" / f) - attempt(dir / "src" / pkg / f) - p = parentDir(p) + while p.len > 0: + let dir = p / pkg + if dirExists(dir): + attempt(dir / f) + # 2nd attempt: try to use 'karax/karax' + attempt(dir / pkg / f) + # 3rd attempt: try to use 'karax/src/karax' + attempt(dir / "src" / f) + attempt(dir / "src" / pkg / f) + p = parentDir(p) - when considerNimbleDirs: - if not options.gNoNimblePath: - var nimbleDir = getEnv("NIMBLE_DIR") - if nimbleDir.len == 0: nimbleDir = getHomeDir() / ".nimble" - result = findInNimbleDir(pkg, subdir, nimbleDir / "pkgs") - if result.len > 0: return result - when not defined(windows): - result = findInNimbleDir(pkg, subdir, "/opt/nimble/pkgs") + when considerNimbleDirs: + if not options.gNoNimblePath: + var nimbleDir = getEnv("NIMBLE_DIR") + if nimbleDir.len == 0: nimbleDir = getHomeDir() / ".nimble" + result = findInNimbleDir(pkg, subdir, nimbleDir / "pkgs") if result.len > 0: return result + when not defined(windows): + result = findInNimbleDir(pkg, subdir, "/opt/nimble/pkgs") + if result.len > 0: return result -proc scriptableImport(pkg, sub: string; info: TLineInfo): string = - result = resolveDollar(gProjectFull, info.toFullPath(), pkg, sub, info) - if result.isNil: result = "" + proc scriptableImport(pkg, sub: string; info: TLineInfo): string = + result = resolveDollar(gProjectFull, info.toFullPath(), pkg, sub, info) + if result.isNil: result = "" -proc lookupPackage(pkg, subdir: PNode): string = - let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: "" - case pkg.kind - of nkStrLit, nkRStrLit, nkTripleStrLit: - result = scriptableImport(pkg.strVal, sub, pkg.info) - of nkIdent: - result = scriptableImport(pkg.ident.s, sub, pkg.info) - else: - localError(pkg.info, "package name must be an identifier or string literal") - result = "" + proc lookupPackage(pkg, subdir: PNode): string = + let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: "" + case pkg.kind + of nkStrLit, nkRStrLit, nkTripleStrLit: + result = scriptableImport(pkg.strVal, sub, pkg.info) + of nkIdent: + result = scriptableImport(pkg.ident.s, sub, pkg.info) + else: + localError(pkg.info, "package name must be an identifier or string literal") + result = "" -proc getModuleName*(n: PNode): string = +proc getModuleName*(conf: ConfigRef; n: PNode): string = # This returns a short relative module name without the nim extension # e.g. like "system", "importer" or "somepath/module" # The proc won't perform any checks that the path is actually valid case n.kind of nkStrLit, nkRStrLit, nkTripleStrLit: try: - result = pathSubs(n.strVal, n.info.toFullPath().splitFile().dir) + result = pathSubs(conf, n.strVal, n.info.toFullPath().splitFile().dir) except ValueError: - localError(n.info, "invalid path: " & n.strVal) + localError(conf, n.info, "invalid path: " & n.strVal) result = n.strVal of nkIdent: result = n.ident.s @@ -135,40 +137,51 @@ proc getModuleName*(n: PNode): string = n.sons[0] = n.sons[1] n.sons[1] = n.sons[2] n.sons.setLen(2) - return getModuleName(n.sons[0]) - if n1.kind == nkPrefix and n1[0].kind == nkIdent and n1[0].ident.s == "$": - if n0.kind == nkIdent and n0.ident.s == "/": - result = lookupPackage(n1[1], n[2]) - else: - localError(n.info, "only '/' supported with $package notation") - result = "" + return getModuleName(conf, n.sons[0]) + when false: + if n1.kind == nkPrefix and n1[0].kind == nkIdent and n1[0].ident.s == "$": + if n0.kind == nkIdent and n0.ident.s == "/": + result = lookupPackage(n1[1], n[2]) + else: + localError(n.info, "only '/' supported with $package notation") + result = "" else: + let modname = getModuleName(conf, n[2]) + if $n1 == "std": + template attempt(a) = + let x = addFileExt(a, "nim") + if fileExists(x): return x + for candidate in stdlibDirs: + attempt(conf.libpath / candidate / modname) + # hacky way to implement 'x / y /../ z': - result = getModuleName(n1) + result = getModuleName(conf, n1) result.add renderTree(n0, {renderNoComments}) - result.add getModuleName(n[2]) + result.add modname of nkPrefix: - if n.sons[0].kind == nkIdent and n.sons[0].ident.s == "$": - result = lookupPackage(n[1], nil) - else: - # hacky way to implement 'x / y /../ z': - result = renderTree(n, {renderNoComments}).replace(" ") + when false: + if n.sons[0].kind == nkIdent and n.sons[0].ident.s == "$": + result = lookupPackage(n[1], nil) + else: + discard + # hacky way to implement 'x / y /../ z': + result = renderTree(n, {renderNoComments}).replace(" ") of nkDotExpr: result = renderTree(n, {renderNoComments}).replace(".", "/") of nkImportAs: - result = getModuleName(n.sons[0]) + result = getModuleName(conf, n.sons[0]) else: - localError(n.info, errGenerated, "invalid module name: '$1'" % n.renderTree) + localError(conf, n.info, "invalid module name: '$1'" % n.renderTree) result = "" -proc checkModuleName*(n: PNode; doLocalError=true): int32 = +proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex = # This returns the full canonical path for a given module import - let modulename = n.getModuleName - let fullPath = findModule(modulename, n.info.toFullPath) + let modulename = getModuleName(conf, n) + let fullPath = findModule(conf, modulename, n.info.toFullPath) if fullPath.len == 0: if doLocalError: let m = if modulename.len > 0: modulename else: $n - localError(n.info, errCannotOpenFile, m) + localError(conf, n.info, "cannot open file: " & m) result = InvalidFileIDX else: - result = fullPath.fileInfoIdx + result = fileInfoIdx(conf, fullPath) diff --git a/compiler/modules.nim b/compiler/modules.nim index 4763ac79b..5d1eba1f2 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -10,131 +10,27 @@ ## Implements the module handling, including the caching of modules. import - ast, astalgo, magicsys, securehash, rodread, msgs, cgendata, sigmatch, options, - idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs - -when false: - type - TNeedRecompile* = enum Maybe, No, Yes, Probing, Recompiled - THashStatus* = enum hashNotTaken, hashCached, hashHasChanged, hashNotChanged - - TModuleInMemory* = object - hash*: SecureHash - deps*: seq[int32] ## XXX: slurped files are currently not tracked - - needsRecompile*: TNeedRecompile - hashStatus*: THashStatus - - var - gCompiledModules: seq[PSym] = @[] - gMemCacheData*: seq[TModuleInMemory] = @[] - ## XXX: we should implement recycling of file IDs - ## if the user keeps renaming modules, the file IDs will keep growing - gFuzzyGraphChecking*: bool # nimsuggest uses this. XXX figure out why. - - proc hashChanged(fileIdx: int32): bool = - internalAssert fileIdx >= 0 and fileIdx < gMemCacheData.len - - template updateStatus = - gMemCacheData[fileIdx].hashStatus = if result: hashHasChanged - else: hashNotChanged - # echo "TESTING Hash: ", fileIdx.toFilename, " ", result - - case gMemCacheData[fileIdx].hashStatus - of hashHasChanged: - result = true - of hashNotChanged: - result = false - of hashCached: - let newHash = secureHashFile(fileIdx.toFullPath) - result = newHash != gMemCacheData[fileIdx].hash - gMemCacheData[fileIdx].hash = newHash - updateStatus() - of hashNotTaken: - gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath) - result = true - updateStatus() - - proc doHash(fileIdx: int32) = - if gMemCacheData[fileIdx].hashStatus == hashNotTaken: - # echo "FIRST Hash: ", fileIdx.ToFilename - gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath) - - proc resetModule*(fileIdx: int32) = - # echo "HARD RESETTING ", fileIdx.toFilename - if fileIdx <% gMemCacheData.len: - gMemCacheData[fileIdx].needsRecompile = Yes - if fileIdx <% gCompiledModules.len: - gCompiledModules[fileIdx] = nil - if fileIdx <% cgendata.gModules.len: - cgendata.gModules[fileIdx] = nil - - proc resetModule*(module: PSym) = - let conflict = getModule(module.position.int32) - if conflict == nil: return - doAssert conflict == module - resetModule(module.position.int32) - initStrTable(module.tab) - - proc resetAllModules* = - for i in 0..gCompiledModules.high: - if gCompiledModules[i] != nil: - resetModule(i.int32) - resetPackageCache() - # for m in cgenModules(): echo "CGEN MODULE FOUND" - - proc resetAllModulesHard* = - resetPackageCache() - gCompiledModules.setLen 0 - gMemCacheData.setLen 0 - magicsys.resetSysTypes() - # XXX - #gOwners = @[] - - proc checkDepMem(fileIdx: int32): TNeedRecompile = - template markDirty = - resetModule(fileIdx) - return Yes - - if gFuzzyGraphChecking: - if gMemCacheData[fileIdx].needsRecompile != Maybe: - return gMemCacheData[fileIdx].needsRecompile - else: - # cycle detection: We claim that a cycle does no harm. - if gMemCacheData[fileIdx].needsRecompile == Probing: - return No - - if optForceFullMake in gGlobalOptions or hashChanged(fileIdx): - markDirty() - - if gMemCacheData[fileIdx].deps != nil: - gMemCacheData[fileIdx].needsRecompile = Probing - for dep in gMemCacheData[fileIdx].deps: - let d = checkDepMem(dep) - if d in {Yes, Recompiled}: - # echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d - markDirty() - - gMemCacheData[fileIdx].needsRecompile = No - return No - -proc resetSystemArtifacts*() = - magicsys.resetSysTypes() - -proc newModule(graph: ModuleGraph; fileIdx: int32): PSym = + ast, astalgo, magicsys, std / sha1, rodread, msgs, cgendata, sigmatch, options, + idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs, rod, + configuration + +proc resetSystemArtifacts*(g: ModuleGraph) = + magicsys.resetSysTypes(g) + +proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = # We cannot call ``newSym`` here, because we have to circumvent the ID # mechanism, which we do in order to assign each module a persistent ID. new(result) - result.id = - 1 # for better error checking + result.id = -1 # for better error checking result.kind = skModule let filename = fileIdx.toFullPath result.name = getIdent(splitFile(filename).name) if not isNimIdentifier(result.name.s): - rawMessage(errInvalidModuleName, result.name.s) + rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s) result.info = newLineInfo(fileIdx, 1, 1) let - pck = getPackageName(filename) + pck = getPackageName(graph.config, filename) pck2 = if pck.len > 0: pck else: "unknown" pack = getIdent(pck2) var packSym = graph.packageSyms.strTableGet(pack) @@ -144,9 +40,9 @@ proc newModule(graph: ModuleGraph; fileIdx: int32): PSym = graph.packageSyms.strTableAdd(packSym) result.owner = packSym - result.position = fileIdx + result.position = int fileIdx - growCache graph.modules, fileIdx + growCache graph.modules, int fileIdx graph.modules[result.position] = result incl(result.flags, sfUsed) @@ -154,11 +50,11 @@ proc newModule(graph: ModuleGraph; fileIdx: int32): PSym = strTableAdd(result.tab, result) # a module knows itself let existing = strTableGet(packSym.tab, result.name) if existing != nil and existing.info.fileIndex != result.info.fileIndex: - localError(result.info, "module names need to be unique per Nimble package; module clashes with " & existing.info.fileIndex.toFullPath) + localError(graph.config, result.info, "module names need to be unique per Nimble package; module clashes with " & existing.info.fileIndex.toFullPath) # strTableIncl() for error corrections: discard strTableIncl(packSym.tab, result) -proc compileModule*(graph: ModuleGraph; fileIdx: int32; cache: IdentCache, flags: TSymFlags): PSym = +proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; cache: IdentCache, flags: TSymFlags): PSym = result = graph.getModule(fileIdx) if result == nil: #growCache gMemCacheData, fileIdx @@ -169,15 +65,17 @@ proc compileModule*(graph: ModuleGraph; fileIdx: int32; cache: IdentCache, flags if sfMainModule in result.flags: gMainPackageId = result.owner.id - if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}: - rd = handleSymbolFile(result, cache) - if result.id < 0: - internalError("handleSymbolFile should have set the module's ID") - return - else: - result.id = getID() + when false: + if conf.cmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}: + rd = handleSymbolFile(result, cache) + if result.id < 0: + internalError("handleSymbolFile should have set the module's ID") + return + else: + discard + result.id = getModuleId(fileIdx, toFullPath(fileIdx)) discard processModule(graph, result, - if sfMainModule in flags and gProjectIsStdin: stdin.llStreamOpen else: nil, + if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil, rd, cache) #if optCaasEnabled in gGlobalOptions: # gMemCacheData[fileIdx].needsRecompile = Recompiled @@ -188,7 +86,7 @@ proc compileModule*(graph: ModuleGraph; fileIdx: int32; cache: IdentCache, flags initStrTable(result.tab) result.ast = nil discard processModule(graph, result, - if sfMainModule in flags and gProjectIsStdin: stdin.llStreamOpen else: nil, + if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil, nil, cache) graph.markClientsDirty(fileIdx) when false: @@ -197,41 +95,44 @@ proc compileModule*(graph: ModuleGraph; fileIdx: int32; cache: IdentCache, flags else: result = gCompiledModules[fileIdx] -proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: int32; +proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex; cache: IdentCache): PSym {.procvar.} = # this is called by the semantic checking phase + assert graph.config != nil result = compileModule(graph, fileIdx, cache, {}) graph.addDep(s, fileIdx) #if sfSystemModule in result.flags: # localError(result.info, errAttemptToRedefine, result.name.s) # restore the notes for outer module: - gNotes = if s.owner.id == gMainPackageId: gMainPackageNotes - else: ForeignPackageNotes + graph.config.notes = + if s.owner.id == gMainPackageId: graph.config.mainPackageNotes + else: graph.config.foreignPackageNotes -proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: int32; +proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex; cache: IdentCache): PNode {.procvar.} = - result = syntaxes.parseFile(fileIdx, cache) + result = syntaxes.parseFile(fileIdx, cache, graph.config) graph.addDep(s, fileIdx) - graph.addIncludeDep(s.position.int32, fileIdx) + graph.addIncludeDep(s.position.FileIndex, fileIdx) proc compileSystemModule*(graph: ModuleGraph; cache: IdentCache) = - if magicsys.systemModule == nil: - systemFileIdx = fileInfoIdx(options.libpath/"system.nim") + if graph.systemModule == nil: + systemFileIdx = fileInfoIdx(graph.config, graph.config.libpath / "system.nim") discard graph.compileModule(systemFileIdx, cache, {sfSystemModule}) -proc wantMainModule* = - if gProjectFull.len == 0: - fatal(gCmdLineInfo, errCommandExpectsFilename) - gProjectMainIdx = addFileExt(gProjectFull, NimExt).fileInfoIdx +proc wantMainModule*(conf: ConfigRef) = + if conf.projectFull.len == 0: + fatal(conf, newLineInfo(conf, "command line", 1, 1), errGenerated, "command expects a filename") + conf.projectMainIdx = int32 fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt)) passes.gIncludeFile = includeModule passes.gImportModule = importModule proc compileProject*(graph: ModuleGraph; cache: IdentCache; - projectFileIdx = -1'i32) = - wantMainModule() - let systemFileIdx = fileInfoIdx(options.libpath / "system.nim") - let projectFile = if projectFileIdx < 0: gProjectMainIdx else: projectFileIdx + projectFileIdx = InvalidFileIDX) = + let conf = graph.config + wantMainModule(conf) + let systemFileIdx = fileInfoIdx(conf, conf.libpath / "system.nim") + let projectFile = if projectFileIdx == InvalidFileIDX: FileIndex(conf.projectMainIdx) else: projectFileIdx graph.importStack.add projectFile if projectFile == systemFileIdx: discard graph.compileModule(projectFile, cache, {sfMainModule, sfSystemModule}) @@ -240,7 +141,7 @@ proc compileProject*(graph: ModuleGraph; cache: IdentCache; discard graph.compileModule(projectFile, cache, {sfMainModule}) proc makeModule*(graph: ModuleGraph; filename: string): PSym = - result = graph.newModule(fileInfoIdx filename) + result = graph.newModule(fileInfoIdx(graph.config, filename)) result.id = getID() proc makeStdinModule*(graph: ModuleGraph): PSym = graph.makeModule"stdin" diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 4e6226122..533d3a57f 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -8,473 +8,13 @@ # import - options, strutils, os, tables, ropes, platform, terminal, macros + options, strutils, os, tables, ropes, platform, terminal, macros, + configuration -type - TMsgKind* = enum - errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile, errGenerated, - errXCompilerDoesNotSupportCpp, errStringLiteralExpected, - errIntLiteralExpected, errInvalidCharacterConstant, - errClosingTripleQuoteExpected, errClosingQuoteExpected, - errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong, - errInvalidNumber, errInvalidNumberOctalCode, errNumberOutOfRange, - errNnotAllowedInCharacter, errClosingBracketExpected, errMissingFinalQuote, - errIdentifierExpected, errNewlineExpected, errInvalidModuleName, - errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected, - errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected, - errInvalidPragma, errUnknownPragma, errInvalidDirectiveX, - errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation, - errExceptionExpected, errExceptionAlreadyHandled, - errYieldNotAllowedHere, errYieldNotAllowedInTryStmt, - errInvalidNumberOfYieldExpr, errCannotReturnExpr, - errNoReturnWithReturnTypeNotAllowed, errAttemptToRedefine, - errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel, - errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected, - errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler, - errOnOrOffExpectedButXFound, errOnOffOrListExpectedButXFound, - errNoneBoehmRefcExpectedButXFound, - errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound, - errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound, - errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected, - errExprExpected, errUndeclaredField, - errUndeclaredRoutine, errUseQualifier, - errTypeExpected, - errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable, - errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue, - errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous, - errConstantDivisionByZero, errOrdinalTypeExpected, - errOrdinalOrFloatTypeExpected, errOverOrUnderflow, - errCannotEvalXBecauseIncompletelyDefined, errChrExpectsRange0_255, - errDynlibRequiresExportc, errUndeclaredFieldX, errNilAccess, - errIndexOutOfBounds, errIndexTypesDoNotMatch, errBracketsInvalidForType, - errValueOutOfSetBounds, errFieldInitTwice, errFieldNotInit, - errExprXCannotBeCalled, errExprHasNoType, errExprXHasNoType, - errCastNotInSafeMode, errExprCannotBeCastToX, errCommaOrParRiExpected, - errCurlyLeOrParLeExpected, errSectionExpected, errRangeExpected, - errMagicOnlyInSystem, errPowerOfTwoExpected, - errStringMayNotBeEmpty, errCallConvExpected, errProcOnlyOneCallConv, - errSymbolMustBeImported, errExprMustBeBool, errConstExprExpected, - errDuplicateCaseLabel, errRangeIsEmpty, errSelectorMustBeOfCertainTypes, - errSelectorMustBeOrdinal, errOrdXMustNotBeNegative, errLenXinvalid, - errWrongNumberOfVariables, errExprCannotBeRaised, errBreakOnlyInLoop, - errTypeXhasUnknownSize, errConstNeedsConstExpr, errConstNeedsValue, - errResultCannotBeOpenArray, errSizeTooBig, errSetTooBig, - errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects, - errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX, - errCannotInstantiateX, errExprHasNoAddress, errXStackEscape, - errVarForOutParamNeededX, - errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX, - errAmbiguousCallXYZ, errWrongNumberOfArguments, - errWrongNumberOfArgumentsInCall, - errMissingGenericParamsForTemplate, - errXCannotBePassedToProcVar, - errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed, - errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX, - errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice, - errInvalidOrderInArrayConstructor, - errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry, - errOptionExpected, errXisNoLabel, errNotAllCasesCovered, - errUnknownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable, - errNoPragmasAllowedForX, errNoGenericParamsAllowedForX, - errInvalidParamKindX, errDefaultArgumentInvalid, errNamedParamHasToBeIdent, - errNoReturnTypeForX, errConvNeedsOneArg, errInvalidPragmaX, - errXNotAllowedHere, errInvalidControlFlowX, - errXisNoType, errCircumNeedsPointer, errInvalidExpression, - errInvalidExpressionX, errEnumHasNoValueX, errNamedExprExpected, - errNamedExprNotAllowed, errXExpectsOneTypeParam, - errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed, - errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType, - errNoReturnTypeDeclared, - errNoCommand, errInvalidCommandX, errXOnlyAtModuleScope, - errXNeedsParamObjectType, - errTemplateInstantiationTooNested, errInstantiationFrom, - errInvalidIndexValueForTuple, errCommandExpectsFilename, - errMainModuleMustBeSpecified, - errXExpected, - errTIsNotAConcreteType, - errCastToANonConcreteType, - errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, - errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, - errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly, - errOnlyACallOpCanBeDelegator, errUsingNoSymbol, - errMacroBodyDependsOnGenericTypes, - errDestructorNotGenericEnough, - errInlineIteratorsAsProcParams, - errXExpectsTwoArguments, - errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations, - errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX, - errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument, - errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, - errXhasSideEffects, errIteratorExpected, errLetNeedsInit, - errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX, - errXCannotBeClosure, errXMustBeCompileTime, - errCannotInferTypeOfTheLiteral, - errCannotInferReturnType, - errCannotInferStaticParam, - errGenericLambdaNotAllowed, - errProcHasNoConcreteType, - errCompilerDoesntSupportTarget, - errInOutFlagNotExtern, - errUser, - warnCannotOpenFile, - warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, - warnDeprecated, warnConfigDeprecated, - warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, - warnUnknownSubstitutionX, warnLanguageXNotSupported, - warnFieldXNotSupported, warnCommentXIgnored, - warnNilStatement, warnTypelessParam, - warnUseBase, warnWriteToForeignHeap, warnUnsafeCode, - warnEachIdentIsTuple, warnShadowIdent, - warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, - warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, - warnUser, - hintSuccess, hintSuccessX, - hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, - hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, - hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, - hintConditionAlwaysTrue, hintName, hintPattern, - hintExecuting, hintLinking, hintDependency, - hintSource, hintStackTrace, hintGCStats, - hintUser, hintUserRaw - -const - MsgKindToStr*: array[TMsgKind, string] = [ - errUnknown: "unknown error", - errInternal: "internal error: $1", - errIllFormedAstX: "illformed AST: $1", - errCannotOpenFile: "cannot open \'$1\'", - errGenerated: "$1", - errXCompilerDoesNotSupportCpp: "\'$1\' compiler does not support C++", - errStringLiteralExpected: "string literal expected", - errIntLiteralExpected: "integer literal expected", - errInvalidCharacterConstant: "invalid character constant", - errClosingTripleQuoteExpected: "closing \"\"\" expected, but end of file reached", - errClosingQuoteExpected: "closing \" expected", - errTabulatorsAreNotAllowed: "tabulators are not allowed", - errInvalidToken: "invalid token: $1", - errLineTooLong: "line too long", - errInvalidNumber: "$1 is not a valid number", - errInvalidNumberOctalCode: "$1 is not a valid number; did you mean octal? Then use one of '0o', '0c' or '0C'.", - errNumberOutOfRange: "number $1 out of valid range", - errNnotAllowedInCharacter: "\\n not allowed in character literal", - errClosingBracketExpected: "closing ']' expected, but end of file reached", - errMissingFinalQuote: "missing final \' for character literal", - errIdentifierExpected: "identifier expected, but found \'$1\'", - errNewlineExpected: "newline expected, but found \'$1\'", - errInvalidModuleName: "invalid module name: '$1'", - errOperatorExpected: "operator expected, but found \'$1\'", - errTokenExpected: "\'$1\' expected", - errStringAfterIncludeExpected: "string after \'include\' expected", - errRecursiveDependencyX: "recursive dependency: \'$1\'", - errOnOrOffExpected: "\'on\' or \'off\' expected", - errNoneSpeedOrSizeExpected: "\'none\', \'speed\' or \'size\' expected", - errInvalidPragma: "invalid pragma", - errUnknownPragma: "unknown pragma: \'$1\'", - errInvalidDirectiveX: "invalid directive: \'$1\'", - errAtPopWithoutPush: "\'pop\' without a \'push\' pragma", - errEmptyAsm: "empty asm statement", - errInvalidIndentation: "invalid indentation", - errExceptionExpected: "exception expected", - errExceptionAlreadyHandled: "exception already handled", - errYieldNotAllowedHere: "'yield' only allowed in an iterator", - errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator", - errInvalidNumberOfYieldExpr: "invalid number of \'yield\' expressions", - errCannotReturnExpr: "current routine cannot return an expression", - errNoReturnWithReturnTypeNotAllowed: "routines with NoReturn pragma are not allowed to have return type", - errAttemptToRedefine: "redefinition of \'$1\'", - errStmtInvalidAfterReturn: "statement not allowed after \'return\', \'break\', \'raise\', \'continue\' or proc call with noreturn pragma", - errStmtExpected: "statement expected", - errInvalidLabel: "\'$1\' is no label", - errInvalidCmdLineOption: "invalid command line option: \'$1\'", - errCmdLineArgExpected: "argument for command line option expected: \'$1\'", - errCmdLineNoArgExpected: "invalid argument for command line option: \'$1\'", - errInvalidVarSubstitution: "invalid variable substitution in \'$1\'", - errUnknownVar: "unknown variable: \'$1\'", - errUnknownCcompiler: "unknown C compiler: \'$1\'", - errOnOrOffExpectedButXFound: "\'on\' or \'off\' expected, but \'$1\' found", - errOnOffOrListExpectedButXFound: "\'on\', \'off\' or \'list\' expected, but \'$1\' found", - errNoneBoehmRefcExpectedButXFound: "'none', 'boehm' or 'refc' expected, but '$1' found", - errNoneSpeedOrSizeExpectedButXFound: "'none', 'speed' or 'size' expected, but '$1' found", - errGuiConsoleOrLibExpectedButXFound: "'gui', 'console' or 'lib' expected, but '$1' found", - errUnknownOS: "unknown OS: '$1'", - errUnknownCPU: "unknown CPU: '$1'", - errGenOutExpectedButXFound: "'c', 'c++' or 'yaml' expected, but '$1' found", - errArgsNeedRunOption: "arguments can only be given if the '--run' option is selected", - errInvalidMultipleAsgn: "multiple assignment is not allowed", - errColonOrEqualsExpected: "\':\' or \'=\' expected, but found \'$1\'", - errExprExpected: "expression expected, but found \'$1\'", - errUndeclaredField: "undeclared field: \'$1\'", - errUndeclaredRoutine: "attempting to call undeclared routine: \'$1\'", - errUseQualifier: "ambiguous identifier: \'$1\' -- use a qualifier", - errTypeExpected: "type expected", - errSystemNeeds: "system module needs \'$1\'", - errExecutionOfProgramFailed: "execution of an external program failed: '$1'", - errNotOverloadable: "overloaded \'$1\' leads to ambiguous calls", - errInvalidArgForX: "invalid argument for \'$1\'", - errStmtHasNoEffect: "statement has no effect", - errXExpectsTypeOrValue: "\'$1\' expects a type or value", - errXExpectsArrayType: "\'$1\' expects an array type", - errIteratorCannotBeInstantiated: "'$1' cannot be instantiated because its body has not been compiled yet", - errExprXAmbiguous: "expression '$1' ambiguous in this context", - errConstantDivisionByZero: "division by zero", - errOrdinalTypeExpected: "ordinal type expected", - errOrdinalOrFloatTypeExpected: "ordinal or float type expected", - errOverOrUnderflow: "over- or underflow", - errCannotEvalXBecauseIncompletelyDefined: "cannot evaluate '$1' because type is not defined completely", - errChrExpectsRange0_255: "\'chr\' expects an int in the range 0..255", - errDynlibRequiresExportc: "\'dynlib\' requires \'exportc\'", - errUndeclaredFieldX: "undeclared field: \'$1\'", - errNilAccess: "attempt to access a nil address", - errIndexOutOfBounds: "index out of bounds", - errIndexTypesDoNotMatch: "index types do not match", - errBracketsInvalidForType: "\'[]\' operator invalid for this type", - errValueOutOfSetBounds: "value out of set bounds", - errFieldInitTwice: "field initialized twice: \'$1\'", - errFieldNotInit: "field \'$1\' not initialized", - errExprXCannotBeCalled: "expression \'$1\' cannot be called", - errExprHasNoType: "expression has no type", - errExprXHasNoType: "expression \'$1\' has no type (or is ambiguous)", - errCastNotInSafeMode: "\'cast\' not allowed in safe mode", - errExprCannotBeCastToX: "expression cannot be cast to $1", - errCommaOrParRiExpected: "',' or ')' expected", - errCurlyLeOrParLeExpected: "\'{\' or \'(\' expected", - errSectionExpected: "section (\'type\', \'proc\', etc.) expected", - errRangeExpected: "range expected", - errMagicOnlyInSystem: "\'magic\' only allowed in system module", - errPowerOfTwoExpected: "power of two expected", - errStringMayNotBeEmpty: "string literal may not be empty", - errCallConvExpected: "calling convention expected", - errProcOnlyOneCallConv: "a proc can only have one calling convention", - errSymbolMustBeImported: "symbol must be imported if 'lib' pragma is used", - errExprMustBeBool: "expression must be of type 'bool'", - errConstExprExpected: "constant expression expected", - errDuplicateCaseLabel: "duplicate case label", - errRangeIsEmpty: "range is empty", - errSelectorMustBeOfCertainTypes: "selector must be of an ordinal type, float or string", - errSelectorMustBeOrdinal: "selector must be of an ordinal type", - errOrdXMustNotBeNegative: "ord($1) must not be negative", - errLenXinvalid: "len($1) must be less than 32768", - errWrongNumberOfVariables: "wrong number of variables", - errExprCannotBeRaised: "only a 'ref object' can be raised", - errBreakOnlyInLoop: "'break' only allowed in loop construct", - errTypeXhasUnknownSize: "type \'$1\' has unknown size", - errConstNeedsConstExpr: "a constant can only be initialized with a constant expression", - errConstNeedsValue: "a constant needs a value", - errResultCannotBeOpenArray: "the result type cannot be on open array", - errSizeTooBig: "computing the type\'s size produced an overflow", - errSetTooBig: "set is too large", - errBaseTypeMustBeOrdinal: "base type of a set must be an ordinal", - errInheritanceOnlyWithNonFinalObjects: "inheritance only works with non-final objects", - errInheritanceOnlyWithEnums: "inheritance only works with an enum", - errIllegalRecursionInTypeX: "illegal recursion in type \'$1\'", - errCannotInstantiateX: "cannot instantiate: \'$1\'", - errExprHasNoAddress: "expression has no address", - errXStackEscape: "address of '$1' may not escape its stack frame", - errVarForOutParamNeededX: "for a \'var\' type a variable needs to be passed; but '$1' is immutable", - errPureTypeMismatch: "type mismatch", - errTypeMismatch: "type mismatch: got (", - errButExpected: "but expected one of: ", - errButExpectedX: "but expected \'$1\'", - errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3", - errWrongNumberOfArguments: "wrong number of arguments", - errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'", - errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters", - errXCannotBePassedToProcVar: "\'$1\' cannot be passed to a procvar", - errXCannotBeInParamDecl: "$1 cannot be declared in parameter declaration", - errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1", - errImplOfXNotAllowed: "implementation of \'$1\' is not allowed", - errImplOfXexpected: "implementation of \'$1\' expected", - errNoSymbolToBorrowFromFound: "no symbol to borrow from found", - errDiscardValueX: "value of type '$1' has to be discarded", - errInvalidDiscard: "statement returns no value that can be discarded", - errIllegalConvFromXtoY: "conversion from $1 to $2 is invalid", - errCannotBindXTwice: "cannot bind parameter \'$1\' twice", - errInvalidOrderInArrayConstructor: "invalid order in array constructor", - errInvalidOrderInEnumX: "invalid order in enum \'$1\'", - errEnumXHasHoles: "enum \'$1\' has holes", - errExceptExpected: "\'except\' or \'finally\' expected", - errInvalidTry: "after catch all \'except\' or \'finally\' no section may follow", - errOptionExpected: "option expected, but found \'$1\'", - errXisNoLabel: "\'$1\' is not a label", - errNotAllCasesCovered: "not all cases are covered", - errUnknownSubstitionVar: "unknown substitution variable: \'$1\'", - errComplexStmtRequiresInd: "complex statement requires indentation", - errXisNotCallable: "\'$1\' is not callable", - errNoPragmasAllowedForX: "no pragmas allowed for $1", - errNoGenericParamsAllowedForX: "no generic parameters allowed for $1", - errInvalidParamKindX: "invalid param kind: \'$1\'", - errDefaultArgumentInvalid: "default argument invalid", - errNamedParamHasToBeIdent: "named parameter has to be an identifier", - errNoReturnTypeForX: "no return type allowed for $1", - errConvNeedsOneArg: "a type conversion needs exactly one argument", - errInvalidPragmaX: "invalid pragma: $1", - errXNotAllowedHere: "$1 not allowed here", - errInvalidControlFlowX: "invalid control flow: $1", - errXisNoType: "invalid type: \'$1\'", - errCircumNeedsPointer: "'[]' needs a pointer or reference type", - errInvalidExpression: "invalid expression", - errInvalidExpressionX: "invalid expression: \'$1\'", - errEnumHasNoValueX: "enum has no value \'$1\'", - errNamedExprExpected: "named expression expected", - errNamedExprNotAllowed: "named expression not allowed here", - errXExpectsOneTypeParam: "\'$1\' expects one type parameter", - errArrayExpectsTwoTypeParams: "array expects two type parameters", - errInvalidVisibilityX: "invalid visibility: \'$1\'", - errInitHereNotAllowed: "initialization not allowed here", - errXCannotBeAssignedTo: "\'$1\' cannot be assigned to", - errIteratorNotAllowed: "iterators can only be defined at the module\'s top level", - errXNeedsReturnType: "$1 needs a return type", - errNoReturnTypeDeclared: "no return type declared", - errNoCommand: "no command given", - errInvalidCommandX: "invalid command: \'$1\'", - errXOnlyAtModuleScope: "\'$1\' is only allowed at top level", - errXNeedsParamObjectType: "'$1' needs a parameter that has an object type", - errTemplateInstantiationTooNested: "template/macro instantiation too nested", - errInstantiationFrom: "template/generic instantiation from here", - errInvalidIndexValueForTuple: "invalid index value for tuple subscript", - errCommandExpectsFilename: "command expects a filename argument", - errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file", - errXExpected: "\'$1\' expected", - errTIsNotAConcreteType: "\'$1\' is not a concrete type.", - errCastToANonConcreteType: "cannot cast to a non concrete type: \'$1\'", - errInvalidSectionStart: "invalid section start", - errGridTableNotImplemented: "grid table is not implemented", - errGeneralParseError: "general parse error", - errNewSectionExpected: "new section expected", - errWhitespaceExpected: "whitespace expected, got \'$1\'", - errXisNoValidIndexFile: "\'$1\' is no valid index file", - errCannotRenderX: "cannot render reStructuredText element \'$1\'", - errVarVarTypeNotAllowed: "type \'var var\' is not allowed", - errInstantiateXExplicitly: "instantiate '$1' explicitly", - errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator", - errUsingNoSymbol: "'$1' is not a variable, constant or a proc name", - errMacroBodyDependsOnGenericTypes: "the macro body cannot be compiled, " & - "because the parameter '$1' has a generic type", - errDestructorNotGenericEnough: "Destructor signature is too specific. " & - "A destructor must be associated will all instantiations of a generic type", - errInlineIteratorsAsProcParams: "inline iterators can be used as parameters only for " & - "templates, macros and other inline iterators", - errXExpectsTwoArguments: "\'$1\' expects two arguments", - errXExpectsObjectTypes: "\'$1\' expects object types", - errXcanNeverBeOfThisSubtype: "\'$1\' can never be of this subtype", - errTooManyIterations: "interpretation requires too many iterations", - errCannotInterpretNodeX: "cannot evaluate \'$1\'", - errFieldXNotFound: "field \'$1\' cannot be found", - errInvalidConversionFromTypeX: "invalid conversion from type \'$1\'", - errAssertionFailed: "assertion failed", - errCannotGenerateCodeForX: "cannot generate code for \'$1\'", - errXRequiresOneArgument: "$1 requires one parameter", - errUnhandledExceptionX: "unhandled exception: $1", - errCyclicTree: "macro returned a cyclic abstract syntax tree", - errXisNoMacroOrTemplate: "\'$1\' is no macro or template", - errXhasSideEffects: "\'$1\' can have side effects", - errIteratorExpected: "iterator within for loop context expected", - errLetNeedsInit: "'let' symbol requires an initialization", - errThreadvarCannotInit: "a thread var cannot be initialized explicitly", - errWrongSymbolX: "usage of \'$1\' is a user-defined error", - errIllegalCaptureX: "illegal capture '$1'", - errXCannotBeClosure: "'$1' cannot have 'closure' calling convention", - errXMustBeCompileTime: "'$1' can only be used in compile-time context", - errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1", - errCannotInferReturnType: "cannot infer the return type of the proc", - errCannotInferStaticParam: "cannot infer the value of the static param `$1`", - errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " & - "it is used as an operand to another routine and the types " & - "of the generic paramers can be inferred from the expected signature.", - errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.", - errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target", - errInOutFlagNotExtern: "The `$1` modifier can be used only with imported types", - errUser: "$1", - warnCannotOpenFile: "cannot open \'$1\'", - warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored", - warnXIsNeverRead: "\'$1\' is never read", - warnXmightNotBeenInit: "\'$1\' might not have been initialized", - warnDeprecated: "$1 is deprecated", - warnConfigDeprecated: "config file '$1' is deprecated", - warnSmallLshouldNotBeUsed: "\'l\' should not be used as an identifier; may look like \'1\' (one)", - warnUnknownMagic: "unknown magic \'$1\' might crash the compiler", - warnRedefinitionOfLabel: "redefinition of label \'$1\'", - warnUnknownSubstitutionX: "unknown substitution \'$1\'", - warnLanguageXNotSupported: "language \'$1\' not supported", - warnFieldXNotSupported: "field \'$1\' not supported", - warnCommentXIgnored: "comment \'$1\' ignored", - warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead", - warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'", - warnUseBase: "use {.base.} for base methods; baseless methods are deprecated", - warnWriteToForeignHeap: "write to foreign heap", - warnUnsafeCode: "unsafe code: '$1'", - warnEachIdentIsTuple: "each identifier is a tuple", - warnShadowIdent: "shadowed identifier: '$1'", - warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.", - warnProveField: "cannot prove that field '$1' is accessible", - warnProveIndex: "cannot prove index '$1' is valid", - warnGcUnsafe: "not GC-safe: '$1'", - warnGcUnsafe2: "$1", - warnUninit: "'$1' might not have been initialized", - warnGcMem: "'$1' uses GC'ed memory", - warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.", - warnLockLevel: "$1", - warnResultShadowed: "Special variable 'result' is shadowed.", - warnUser: "$1", - hintSuccess: "operation successful", - hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)", - hintLineTooLong: "line too long", - hintXDeclaredButNotUsed: "\'$1\' is declared but not used", - hintConvToBaseNotNeeded: "conversion to base object is not needed", - hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless", - hintExprAlwaysX: "expression evaluates always to \'$1\'", - hintQuitCalled: "quit() called", - hintProcessing: "$1", - hintCodeBegin: "generated code listing:", - hintCodeEnd: "end of listing", - hintConf: "used config file \'$1\'", - hintPath: "added path: '$1'", - hintConditionAlwaysTrue: "condition is always true: '$1'", - hintName: "name should be: '$1'", - hintPattern: "$1", - hintExecuting: "$1", - hintLinking: "", - hintDependency: "$1", - hintSource: "$1", - hintStackTrace: "$1", - hintGCStats: "$1", - hintUser: "$1", - hintUserRaw: "$1"] - -const - WarningsToStr* = ["CannotOpenFile", "OctalEscape", - "XIsNeverRead", "XmightNotBeenInit", - "Deprecated", "ConfigDeprecated", - "SmallLshouldNotBeUsed", "UnknownMagic", - "RedefinitionOfLabel", "UnknownSubstitutionX", - "LanguageXNotSupported", "FieldXNotSupported", - "CommentXIgnored", "NilStmt", - "TypelessParam", "UseBase", "WriteToForeignHeap", - "UnsafeCode", "EachIdentIsTuple", "ShadowIdent", - "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", - "GcMem", "Destructor", "LockLevel", "ResultShadowed", "User"] - - HintsToStr* = ["Success", "SuccessX", "LineTooLong", - "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", - "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", - "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency", - "Source", "StackTrace", "GCStats", - "User", "UserRaw"] - -const - fatalMin* = errUnknown - fatalMax* = errInternal - errMin* = errUnknown - errMax* = errUser - warnMin* = warnCannotOpenFile - warnMax* = pred(hintSuccess) - hintMin* = hintSuccess - hintMax* = high(TMsgKind) +#type +# MsgConfig* = ref object of RootObj type - TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints - TNoteKinds* = set[TNoteKind] - TFileInfo* = object fullPath: string # This is a canonical full filesystem path projPath*: string # This is relative to the project's root @@ -491,15 +31,19 @@ type dirtyfile: string # the file that is actually read into memory # and parsed; usually 'nil' but is used # for 'nimsuggest' - + hash*: string # the checksum of the file + when defined(nimpretty): + fullContent*: string + FileIndex* = distinct int32 TLineInfo* = object # This is designed to be as small as possible, # because it is used # in syntax nodes. We save space here by using # two int16 and an int32. # On 64 bit and on 32 bit systems this is # only 8 bytes. - line*, col*: int16 - fileIndex*: int32 + line*: uint16 + col*: int16 + fileIndex*: FileIndex when defined(nimpretty): offsetA*, offsetB*: int commentOffsetA*, commentOffsetB*: int @@ -513,38 +57,16 @@ type ERecoverableError* = object of ValueError ESuggestDone* = object of Exception -const - NotesVerbosity*: array[0..3, TNoteKinds] = [ - {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, - warnProveField, warnProveIndex, - warnGcUnsafe, - hintSuccessX, hintPath, hintConf, - hintProcessing, hintPattern, - hintDependency, - hintExecuting, hintLinking, - hintCodeBegin, hintCodeEnd, - hintSource, hintStackTrace, - hintGCStats}, - {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, - warnProveField, warnProveIndex, - warnGcUnsafe, - hintPath, - hintDependency, - hintCodeBegin, hintCodeEnd, - hintSource, hintStackTrace, - hintGCStats}, - {low(TNoteKind)..high(TNoteKind)} - {hintStackTrace, warnUninit}, - {low(TNoteKind)..high(TNoteKind)}] +proc `==`*(a, b: FileIndex): bool {.borrow.} + const - InvalidFileIDX* = int32(-1) + InvalidFileIDX* = FileIndex(-1) var - ForeignPackageNotes*: TNoteKinds = {hintProcessing, warnUnknownMagic, - hintQuitCalled, hintExecuting} - filenameToIndexTbl = initTable[string, int32]() + filenameToIndexTbl = initTable[string, FileIndex]() fileInfos*: seq[TFileInfo] = @[] - systemFileIdx*: int32 + systemFileIdx*: FileIndex proc toCChar*(c: char): string = case c @@ -577,25 +99,36 @@ proc newFileInfo(fullPath, projPath: string): TFileInfo = result.shortName = fileName.changeFileExt("") result.quotedName = fileName.makeCString result.quotedFullName = fullPath.makeCString - if optEmbedOrigSrc in gGlobalOptions or true: - result.lines = @[] - -proc fileInfoKnown*(filename: string): bool = + result.lines = @[] + when defined(nimpretty): + if result.fullPath.len > 0: + try: + result.fullContent = readFile(result.fullPath) + except IOError: + #rawMessage(errCannotOpenFile, result.fullPath) + # XXX fixme + result.fullContent = "" + +when defined(nimpretty): + proc fileSection*(fid: FileIndex; a, b: int): string = + substr(fileInfos[fid.int].fullContent, a, b) + +proc fileInfoKnown*(conf: ConfigRef; filename: string): bool = var canon: string try: - canon = canonicalizePath(filename) + canon = canonicalizePath(conf, filename) except: canon = filename result = filenameToIndexTbl.hasKey(canon) -proc fileInfoIdx*(filename: string; isKnownFile: var bool): int32 = +proc fileInfoIdx*(conf: ConfigRef; filename: string; isKnownFile: var bool): FileIndex = var canon: string pseudoPath = false try: - canon = canonicalizePath(filename) + canon = canonicalizePath(conf, filename) shallow(canon) except: canon = filename @@ -607,46 +140,39 @@ proc fileInfoIdx*(filename: string; isKnownFile: var bool): int32 = result = filenameToIndexTbl[canon] else: isKnownFile = false - result = fileInfos.len.int32 + result = fileInfos.len.FileIndex fileInfos.add(newFileInfo(canon, if pseudoPath: filename - else: canon.shortenDir)) + else: shortenDir(conf, canon))) filenameToIndexTbl[canon] = result -proc fileInfoIdx*(filename: string): int32 = +proc fileInfoIdx*(conf: ConfigRef; filename: string): FileIndex = var dummy: bool - result = fileInfoIdx(filename, dummy) + result = fileInfoIdx(conf, filename, dummy) -proc newLineInfo*(fileInfoIdx: int32, line, col: int): TLineInfo = +proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo = result.fileIndex = fileInfoIdx - result.line = int16(line) + result.line = uint16(line) result.col = int16(col) -proc newLineInfo*(filename: string, line, col: int): TLineInfo {.inline.} = - result = newLineInfo(filename.fileInfoIdx, line, col) +proc newLineInfo*(conf: ConfigRef; filename: string, line, col: int): TLineInfo {.inline.} = + result = newLineInfo(fileInfoIdx(conf, filename), line, col) -fileInfos.add(newFileInfo("", "command line")) -var gCmdLineInfo* = newLineInfo(int32(0), 1, 1) +when false: + fileInfos.add(newFileInfo("", "command line")) + var gCmdLineInfo* = newLineInfo(FileIndex(0), 1, 1) -fileInfos.add(newFileInfo("", "compilation artifact")) -var gCodegenLineInfo* = newLineInfo(int32(1), 1, 1) + fileInfos.add(newFileInfo("", "compilation artifact")) + var gCodegenLineInfo* = newLineInfo(FileIndex(1), 1, 1) proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} = raise newException(ERecoverableError, msg) -proc sourceLine*(i: TLineInfo): Rope - -var - gNotes*: TNoteKinds = NotesVerbosity[1] # defaults to verbosity of 1 - gErrorCounter*: int = 0 # counts the number of errors - gHintCounter*: int = 0 - gWarnCounter*: int = 0 - gErrorMax*: int = 1 # stop after gErrorMax errors - gMainPackageNotes*: TNoteKinds = NotesVerbosity[1] +proc sourceLine*(conf: ConfigRef; i: TLineInfo): Rope proc unknownLineInfo*(): TLineInfo = - result.line = int16(-1) + result.line = uint16(0) result.col = int16(-1) - result.fileIndex = -1 + result.fileIndex = InvalidFileIDX type Severity* {.pure.} = enum ## VS Code only supports these three @@ -708,24 +234,32 @@ proc getInfoContext*(index: int): TLineInfo = if i >=% L: result = unknownLineInfo() else: result = msgContext[i] -template toFilename*(fileIdx: int32): string = - (if fileIdx < 0: "???" else: fileInfos[fileIdx].projPath) +template toFilename*(fileIdx: FileIndex): string = + (if fileIdx.int32 < 0: "???" else: fileInfos[fileIdx.int32].projPath) + +proc toFullPath*(fileIdx: FileIndex): string = + if fileIdx.int32 < 0: result = "???" + else: result = fileInfos[fileIdx.int32].fullPath -proc toFullPath*(fileIdx: int32): string = - if fileIdx < 0: result = "???" - else: result = fileInfos[fileIdx].fullPath +proc setDirtyFile*(fileIdx: FileIndex; filename: string) = + assert fileIdx.int32 >= 0 + fileInfos[fileIdx.int32].dirtyFile = filename -proc setDirtyFile*(fileIdx: int32; filename: string) = - assert fileIdx >= 0 - fileInfos[fileIdx].dirtyFile = filename +proc setHash*(fileIdx: FileIndex; hash: string) = + assert fileIdx.int32 >= 0 + shallowCopy(fileInfos[fileIdx.int32].hash, hash) -proc toFullPathConsiderDirty*(fileIdx: int32): string = - if fileIdx < 0: +proc getHash*(fileIdx: FileIndex): string = + assert fileIdx.int32 >= 0 + shallowCopy(result, fileInfos[fileIdx.int32].hash) + +proc toFullPathConsiderDirty*(fileIdx: FileIndex): string = + if fileIdx.int32 < 0: result = "???" - elif not fileInfos[fileIdx].dirtyFile.isNil: - result = fileInfos[fileIdx].dirtyFile + elif not fileInfos[fileIdx.int32].dirtyFile.isNil: + result = fileInfos[fileIdx.int32].dirtyFile else: - result = fileInfos[fileIdx].fullPath + result = fileInfos[fileIdx.int32].fullPath template toFilename*(info: TLineInfo): string = info.fileIndex.toFilename @@ -733,16 +267,16 @@ template toFilename*(info: TLineInfo): string = template toFullPath*(info: TLineInfo): string = info.fileIndex.toFullPath -proc toMsgFilename*(info: TLineInfo): string = - if info.fileIndex < 0: +proc toMsgFilename*(conf: ConfigRef; info: TLineInfo): string = + if info.fileIndex.int32 < 0: result = "???" - elif gListFullPaths: - result = fileInfos[info.fileIndex].fullPath + elif optListFullPaths in conf.globalOptions: + result = fileInfos[info.fileIndex.int32].fullPath else: - result = fileInfos[info.fileIndex].projPath + result = fileInfos[info.fileIndex.int32].projPath proc toLinenumber*(info: TLineInfo): int {.inline.} = - result = info.line + result = int info.line proc toColumn*(info: TLineInfo): int {.inline.} = result = info.col @@ -759,7 +293,7 @@ proc `??`* (info: TLineInfo, filename: string): bool = # only for debugging purposes result = filename in info.toFilename -const trackPosInvalidFileIdx* = -2 # special marker so that no suggestions +const trackPosInvalidFileIdx* = FileIndex(-2) # special marker so that no suggestions # are produced within comments and string literals var gTrackPos*: TLineInfo var gTrackPosAttached*: bool ## whether the tracking position was attached to some @@ -771,7 +305,7 @@ type msgSkipHook ## skip message hook even if it is present MsgFlags* = set[MsgFlag] -proc msgWriteln*(s: string, flags: MsgFlags = {}) = +proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) = ## Writes given message string to stderr by default. ## If ``--stdout`` option is given, writes to stdout instead. If message hook ## is present, then it is used to output message rather than stderr/stdout. @@ -779,11 +313,11 @@ proc msgWriteln*(s: string, flags: MsgFlags = {}) = ## This is used for 'nim dump' etc. where we don't have nimsuggest ## support. - #if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return + #if conf.cmd == cmdIdeTools and optCDebug notin gGlobalOptions: return if not isNil(writelnHook) and msgSkipHook notin flags: writelnHook(s) - elif optStdout in gGlobalOptions or msgStdout in flags: + elif optStdout in conf.globalOptions or msgStdout in flags: if eStdOut in errorOutputs: writeLine(stdout, s) flushFile(stdout) @@ -824,13 +358,13 @@ template callWritelnHook(args: varargs[string, `$`]) = template styledMsgWriteln*(args: varargs[typed]) = if not isNil(writelnHook): callIgnoringStyle(callWritelnHook, nil, args) - elif optStdout in gGlobalOptions: + elif optStdout in conf.globalOptions: if eStdOut in errorOutputs: callIgnoringStyle(writeLine, stdout, args) flushFile(stdout) else: if eStdErr in errorOutputs: - if optUseColors in gGlobalOptions: + if optUseColors in conf.globalOptions: callStyledWriteLineStderr(args) else: callIgnoringStyle(writeLine, stderr, args) @@ -858,27 +392,27 @@ proc log*(s: string) {.procvar.} = f.writeLine(s) close(f) -proc quit(msg: TMsgKind) = - if defined(debug) or msg == errInternal or hintStackTrace in gNotes: +proc quit(conf: ConfigRef; msg: TMsgKind) = + if defined(debug) or msg == errInternal or hintStackTrace in conf.notes: if stackTraceAvailable() and isNil(writelnHook): writeStackTrace() else: styledMsgWriteln(fgRed, "No stack traceback available\n" & "To create a stacktrace, rerun compilation with ./koch temp " & - options.command & " <file>") + conf.command & " <file>") quit 1 -proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) = +proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string) = if msg >= fatalMin and msg <= fatalMax: - if gCmd == cmdIdeTools: log(s) - quit(msg) + if conf.cmd == cmdIdeTools: log(s) + quit(conf, msg) if msg >= errMin and msg <= errMax: - inc(gErrorCounter) - options.gExitcode = 1'i8 - if gErrorCounter >= gErrorMax: - quit(msg) - elif eh == doAbort and gCmd != cmdIdeTools: - quit(msg) + inc(conf.errorCounter) + conf.exitcode = 1'i8 + if conf.errorCounter >= conf.errorMax: + quit(conf, msg) + elif eh == doAbort and conf.cmd != cmdIdeTools: + quit(conf, msg) elif eh == doRaise: raiseRecoverableError(s) @@ -888,26 +422,27 @@ proc `==`*(a, b: TLineInfo): bool = proc exactEquals*(a, b: TLineInfo): bool = result = a.fileIndex == b.fileIndex and a.line == b.line and a.col == b.col -proc writeContext(lastinfo: TLineInfo) = +proc writeContext(conf: ConfigRef; lastinfo: TLineInfo) = + const instantiationFrom = "template/generic instantiation from here" var info = lastinfo for i in countup(0, len(msgContext) - 1): if msgContext[i] != lastinfo and msgContext[i] != info: if structuredErrorHook != nil: - structuredErrorHook(msgContext[i], getMessageStr(errInstantiationFrom, ""), + structuredErrorHook(msgContext[i], instantiationFrom, Severity.Error) else: styledMsgWriteln(styleBright, - PosFormat % [toMsgFilename(msgContext[i]), - coordToStr(msgContext[i].line), + PosFormat % [toMsgFilename(conf, msgContext[i]), + coordToStr(msgContext[i].line.int), coordToStr(msgContext[i].col+1)], resetStyle, - getMessageStr(errInstantiationFrom, "")) + instantiationFrom) info = msgContext[i] -proc ignoreMsgBecauseOfIdeTools(msg: TMsgKind): bool = - msg >= errGenerated and gCmd == cmdIdeTools and optIdeDebug notin gGlobalOptions +proc ignoreMsgBecauseOfIdeTools(conf: ConfigRef; msg: TMsgKind): bool = + msg >= errGenerated and conf.cmd == cmdIdeTools and optIdeDebug notin conf.globalOptions -proc rawMessage*(msg: TMsgKind, args: openArray[string]) = +proc rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) = var title: string color: ForegroundColor @@ -916,62 +451,62 @@ proc rawMessage*(msg: TMsgKind, args: openArray[string]) = case msg of errMin..errMax: sev = Severity.Error - writeContext(unknownLineInfo()) + writeContext(conf, unknownLineInfo()) title = ErrorTitle color = ErrorColor of warnMin..warnMax: sev = Severity.Warning - if optWarns notin gOptions: return - if msg notin gNotes: return - writeContext(unknownLineInfo()) + if optWarns notin conf.options: return + if msg notin conf.notes: return + writeContext(conf, unknownLineInfo()) title = WarningTitle color = WarningColor kind = WarningsToStr[ord(msg) - ord(warnMin)] - inc(gWarnCounter) + inc(conf.warnCounter) of hintMin..hintMax: sev = Severity.Hint - if optHints notin gOptions: return - if msg notin gNotes: return + if optHints notin conf.options: return + if msg notin conf.notes: return title = HintTitle color = HintColor if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)] - inc(gHintCounter) + inc(conf.hintCounter) let s = msgKindToString(msg) % args if structuredErrorHook != nil: structuredErrorHook(unknownLineInfo(), s & (if kind != nil: KindFormat % kind else: ""), sev) - if not ignoreMsgBecauseOfIdeTools(msg): + if not ignoreMsgBecauseOfIdeTools(conf, msg): if kind != nil: styledMsgWriteln(color, title, resetStyle, s, KindColor, `%`(KindFormat, kind)) else: styledMsgWriteln(color, title, resetStyle, s) - handleError(msg, doAbort, s) + handleError(conf, msg, doAbort, s) -proc rawMessage*(msg: TMsgKind, arg: string) = - rawMessage(msg, [arg]) +proc rawMessage*(conf: ConfigRef; msg: TMsgKind, arg: string) = + rawMessage(conf, msg, [arg]) -proc resetAttributes* = - if {optUseColors, optStdout} * gGlobalOptions == {optUseColors}: +proc resetAttributes*(conf: ConfigRef) = + if {optUseColors, optStdout} * conf.globalOptions == {optUseColors}: terminal.resetAttributes(stderr) -proc writeSurroundingSrc(info: TLineInfo) = +proc writeSurroundingSrc(conf: ConfigRef; info: TLineInfo) = const indent = " " - msgWriteln(indent & $info.sourceLine) - msgWriteln(indent & spaces(info.col) & '^') + msgWriteln(conf, indent & $sourceLine(conf, info)) + msgWriteln(conf, indent & spaces(info.col) & '^') -proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string = +proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): string = let title = case msg of warnMin..warnMax: WarningTitle of hintMin..hintMax: HintTitle else: ErrorTitle - result = PosFormat % [toMsgFilename(info), coordToStr(info.line), + result = PosFormat % [toMsgFilename(conf, info), coordToStr(info.line.int), coordToStr(info.col+1)] & title & getMessageStr(msg, arg) -proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, +proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, eh: TErrorHandling) = var title: string @@ -982,7 +517,7 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, case msg of errMin..errMax: sev = Severity.Error - writeContext(info) + writeContext(conf, info) title = ErrorTitle color = ErrorColor # we try to filter error messages so that not two error message @@ -991,125 +526,124 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, lastError = info of warnMin..warnMax: sev = Severity.Warning - ignoreMsg = optWarns notin gOptions or msg notin gNotes - if not ignoreMsg: writeContext(info) + ignoreMsg = optWarns notin conf.options or msg notin conf.notes + if not ignoreMsg: writeContext(conf, info) title = WarningTitle color = WarningColor kind = WarningsToStr[ord(msg) - ord(warnMin)] - inc(gWarnCounter) + inc(conf.warnCounter) of hintMin..hintMax: sev = Severity.Hint - ignoreMsg = optHints notin gOptions or msg notin gNotes + ignoreMsg = optHints notin conf.options or msg notin conf.notes title = HintTitle color = HintColor if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)] - inc(gHintCounter) + inc(conf.hintCounter) # 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 x = PosFormat % [toMsgFilename(info), coordToStr(info.line), + let x = PosFormat % [toMsgFilename(conf, info), coordToStr(info.line.int), coordToStr(info.col+1)] let s = getMessageStr(msg, arg) if not ignoreMsg: if structuredErrorHook != nil: structuredErrorHook(info, s & (if kind != nil: KindFormat % kind else: ""), sev) - if not ignoreMsgBecauseOfIdeTools(msg): + if not ignoreMsgBecauseOfIdeTools(conf, msg): if kind != nil: styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s, KindColor, `%`(KindFormat, kind)) else: styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s) - if msg in errMin..errMax and hintSource in gNotes: - info.writeSurroundingSrc - handleError(msg, eh, s) + if hintSource in conf.notes: + conf.writeSurroundingSrc(info) + handleError(conf, msg, eh, s) -proc fatal*(info: TLineInfo, msg: TMsgKind, arg = "") = - liMessage(info, msg, arg, doAbort) +proc fatal*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = + # this fixes bug #7080 so that it is at least obvious 'fatal' + # was executed. + errorOutputs = {eStdOut, eStdErr} + liMessage(conf, info, msg, arg, doAbort) -proc globalError*(info: TLineInfo, msg: TMsgKind, arg = "") = - liMessage(info, msg, arg, doRaise) +proc globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = + liMessage(conf, info, msg, arg, doRaise) -proc globalError*(info: TLineInfo, arg: string) = - liMessage(info, errGenerated, arg, doRaise) +proc globalError*(conf: ConfigRef; info: TLineInfo, arg: string) = + liMessage(conf, info, errGenerated, arg, doRaise) -proc localError*(info: TLineInfo, msg: TMsgKind, arg = "") = - liMessage(info, msg, arg, doNothing) +proc localError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = + liMessage(conf, info, msg, arg, doNothing) -proc localError*(info: TLineInfo, arg: string) = - liMessage(info, errGenerated, arg, doNothing) +proc localError*(conf: ConfigRef; info: TLineInfo, arg: string) = + liMessage(conf, info, errGenerated, arg, doNothing) -proc localError*(info: TLineInfo, format: string, params: openarray[string]) = - localError(info, format % params) +proc localError*(conf: ConfigRef; info: TLineInfo, format: string, params: openarray[string]) = + localError(conf, info, format % params) -proc message*(info: TLineInfo, msg: TMsgKind, arg = "") = - liMessage(info, msg, arg, doNothing) +proc message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = + liMessage(conf, info, msg, arg, doNothing) -proc internalError*(info: TLineInfo, errMsg: string) = - if gCmd == cmdIdeTools and structuredErrorHook.isNil: return - writeContext(info) - liMessage(info, errInternal, errMsg, doAbort) +proc internalError*(conf: ConfigRef; info: TLineInfo, errMsg: string) = + if conf.cmd == cmdIdeTools and structuredErrorHook.isNil: return + writeContext(conf, info) + liMessage(conf, info, errInternal, errMsg, doAbort) -proc internalError*(errMsg: string) = - if gCmd == cmdIdeTools and structuredErrorHook.isNil: return - writeContext(unknownLineInfo()) - rawMessage(errInternal, errMsg) +proc internalError*(conf: ConfigRef; errMsg: string) = + if conf.cmd == cmdIdeTools and structuredErrorHook.isNil: return + writeContext(conf, unknownLineInfo()) + rawMessage(conf, errInternal, errMsg) -template assertNotNil*(e): untyped = - if e == nil: internalError($instantiationInfo()) +template assertNotNil*(conf: ConfigRef; e): untyped = + if e == nil: internalError(conf, $instantiationInfo()) e -template internalAssert*(e: bool) = - if not e: internalError($instantiationInfo()) +template internalAssert*(conf: ConfigRef, e: bool) = + if not e: internalError(conf, $instantiationInfo()) -proc addSourceLine*(fileIdx: int32, line: string) = - fileInfos[fileIdx].lines.add line.rope +proc addSourceLine*(fileIdx: FileIndex, line: string) = + fileInfos[fileIdx.int32].lines.add line.rope -proc sourceLine*(i: TLineInfo): Rope = - if i.fileIndex < 0: return nil +proc sourceLine*(conf: ConfigRef; i: TLineInfo): Rope = + if i.fileIndex.int32 < 0: return nil - if not optPreserveOrigSource and fileInfos[i.fileIndex].lines.len == 0: + if not optPreserveOrigSource(conf) and fileInfos[i.fileIndex.int32].lines.len == 0: try: for line in lines(i.toFullPath): addSourceLine i.fileIndex, line.string except IOError: discard - internalAssert i.fileIndex < fileInfos.len + assert i.fileIndex.int32 < fileInfos.len # can happen if the error points to EOF: - if i.line > fileInfos[i.fileIndex].lines.len: return nil + if i.line.int > fileInfos[i.fileIndex.int32].lines.len: return nil - result = fileInfos[i.fileIndex].lines[i.line-1] + result = fileInfos[i.fileIndex.int32].lines[i.line.int-1] -proc quotedFilename*(i: TLineInfo): Rope = - internalAssert i.fileIndex >= 0 - if optExcessiveStackTrace in gGlobalOptions: - result = fileInfos[i.fileIndex].quotedFullName +proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope = + assert i.fileIndex.int32 >= 0 + if optExcessiveStackTrace in conf.globalOptions: + result = fileInfos[i.fileIndex.int32].quotedFullName else: - result = fileInfos[i.fileIndex].quotedName + result = fileInfos[i.fileIndex.int32].quotedName ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) = case err of rInvalidFormatStr: - internalError("ropes: invalid format string: " & msg) + internalError(newPartialConfigRef(), "ropes: invalid format string: " & msg) of rCannotOpenFile: - rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg) + rawMessage(newPartialConfigRef(), if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg) -proc listWarnings*() = - msgWriteln("Warnings:") +proc listWarnings*(conf: ConfigRef) = + msgWriteln(conf, "Warnings:") for warn in warnMin..warnMax: - msgWriteln(" [$1] $2" % [ - if warn in gNotes: "x" else: " ", - msgs.WarningsToStr[ord(warn) - ord(warnMin)] + msgWriteln(conf, " [$1] $2" % [ + if warn in conf.notes: "x" else: " ", + configuration.WarningsToStr[ord(warn) - ord(warnMin)] ]) -proc listHints*() = - msgWriteln("Hints:") +proc listHints*(conf: ConfigRef) = + msgWriteln(conf, "Hints:") for hint in hintMin..hintMax: - msgWriteln(" [$1] $2" % [ - if hint in gNotes: "x" else: " ", - msgs.HintsToStr[ord(hint) - ord(hintMin)] + msgWriteln(conf, " [$1] $2" % [ + if hint in conf.notes: "x" else: " ", + configuration.HintsToStr[ord(hint) - ord(hintMin)] ]) - -# enable colors by default on terminals -if terminal.isatty(stderr): - incl(gGlobalOptions, optUseColors) diff --git a/compiler/nim.nim b/compiler/nim.nim index 89225a5e0..d3e00017f 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -21,7 +21,7 @@ when defined(i386) and defined(windows) and defined(vcc): import commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes, extccomp, strutils, os, osproc, platform, main, parseopt, service, - nodejs, scriptconfig, idents, modulegraphs + nodejs, scriptconfig, idents, modulegraphs, configuration when hasTinyCBackend: import tccgen @@ -37,77 +37,70 @@ proc prependCurDir(f: string): string = else: result = f -proc handleCmdLine(cache: IdentCache; config: ConfigRef) = +proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = + condsyms.initDefines(conf.symbols) if paramCount() == 0: - writeCommandLineUsage() + writeCommandLineUsage(conf, conf.helpWritten) else: # Process command line arguments: - processCmdLine(passCmd1, "") - if gProjectName == "-": - gProjectName = "stdinfile" - gProjectFull = "stdinfile" - gProjectPath = canonicalizePath getCurrentDir() - gProjectIsStdin = true - elif gProjectName != "": + processCmdLine(passCmd1, "", conf) + if conf.projectName == "-": + conf.projectName = "stdinfile" + conf.projectFull = "stdinfile" + conf.projectPath = canonicalizePath(conf, getCurrentDir()) + conf.projectIsStdin = true + elif conf.projectName != "": try: - gProjectFull = canonicalizePath(gProjectName) + conf.projectFull = canonicalizePath(conf, conf.projectName) except OSError: - gProjectFull = gProjectName - let p = splitFile(gProjectFull) + conf.projectFull = conf.projectName + let p = splitFile(conf.projectFull) let dir = if p.dir.len > 0: p.dir else: getCurrentDir() - gProjectPath = canonicalizePath dir - gProjectName = p.name + conf.projectPath = canonicalizePath(conf, dir) + conf.projectName = p.name else: - gProjectPath = canonicalizePath getCurrentDir() - loadConfigs(DefaultConfig, config) # load all config files - let scriptFile = gProjectFull.changeFileExt("nims") + conf.projectPath = canonicalizePath(conf, getCurrentDir()) + loadConfigs(DefaultConfig, conf) # load all config files + let scriptFile = conf.projectFull.changeFileExt("nims") if fileExists(scriptFile): - runNimScript(cache, scriptFile, freshDefines=false, config) + runNimScript(cache, scriptFile, freshDefines=false, conf) # 'nim foo.nims' means to just run the NimScript file and do nothing more: - if scriptFile == gProjectFull: return - elif fileExists(gProjectPath / "config.nims"): + if scriptFile == conf.projectFull: return + elif fileExists(conf.projectPath / "config.nims"): # directory wide NimScript file - runNimScript(cache, gProjectPath / "config.nims", freshDefines=false, config) + runNimScript(cache, conf.projectPath / "config.nims", freshDefines=false, conf) # now process command line arguments again, because some options in the # command line can overwite the config file's settings - extccomp.initVars() - processCmdLine(passCmd2, "") - if options.command == "": - rawMessage(errNoCommand, command) - mainCommand(newModuleGraph(config), cache) - if optHints in gOptions and hintGCStats in gNotes: echo(GC_getStatistics()) + extccomp.initVars(conf) + processCmdLine(passCmd2, "", conf) + if conf.command == "": + rawMessage(conf, errGenerated, "command missing") + mainCommand(newModuleGraph(conf), cache) + if optHints in conf.options and hintGCStats in conf.notes: echo(GC_getStatistics()) #echo(GC_getStatistics()) - if msgs.gErrorCounter == 0: + if conf.errorCounter == 0: when hasTinyCBackend: - if gCmd == cmdRun: - tccgen.run(commands.arguments) - if optRun in gGlobalOptions: - if gCmd == cmdCompileToJS: + if conf.cmd == cmdRun: + tccgen.run(conf.arguments) + if optRun in conf.globalOptions: + if conf.cmd == cmdCompileToJS: var ex: string - if options.outFile.len > 0: - ex = options.outFile.prependCurDir.quoteShell + if conf.outFile.len > 0: + ex = conf.outFile.prependCurDir.quoteShell else: ex = quoteShell( - completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir)) - execExternalProgram(findNodeJs() & " " & ex & ' ' & commands.arguments) - elif gCmd == cmdCompileToPHP: - var ex: string - if options.outFile.len > 0: - ex = options.outFile.prependCurDir.quoteShell - else: - ex = quoteShell( - completeCFilePath(changeFileExt(gProjectFull, "php").prependCurDir)) - execExternalProgram("php " & ex & ' ' & commands.arguments) + completeCFilePath(conf, changeFileExt(conf.projectFull, "js").prependCurDir)) + execExternalProgram(conf, findNodeJs() & " " & ex & ' ' & conf.arguments) else: var binPath: string - if options.outFile.len > 0: + if conf.outFile.len > 0: # If the user specified an outFile path, use that directly. - binPath = options.outFile.prependCurDir + binPath = conf.outFile.prependCurDir else: # Figure out ourselves a valid binary name. - binPath = changeFileExt(gProjectFull, ExeExt).prependCurDir + binPath = changeFileExt(conf.projectFull, ExeExt).prependCurDir var ex = quoteShell(binPath) - execExternalProgram(ex & ' ' & commands.arguments) + execExternalProgram(conf, ex & ' ' & conf.arguments) when declared(GC_setMaxPause): GC_setMaxPause 2_000 @@ -115,10 +108,10 @@ when declared(GC_setMaxPause): when compileOption("gc", "v2") or compileOption("gc", "refc"): # the new correct mark&sweet collector is too slow :-/ GC_disableMarkAndSweep() -condsyms.initDefines() when not defined(selftest): - handleCmdLine(newIdentCache(), newConfigRef()) + let conf = newConfigRef() + handleCmdLine(newIdentCache(), conf) when declared(GC_setMaxPause): echo GC_getStatistics() - msgQuit(int8(msgs.gErrorCounter > 0)) + msgQuit(int8(conf.errorCounter > 0)) diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim index 0f9e03352..8305b01f5 100644 --- a/compiler/nimblecmd.nim +++ b/compiler/nimblecmd.nim @@ -9,11 +9,12 @@ ## Implements some helper procs for Nimble (Nim's package manager) support. -import parseutils, strutils, strtabs, os, options, msgs, sequtils +import parseutils, strutils, strtabs, os, options, msgs, sequtils, + configuration -proc addPath*(path: string, info: TLineInfo) = - if not options.searchPaths.contains(path): - options.searchPaths.insert(path, 0) +proc addPath*(conf: ConfigRef; path: string, info: TLineInfo) = + if not conf.searchPaths.contains(path): + conf.searchPaths.insert(path, 0) type Version* = distinct string @@ -84,7 +85,7 @@ proc getPathVersion*(p: string): tuple[name, version: string] = result.name = p[0 .. sepIdx - 1] result.version = p.substr(sepIdx + 1) -proc addPackage(packages: StringTableRef, p: string; info: TLineInfo) = +proc addPackage(conf: ConfigRef; packages: StringTableRef, p: string; info: TLineInfo) = let (name, ver) = getPathVersion(p) if isValidVersion(ver): let version = newVersion(ver) @@ -92,14 +93,14 @@ proc addPackage(packages: StringTableRef, p: string; info: TLineInfo) = (not packages.hasKey(name)): packages[name] = $version else: - localError(info, "invalid package name: " & p) + localError(conf, info, "invalid package name: " & p) iterator chosen(packages: StringTableRef): string = for key, val in pairs(packages): let res = if val.len == 0: key else: key & '-' & val yield res -proc addNimblePath(p: string, info: TLineInfo) = +proc addNimblePath(conf: ConfigRef; p: string, info: TLineInfo) = var path = p let nimbleLinks = toSeq(walkPattern(p / "*.nimble-link")) if nimbleLinks.len > 0: @@ -111,23 +112,23 @@ proc addNimblePath(p: string, info: TLineInfo) = if not path.isAbsolute(): path = p / path - if not contains(options.searchPaths, path): - message(info, hintPath, path) - options.lazyPaths.insert(path, 0) + if not contains(conf.searchPaths, path): + message(conf, info, hintPath, path) + conf.lazyPaths.insert(path, 0) -proc addPathRec(dir: string, info: TLineInfo) = +proc addPathRec(conf: ConfigRef; dir: string, info: TLineInfo) = var packages = newStringTable(modeStyleInsensitive) var pos = dir.len-1 if dir[pos] in {DirSep, AltSep}: inc(pos) for k,p in os.walkDir(dir): if k == pcDir and p[pos] != '.': - addPackage(packages, p, info) + addPackage(conf, packages, p, info) for p in packages.chosen: - addNimblePath(p, info) + addNimblePath(conf, p, info) -proc nimblePath*(path: string, info: TLineInfo) = - addPathRec(path, info) - addNimblePath(path, info) +proc nimblePath*(conf: ConfigRef; path: string, info: TLineInfo) = + addPathRec(conf, path, info) + addNimblePath(conf, path, info) when isMainModule: proc v(s: string): Version = s.newVersion diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index c19b41af1..a455b4a44 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -11,7 +11,7 @@ import llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer, - options, idents, wordrecg, strtabs + options, idents, wordrecg, strtabs, configuration # ---------------- configuration file parser ----------------------------- # we use Nim's scanner here to save space and work @@ -27,12 +27,12 @@ proc parseAtom(L: var TLexer, tok: var TToken; config: ConfigRef): bool = ppGetTok(L, tok) result = parseExpr(L, tok, config) if tok.tokType == tkParRi: ppGetTok(L, tok) - else: lexMessage(L, errTokenExpected, "\')\'") + else: lexMessage(L, errGenerated, "expected closing ')'") elif tok.ident.id == ord(wNot): ppGetTok(L, tok) result = not parseAtom(L, tok, config) else: - result = isDefined(tok.ident) + result = isDefined(config, tok.ident.s) ppGetTok(L, tok) proc parseAndExpr(L: var TLexer, tok: var TToken; config: ConfigRef): bool = @@ -53,12 +53,12 @@ proc evalppIf(L: var TLexer, tok: var TToken; config: ConfigRef): bool = ppGetTok(L, tok) # skip 'if' or 'elif' result = parseExpr(L, tok, config) if tok.tokType == tkColon: ppGetTok(L, tok) - else: lexMessage(L, errTokenExpected, "\':\'") + else: lexMessage(L, errGenerated, "expected ':'") -var condStack: seq[bool] = @[] +#var condStack: seq[bool] = @[] -proc doEnd(L: var TLexer, tok: var TToken) = - if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if") +proc doEnd(L: var TLexer, tok: var TToken; condStack: var seq[bool]) = + if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if") ppGetTok(L, tok) # skip 'end' setLen(condStack, high(condStack)) @@ -66,20 +66,22 @@ type TJumpDest = enum jdEndif, jdElseEndif -proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef) -proc doElse(L: var TLexer, tok: var TToken; config: ConfigRef) = - if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if") +proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef; + condStack: var seq[bool]) +proc doElse(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) = + if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if") ppGetTok(L, tok) if tok.tokType == tkColon: ppGetTok(L, tok) - if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif, config) + if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif, config, condStack) -proc doElif(L: var TLexer, tok: var TToken; config: ConfigRef) = - if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if") +proc doElif(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) = + if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if") var res = evalppIf(L, tok, config) - if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif, config) + if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif, config, condStack) else: condStack[high(condStack)] = true -proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef) = +proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef; + condStack: var seq[bool]) = var nestedIfs = 0 while true: if tok.ident != nil and tok.ident.s == "@": @@ -89,39 +91,39 @@ proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: Co inc(nestedIfs) of wElse: if dest == jdElseEndif and nestedIfs == 0: - doElse(L, tok, config) + doElse(L, tok, config, condStack) break of wElif: if dest == jdElseEndif and nestedIfs == 0: - doElif(L, tok, config) + doElif(L, tok, config, condStack) break of wEnd: if nestedIfs == 0: - doEnd(L, tok) + doEnd(L, tok, condStack) break if nestedIfs > 0: dec(nestedIfs) else: discard ppGetTok(L, tok) elif tok.tokType == tkEof: - lexMessage(L, errTokenExpected, "@end") + lexMessage(L, errGenerated, "expected @end") else: ppGetTok(L, tok) -proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef) = +proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) = ppGetTok(L, tok) # skip @ case whichKeyword(tok.ident) of wIf: setLen(condStack, len(condStack) + 1) let res = evalppIf(L, tok, config) condStack[high(condStack)] = res - if not res: jumpToDirective(L, tok, jdElseEndif, config) - of wElif: doElif(L, tok, config) - of wElse: doElse(L, tok, config) - of wEnd: doEnd(L, tok) + if not res: jumpToDirective(L, tok, jdElseEndif, config, condStack) + of wElif: doElif(L, tok, config, condStack) + of wElse: doElse(L, tok, config, condStack) + of wEnd: doEnd(L, tok, condStack) of wWrite: ppGetTok(L, tok) - msgs.msgWriteln(strtabs.`%`(tokToStr(tok), options.gConfigVars, + msgs.msgWriteln(config, strtabs.`%`(tokToStr(tok), config.configVars, {useEnvironment, useKey})) ppGetTok(L, tok) else: @@ -144,60 +146,63 @@ proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef) = ppGetTok(L, tok) os.putEnv(key, os.getEnv(key) & tokToStr(tok)) ppGetTok(L, tok) - else: lexMessage(L, errInvalidDirectiveX, tokToStr(tok)) + else: + lexMessage(L, errGenerated, "invalid directive: '$1'" % tokToStr(tok)) -proc confTok(L: var TLexer, tok: var TToken; config: ConfigRef) = +proc confTok(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) = ppGetTok(L, tok) while tok.ident != nil and tok.ident.s == "@": - parseDirective(L, tok, config) # else: give the token to the parser + parseDirective(L, tok, config, condStack) # else: give the token to the parser proc checkSymbol(L: TLexer, tok: TToken) = if tok.tokType notin {tkSymbol..tkInt64Lit, tkStrLit..tkTripleStrLit}: - lexMessage(L, errIdentifierExpected, tokToStr(tok)) + lexMessage(L, errGenerated, "expected identifier, but got: " & tokToStr(tok)) -proc parseAssignment(L: var TLexer, tok: var TToken; config: ConfigRef) = +proc parseAssignment(L: var TLexer, tok: var TToken; + config: ConfigRef; condStack: var seq[bool]) = if tok.ident.s == "-" or tok.ident.s == "--": - confTok(L, tok, config) # skip unnecessary prefix + confTok(L, tok, config, condStack) # skip unnecessary prefix var info = getLineInfo(L, tok) # save for later in case of an error checkSymbol(L, tok) var s = tokToStr(tok) - confTok(L, tok, config) # skip symbol + confTok(L, tok, config, condStack) # skip symbol var val = "" while tok.tokType == tkDot: add(s, '.') - confTok(L, tok, config) + confTok(L, tok, config, condStack) checkSymbol(L, tok) add(s, tokToStr(tok)) - confTok(L, tok, config) + confTok(L, tok, config, condStack) if tok.tokType == tkBracketLe: # BUGFIX: val, not s! # BUGFIX: do not copy '['! - confTok(L, tok, config) + confTok(L, tok, config, condStack) checkSymbol(L, tok) add(val, tokToStr(tok)) - confTok(L, tok, config) - if tok.tokType == tkBracketRi: confTok(L, tok, config) - else: lexMessage(L, errTokenExpected, "']'") + confTok(L, tok, config, condStack) + if tok.tokType == tkBracketRi: confTok(L, tok, config, condStack) + else: lexMessage(L, errGenerated, "expected closing ']'") add(val, ']') let percent = tok.ident != nil and tok.ident.s == "%=" if tok.tokType in {tkColon, tkEquals} or percent: if len(val) > 0: add(val, ':') - confTok(L, tok, config) # skip ':' or '=' or '%' + confTok(L, tok, config, condStack) # skip ':' or '=' or '%' checkSymbol(L, tok) add(val, tokToStr(tok)) - confTok(L, tok, config) # skip symbol + confTok(L, tok, config, condStack) # skip symbol while tok.ident != nil and tok.ident.s == "&": - confTok(L, tok, config) + confTok(L, tok, config, condStack) checkSymbol(L, tok) add(val, tokToStr(tok)) - confTok(L, tok, config) + confTok(L, tok, config, condStack) if percent: - processSwitch(s, strtabs.`%`(val, options.gConfigVars, + processSwitch(s, strtabs.`%`(val, config.configVars, {useEnvironment, useEmpty}), passPP, info, config) else: processSwitch(s, val, passPP, info, config) -proc readConfigFile(filename: string; cache: IdentCache; config: ConfigRef) = +proc readConfigFile( + filename: string; cache: IdentCache; config: ConfigRef): bool = var L: TLexer tok: TToken @@ -205,54 +210,61 @@ proc readConfigFile(filename: string; cache: IdentCache; config: ConfigRef) = stream = llStreamOpen(filename, fmRead) if stream != nil: initToken(tok) - openLexer(L, filename, stream, cache) + openLexer(L, filename, stream, cache, config) tok.tokType = tkEof # to avoid a pointless warning - confTok(L, tok, config) # read in the first token - while tok.tokType != tkEof: parseAssignment(L, tok, config) - if len(condStack) > 0: lexMessage(L, errTokenExpected, "@end") + var condStack: seq[bool] = @[] + confTok(L, tok, config, condStack) # read in the first token + while tok.tokType != tkEof: parseAssignment(L, tok, config, condStack) + if len(condStack) > 0: lexMessage(L, errGenerated, "expected @end") closeLexer(L) - rawMessage(hintConf, filename) + return true proc getUserConfigPath(filename: string): string = result = joinPath(getConfigDir(), filename) -proc getSystemConfigPath(filename: string): string = +proc getSystemConfigPath(conf: ConfigRef; filename: string): string = # try standard configuration file (installation did not distribute files # the UNIX way) - let p = getPrefixDir() + let p = getPrefixDir(conf) result = joinPath([p, "config", filename]) when defined(unix): if not existsFile(result): result = joinPath([p, "etc", filename]) if not existsFile(result): result = "/etc/" & filename -proc loadConfigs*(cfg: string; cache: IdentCache; config: ConfigRef = nil) = - setDefaultLibpath() +proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) = + setDefaultLibpath(conf) + + var configFiles = newSeq[string]() + + template readConfigFile(path: string) = + let configPath = path + if readConfigFile(configPath, cache, conf): + add(configFiles, configPath) - if optSkipConfigFile notin gGlobalOptions: - readConfigFile(getSystemConfigPath(cfg), cache, config) + if optSkipConfigFile notin conf.globalOptions: + readConfigFile(getSystemConfigPath(conf, cfg)) - if optSkipUserConfigFile notin gGlobalOptions: - readConfigFile(getUserConfigPath(cfg), cache, config) + if optSkipUserConfigFile notin conf.globalOptions: + readConfigFile(getUserConfigPath(cfg)) - var pd = if gProjectPath.len > 0: gProjectPath else: getCurrentDir() - if optSkipParentConfigFiles notin gGlobalOptions: + let pd = if conf.projectPath.len > 0: conf.projectPath else: getCurrentDir() + if optSkipParentConfigFiles notin conf.globalOptions: for dir in parentDirs(pd, fromRoot=true, inclusive=false): - readConfigFile(dir / cfg, cache, config) + readConfigFile(dir / cfg) - if optSkipProjConfigFile notin gGlobalOptions: - readConfigFile(pd / cfg, cache, config) + if optSkipProjConfigFile notin conf.globalOptions: + readConfigFile(pd / cfg) - if gProjectName.len != 0: + if conf.projectName.len != 0: # new project wide config file: - var projectConfig = changeFileExt(gProjectFull, "nimcfg") + var projectConfig = changeFileExt(conf.projectFull, "nimcfg") if not fileExists(projectConfig): - projectConfig = changeFileExt(gProjectFull, "nim.cfg") - if not fileExists(projectConfig): - projectConfig = changeFileExt(gProjectFull, "nimrod.cfg") - if fileExists(projectConfig): - rawMessage(warnDeprecated, projectConfig) - readConfigFile(projectConfig, cache, config) + projectConfig = changeFileExt(conf.projectFull, "nim.cfg") + readConfigFile(projectConfig) + + for filename in configFiles: + rawMessage(conf, hintConf, filename) -proc loadConfigs*(cfg: string; config: ConfigRef = nil) = +proc loadConfigs*(cfg: string; conf: ConfigRef) = # for backwards compatibility only. - loadConfigs(cfg, newIdentCache(), config) + loadConfigs(cfg, newIdentCache(), conf) diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index aca03fc16..ff91861d0 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -20,6 +20,7 @@ proc execute*(program: string) = initDefines() defineSymbol("nimrodvm") + defineSymbol("nimscript") when hasFFI: defineSymbol("nimffi") registerPass(verbosePass) registerPass(semPass) diff --git a/compiler/nimfix/nimfix.nim b/compiler/nimfix/nimfix.nim index a97d88078..c3e29f9a1 100644 --- a/compiler/nimfix/nimfix.nim +++ b/compiler/nimfix/nimfix.nim @@ -38,7 +38,7 @@ In addition, all command line options of Nim are supported. proc mainCommand = registerPass verbosePass registerPass semPass - gCmd = cmdPretty + conf.cmd = cmdPretty searchPaths.add options.libpath if gProjectFull.len != 0: # current path is always looked first for modules @@ -47,7 +47,7 @@ proc mainCommand = compileProject(newModuleGraph(), newIdentCache()) pretty.overwriteFiles() -proc processCmdLine*(pass: TCmdLinePass, cmd: string) = +proc processCmdLine*(pass: TCmdLinePass, cmd: string, config: ConfigRef) = var p = parseopt.initOptParser(cmd) var argsCount = 0 gOnlyMainfile = true @@ -76,16 +76,16 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) = of "wholeproject": gOnlyMainfile = false of "besteffort": msgs.gErrorMax = high(int) # don't stop after first error else: - processSwitch(pass, p) + processSwitch(pass, p, config) of cmdArgument: options.gProjectName = unixToNativePath(p.key) # if processArgument(pass, p, argsCount): break -proc handleCmdLine() = +proc handleCmdLine(config: ConfigRef) = if paramCount() == 0: stdout.writeLine(Usage) else: - processCmdLine(passCmd1, "") + processCmdLine(passCmd1, "", config) if gProjectName != "": try: gProjectFull = canonicalizePath(gProjectName) @@ -96,11 +96,11 @@ proc handleCmdLine() = gProjectName = p.name else: gProjectPath = getCurrentDir() - loadConfigs(DefaultConfig) # load all config files + loadConfigs(DefaultConfig, config) # 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, "") + processCmdLine(passCmd2, "", config) mainCommand() when compileOption("gc", "v2") or compileOption("gc", "refc"): @@ -108,4 +108,4 @@ when compileOption("gc", "v2") or compileOption("gc", "refc"): condsyms.initDefines() defineSymbol "nimfix" -handleCmdline() +handleCmdline newConfigRef() diff --git a/compiler/nimfix/pretty.nim b/compiler/nimfix/pretty.nim index 8ba922927..96429ad53 100644 --- a/compiler/nimfix/pretty.nim +++ b/compiler/nimfix/pretty.nim @@ -13,8 +13,9 @@ import strutils, os, intsets, strtabs -import compiler/options, compiler/ast, compiler/astalgo, compiler/msgs, - compiler/semdata, compiler/nimfix/prettybase, compiler/ropes, compiler/idents +import ".." / [options, ast, astalgo, msgs, semdata, ropes, idents, + configuration] +import prettybase type StyleCheck* {.pure.} = enum None, Warn, Auto @@ -24,11 +25,11 @@ var gStyleCheck*: StyleCheck gCheckExtern*, gOnlyMainfile*: bool -proc overwriteFiles*() = - let doStrip = options.getConfigVar("pretty.strip").normalize == "on" +proc overwriteFiles*(conf: ConfigRef) = + let doStrip = options.getConfigVar(conf, "pretty.strip").normalize == "on" for i in 0 .. high(gSourceFiles): if gSourceFiles[i].dirty and not gSourceFiles[i].isNimfixFile and - (not gOnlyMainfile or gSourceFiles[i].fileIdx == gProjectMainIdx): + (not gOnlyMainfile or gSourceFiles[i].fileIdx == conf.projectMainIdx.FileIndex): let newFile = if gOverWrite: gSourceFiles[i].fullpath else: gSourceFiles[i].fullpath.changeFileExt(".pretty.nim") try: @@ -41,7 +42,7 @@ proc overwriteFiles*() = f.write(gSourceFiles[i].newline) f.close except IOError: - rawMessage(errCannotOpenFile, newFile) + rawMessage(conf, errGenerated, "cannot open file: " & newFile) proc `=~`(s: string, a: openArray[string]): bool = for x in a: @@ -95,7 +96,7 @@ proc beautifyName(s: string, k: TSymKind): string = proc replaceInFile(info: TLineInfo; newName: string) = loadFile(info) - let line = gSourceFiles[info.fileIndex].lines[info.line-1] + let line = gSourceFiles[info.fileIndex.int].lines[info.line.int-1] var first = min(info.col.int, line.len) if first < 0: return #inc first, skipIgnoreCase(line, "proc ", first) @@ -107,28 +108,28 @@ proc replaceInFile(info: TLineInfo; newName: string) = if differ(line, first, last, newName): # last-first+1 != newName.len or var x = line.substr(0, first-1) & newName & line.substr(last+1) - system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x) - gSourceFiles[info.fileIndex].dirty = true + system.shallowCopy(gSourceFiles[info.fileIndex.int].lines[info.line.int-1], x) + gSourceFiles[info.fileIndex.int].dirty = true -proc checkStyle(info: TLineInfo, s: string, k: TSymKind; sym: PSym) = +proc checkStyle(conf: ConfigRef; info: TLineInfo, s: string, k: TSymKind; sym: PSym) = let beau = beautifyName(s, k) if s != beau: if gStyleCheck == StyleCheck.Auto: sym.name = getIdent(beau) replaceInFile(info, beau) else: - message(info, hintName, beau) + message(conf, info, hintName, beau) -proc styleCheckDefImpl(info: TLineInfo; s: PSym; k: TSymKind) = +proc styleCheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) = # operators stay as they are: if k in {skResult, skTemp} or s.name.s[0] notin prettybase.Letters: return if k in {skType, skGenericParam} and sfAnon in s.flags: return if {sfImportc, sfExportc} * s.flags == {} or gCheckExtern: - checkStyle(info, s.name.s, k, s) + checkStyle(conf, info, s.name.s, k, s) template styleCheckDef*(info: TLineInfo; s: PSym; k: TSymKind) = when defined(nimfix): - if gStyleCheck != StyleCheck.None: styleCheckDefImpl(info, s, k) + if gStyleCheck != StyleCheck.None: styleCheckDefImpl(conf, info, s, k) template styleCheckDef*(info: TLineInfo; s: PSym) = styleCheckDef(info, s, s.kind) @@ -136,7 +137,7 @@ template styleCheckDef*(s: PSym) = styleCheckDef(s.info, s, s.kind) proc styleCheckUseImpl(info: TLineInfo; s: PSym) = - if info.fileIndex < 0: return + if info.fileIndex.int < 0: return # we simply convert it to what it looks like in the definition # for consistency @@ -151,4 +152,4 @@ proc styleCheckUseImpl(info: TLineInfo; s: PSym) = template styleCheckUse*(info: TLineInfo; s: PSym) = when defined(nimfix): - if gStyleCheck != StyleCheck.None: styleCheckUseImpl(info, s) + if gStyleCheck != StyleCheck.None: styleCheckUseImpl(conf, info, s) diff --git a/compiler/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim index 0f17cbcb1..c32dbe623 100644 --- a/compiler/nimfix/prettybase.nim +++ b/compiler/nimfix/prettybase.nim @@ -8,7 +8,7 @@ # import strutils, lexbase, streams -import compiler/ast, compiler/msgs, compiler/idents +import ".." / [ast, msgs, idents] from os import splitFile type @@ -16,13 +16,13 @@ type lines*: seq[string] dirty*, isNimfixFile*: bool fullpath*, newline*: string - fileIdx*: int32 + fileIdx*: FileIndex var gSourceFiles*: seq[TSourceFile] = @[] proc loadFile*(info: TLineInfo) = - let i = info.fileIndex + let i = info.fileIndex.int if i >= gSourceFiles.len: gSourceFiles.setLen(i+1) if gSourceFiles[i].lines.isNil: @@ -64,7 +64,7 @@ proc differ*(line: string, a, b: int, x: string): bool = proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) = loadFile(info) - let line = gSourceFiles[info.fileIndex].lines[info.line-1] + let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1] var first = min(info.col.int, line.len) if first < 0: return #inc first, skipIgnoreCase(line, "proc ", first) @@ -75,8 +75,8 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) = let last = first+identLen(line, first)-1 if cmpIgnoreStyle(line[first..last], oldSym.s) == 0: var x = line.substr(0, first-1) & newSym.s & line.substr(last+1) - system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x) - gSourceFiles[info.fileIndex].dirty = true + system.shallowCopy(gSourceFiles[info.fileIndex.int32].lines[info.line.int-1], x) + gSourceFiles[info.fileIndex.int32].dirty = true #if newSym.s == "File": writeStackTrace() proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PSym) = @@ -85,10 +85,10 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PSym) = proc replaceComment*(info: TLineInfo) = loadFile(info) - let line = gSourceFiles[info.fileIndex].lines[info.line-1] + let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1] var first = info.col.int if line[first] != '#': inc first var x = line.substr(0, first-1) & "discard " & line.substr(first+1).escape - system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x) - gSourceFiles[info.fileIndex].dirty = true + system.shallowCopy(gSourceFiles[info.fileIndex.int32].lines[info.line.int-1], x) + gSourceFiles[info.fileIndex.int32].dirty = true diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim index 94507adf0..6cb675ed8 100644 --- a/compiler/nimsets.nim +++ b/compiler/nimsets.nim @@ -12,27 +12,10 @@ import ast, astalgo, trees, nversion, msgs, platform, bitsets, types, renderer -proc toBitSet*(s: PNode, b: var TBitSet) - # this function is used for case statement checking: -proc overlap*(a, b: PNode): bool -proc inSet*(s: PNode, elem: PNode): bool -proc someInSet*(s: PNode, a, b: PNode): bool -proc emptyRange*(a, b: PNode): bool -proc setHasRange*(s: PNode): bool - # returns true if set contains a range (needed by the code generator) - # these are used for constant folding: -proc unionSets*(a, b: PNode): PNode -proc diffSets*(a, b: PNode): PNode -proc intersectSets*(a, b: PNode): PNode -proc symdiffSets*(a, b: PNode): PNode -proc containsSets*(a, b: PNode): bool -proc equalSets*(a, b: PNode): bool -proc cardSet*(s: PNode): BiggestInt -# implementation - -proc inSet(s: PNode, elem: PNode): bool = +proc inSet*(s: PNode, elem: PNode): bool = + assert s.kind == nkCurly if s.kind != nkCurly: - internalError(s.info, "inSet") + #internalError(s.info, "inSet") return false for i in countup(0, sonsLen(s) - 1): if s.sons[i].kind == nkRange: @@ -44,7 +27,7 @@ proc inSet(s: PNode, elem: PNode): bool = return true result = false -proc overlap(a, b: PNode): bool = +proc overlap*(a, b: PNode): bool = if a.kind == nkRange: if b.kind == nkRange: # X..Y and C..D overlap iff (X <= D and C <= Y) @@ -58,10 +41,11 @@ proc overlap(a, b: PNode): bool = else: result = sameValue(a, b) -proc someInSet(s: PNode, a, b: PNode): bool = +proc someInSet*(s: PNode, a, b: PNode): bool = # checks if some element of a..b is in the set s + assert s.kind == nkCurly if s.kind != nkCurly: - internalError(s.info, "SomeInSet") + #internalError(s.info, "SomeInSet") return false for i in countup(0, sonsLen(s) - 1): if s.sons[i].kind == nkRange: @@ -74,7 +58,7 @@ proc someInSet(s: PNode, a, b: PNode): bool = return true result = false -proc toBitSet(s: PNode, b: var TBitSet) = +proc toBitSet*(s: PNode, b: var TBitSet) = var first, j: BiggestInt first = firstOrd(s.typ.sons[0]) bitSetInit(b, int(getSize(s.typ))) @@ -87,7 +71,7 @@ proc toBitSet(s: PNode, b: var TBitSet) = else: bitSetIncl(b, getOrdValue(s.sons[i]) - first) -proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode = +proc toTreeSet*(s: TBitSet, settype: PType, info: TLineInfo): PNode = var a, b, e, first: BiggestInt # a, b are interval borders elemType: PType @@ -128,18 +112,18 @@ template nodeSetOp(a, b: PNode, op: untyped) {.dirty.} = op(x, y) result = toTreeSet(x, a.typ, a.info) -proc unionSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetUnion) -proc diffSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetDiff) -proc intersectSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetIntersect) -proc symdiffSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff) +proc unionSets*(a, b: PNode): PNode = nodeSetOp(a, b, bitSetUnion) +proc diffSets*(a, b: PNode): PNode = nodeSetOp(a, b, bitSetDiff) +proc intersectSets*(a, b: PNode): PNode = nodeSetOp(a, b, bitSetIntersect) +proc symdiffSets*(a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff) -proc containsSets(a, b: PNode): bool = +proc containsSets*(a, b: PNode): bool = var x, y: TBitSet toBitSet(a, x) toBitSet(b, y) result = bitSetContains(x, y) -proc equalSets(a, b: PNode): bool = +proc equalSets*(a, b: PNode): bool = var x, y: TBitSet toBitSet(a, x) toBitSet(b, y) @@ -151,26 +135,24 @@ proc complement*(a: PNode): PNode = for i in countup(0, high(x)): x[i] = not x[i] result = toTreeSet(x, a.typ, a.info) -proc cardSet(s: PNode): BiggestInt = - # here we can do better than converting it into a compact set - # we just count the elements directly - result = 0 - for i in countup(0, sonsLen(s) - 1): - if s.sons[i].kind == nkRange: - result = result + getOrdValue(s.sons[i].sons[1]) - - getOrdValue(s.sons[i].sons[0]) + 1 - else: - inc(result) +proc deduplicate*(a: PNode): PNode = + var x: TBitSet + toBitSet(a, x) + result = toTreeSet(x, a.typ, a.info) -proc setHasRange(s: PNode): bool = +proc cardSet*(a: PNode): BiggestInt = + var x: TBitSet + toBitSet(a, x) + result = bitSetCard(x) + +proc setHasRange*(s: PNode): bool = + assert s.kind == nkCurly if s.kind != nkCurly: - internalError(s.info, "SetHasRange") return false for i in countup(0, sonsLen(s) - 1): if s.sons[i].kind == nkRange: return true result = false -proc emptyRange(a, b: PNode): bool = +proc emptyRange*(a, b: PNode): bool = result = not leValue(a, b) # a > b iff not (a <= b) - diff --git a/compiler/nversion.nim b/compiler/nversion.nim index 85265a7c0..caa818d79 100644 --- a/compiler/nversion.nim +++ b/compiler/nversion.nim @@ -15,3 +15,6 @@ const VersionAsString* = system.NimVersion RodFileVersion* = "1223" # modify this if the rod-format changes! + NimCompilerApiVersion* = 1 ## Check for the existance of this before accessing it + ## as older versions of the compiler API do not + ## declare this. diff --git a/compiler/options.nim b/compiler/options.nim index 0732e4989..150e67a18 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -8,7 +8,9 @@ # import - os, strutils, strtabs, osproc, sets + os, strutils, strtabs, osproc, sets, configuration, platform + +from terminal import isatty const hasTinyCBackend* = defined(tinyc) @@ -17,14 +19,14 @@ const hasFFI* = defined(useFFI) newScopeForIf* = true useCaas* = not defined(noCaas) - noTimeMachine* = defined(avoidTimeMachine) and defined(macosx) + copyrightYear* = "2018" type # please make sure we have under 32 options # (improves code efficiency a lot!) TOption* = enum # **keep binary compatible** optNone, optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck, optOverflowCheck, optNilCheck, - optNaNCheck, optInfCheck, + optNaNCheck, optInfCheck, optMoveCheck, optAssert, optLineDir, optWarns, optHints, optOptimizeSpeed, optOptimizeSize, optStackTrace, # stack tracing support optLineTrace, # line tracing support (includes stack tracing) @@ -35,11 +37,14 @@ type # please make sure we have under 32 options optImplicitStatic, # optimization: implicit at compile time # evaluation optPatterns, # en/disable pattern matching - optMemTracker + optMemTracker, + optHotCodeReloading, + optLaxStrings TOptions* = set[TOption] TGlobalOption* = enum # **keep binary compatible** - gloptNone, optForceFullMake, optDeadCodeElim, + gloptNone, optForceFullMake, + optDeadCodeElimUnused, # deprecated, always on optListCmd, optCompileOnly, optNoLinking, optCDebug, # turn on debugging information optGenDynLib, # generate a dynamic library @@ -67,6 +72,11 @@ type # please make sure we have under 32 options optIdeTerse # idetools: use terse descriptions optNoCppExceptions # use C exception handling even with CPP optExcessiveStackTrace # fully qualified module filenames + optWholeProject # for 'doc2': output any dependency + optListFullPaths + optNoNimblePath + optDynlibOverrideAll + optUseNimNamespace TGlobalOptions* = set[TGlobalOption] @@ -79,7 +89,6 @@ type # **keep binary compatible** cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS, - cmdCompileToPHP, cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc, cmdGenDepend, cmdDump, cmdCheck, # semantic checking for whole project @@ -101,67 +110,187 @@ type ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod, ideHighlight, ideOutline, ideKnown, ideMsg - ConfigRef* = ref object ## eventually all global configuration should be moved here - cppDefines*: HashSet[string] - headerFile*: string + Feature* = enum ## experimental features + implicitDeref, + dotOperators, + callOperator, + parallel, + destructor, + notnil, + oldIterTransf -proc newConfigRef*(): ConfigRef = - result = ConfigRef(cppDefines: initSet[string](), - headerFile: "") + SymbolFilesOption* = enum + disabledSf, enabledSf, writeOnlySf, readOnlySf, v2Sf -proc cppDefine*(c: ConfigRef; define: string) = - c.cppDefines.incl define + ConfigRef* = ref object ## eventually all global configuration should be moved here + linesCompiled*: int # all lines that have been compiled + options*: TOptions + globalOptions*: TGlobalOptions + exitcode*: int8 + cmd*: TCommands # the command + selectedGC*: TGCMode # the selected GC + verbosity*: int # how verbose the compiler is + numberOfProcessors*: int # number of processors + evalExpr*: string # expression for idetools --eval + lastCmdTime*: float # when caas is enabled, we measure each command + symbolFiles*: SymbolFilesOption -var - gIdeCmd*: IdeCmd + cppDefines*: HashSet[string] + headerFile*: string + features*: set[Feature] + arguments*: string ## the arguments to be passed to the program that + ## should be run + helpWritten*: bool + ideCmd*: IdeCmd + oldNewlines*: bool + enableNotes*: TNoteKinds + disableNotes*: TNoteKinds + foreignPackageNotes*: TNoteKinds + notes*: TNoteKinds + mainPackageNotes*: TNoteKinds + errorCounter*: int + hintCounter*: int + warnCounter*: int + errorMax*: int + configVars*: StringTableRef + symbols*: StringTableRef ## We need to use a StringTableRef here as defined + ## symbols are always guaranteed to be style + ## insensitive. Otherwise hell would break lose. + packageCache*: StringTableRef + searchPaths*: seq[string] + lazyPaths*: seq[string] + outFile*, prefixDir*, libpath*, nimcacheDir*: string + dllOverrides, moduleOverrides*: StringTableRef + projectName*: string # holds a name like 'nim' + projectPath*: string # holds a path like /home/alice/projects/nim/compiler/ + projectFull*: string # projectPath/projectName + projectIsStdin*: bool # whether we're compiling from stdin + projectMainIdx*: int32 # the canonical path id of the main module + command*: string # the main command (e.g. cc, check, scan, etc) + commandArgs*: seq[string] # any arguments after the main command + keepComments*: bool # whether the parser needs to keep comments + implicitImports*: seq[string] # modules that are to be implicitly imported + implicitIncludes*: seq[string] # modules that are to be implicitly included + docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \ + # The string uses the formatting variables `path` and `line`. + +const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel} const ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck, - optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck} - -var - gOptions*: TOptions = {optObjCheck, optFieldCheck, optRangeCheck, - optBoundsCheck, optOverflowCheck, optAssert, optWarns, - optHints, optStackTrace, optLineTrace, - optPatterns, optNilCheck} - gGlobalOptions*: TGlobalOptions = {optThreadAnalysis} - gExitcode*: int8 - gCmd*: TCommands = cmdNone # the command - gSelectedGC* = gcRefc # the selected GC - searchPaths*: seq[string] = @[] - lazyPaths*: seq[string] = @[] - outFile*: string = "" - docSeeSrcUrl*: string = "" # if empty, no seeSrc will be generated. \ - # The string uses the formatting variables `path` and `line`. - #headerFile*: string = "" - gVerbosity* = 1 # how verbose the compiler is - gNumberOfProcessors*: int # number of processors - gWholeProject*: bool # for 'doc2': output any dependency - gEvalExpr* = "" # expression for idetools --eval - gLastCmdTime*: float # when caas is enabled, we measure each command - gListFullPaths*: bool - gPreciseStack*: bool = false - gNoNimblePath* = false - gExperimentalMode*: bool - newDestructors*: bool - gDynlibOverrideAll*: bool + optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck, + optMoveCheck} -type - SymbolFilesOption* = enum - disabledSf, enabledSf, writeOnlySf, readOnlySf + DefaultOptions* = {optObjCheck, optFieldCheck, optRangeCheck, + optBoundsCheck, optOverflowCheck, optAssert, optWarns, + optHints, optStackTrace, optLineTrace, + optPatterns, optNilCheck, optMoveCheck} + DefaultGlobalOptions* = {optThreadAnalysis} + +template newPackageCache*(): untyped = + newStringTable(when FileSystemCaseSensitive: + modeCaseInsensitive + else: + modeCaseSensitive) -var gSymbolFiles*: SymbolFilesOption +proc newConfigRef*(): ConfigRef = + result = ConfigRef( + selectedGC: gcRefc, + verbosity: 1, + options: DefaultOptions, + globalOptions: DefaultGlobalOptions, + evalExpr: "", + cppDefines: initSet[string](), + headerFile: "", features: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic, + hintQuitCalled, hintExecuting}, + notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1], + configVars: newStringTable(modeStyleInsensitive), + symbols: newStringTable(modeStyleInsensitive), + packageCache: newPackageCache(), + searchPaths: @[], + lazyPaths: @[], + outFile: "", prefixDir: "", libpath: "", nimcacheDir: "", + dllOverrides: newStringTable(modeCaseInsensitive), + moduleOverrides: newStringTable(modeStyleInsensitive), + projectName: "", # holds a name like 'nim' + projectPath: "", # holds a path like /home/alice/projects/nim/compiler/ + projectFull: "", # projectPath/projectName + projectIsStdin: false, # whether we're compiling from stdin + projectMainIdx: 0'i32, # the canonical path id of the main module + command: "", # the main command (e.g. cc, check, scan, etc) + commandArgs: @[], # any arguments after the main command + keepComments: true, # whether the parser needs to keep comments + implicitImports: @[], # modules that are to be implicitly imported + implicitIncludes: @[], # modules that are to be implicitly included + docSeeSrcUrl: "", + arguments: "" + ) + # enable colors by default on terminals + if terminal.isatty(stderr): + incl(result.globalOptions, optUseColors) + +proc newPartialConfigRef*(): ConfigRef = + ## create a new ConfigRef that is only good enough for error reporting. + result = ConfigRef( + selectedGC: gcRefc, + verbosity: 1, + options: DefaultOptions, + globalOptions: DefaultGlobalOptions, + foreignPackageNotes: {hintProcessing, warnUnknownMagic, + hintQuitCalled, hintExecuting}, + notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1]) -proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools} -proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc -template preciseStack*(): bool = gPreciseStack +proc cppDefine*(c: ConfigRef; define: string) = + c.cppDefines.incl define -template compilationCachePresent*: untyped = - gSymbolFiles in {enabledSf, writeOnlySf} +proc isDefined*(conf: ConfigRef; symbol: string): bool = + if conf.symbols.hasKey(symbol): + result = conf.symbols[symbol] != "false" + 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, osDragonfly, osMacosx, + osAndroid} + of "linux": + result = targetOS in {osLinux, osAndroid} + of "bsd": + result = targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly} + 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, + osDragonfly, osMacosx} + else: discard + +proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in {cmdDoc, cmdIdeTools} +proc usesNativeGC*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc + +template compilationCachePresent*(conf: ConfigRef): untyped = + conf.symbolFiles in {enabledSf, writeOnlySf} # {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {} -template optPreserveOrigSource*: untyped = - optEmbedOrigSrc in gGlobalOptions +template optPreserveOrigSource*(conf: ConfigRef): untyped = + optEmbedOrigSrc in conf.globalOptions const genSubDir* = "nimcache" @@ -176,82 +305,62 @@ const DocConfig* = "nimdoc.cfg" DocTexConfig* = "nimdoc.tex.cfg" -# additional configuration variables: -var - gConfigVars* = newStringTable(modeStyleInsensitive) - gDllOverrides = newStringTable(modeCaseInsensitive) - gModuleOverrides* = newStringTable(modeStyleInsensitive) - gPrefixDir* = "" # Overrides the default prefix dir in getPrefixDir proc. - libpath* = "" - gProjectName* = "" # holds a name like 'nim' - gProjectPath* = "" # holds a path like /home/alice/projects/nim/compiler/ - gProjectFull* = "" # projectPath/projectName - gProjectIsStdin* = false # whether we're compiling from stdin - gProjectMainIdx*: int32 # the canonical path id of the main module - nimcacheDir* = "" - command* = "" # the main command (e.g. cc, check, scan, etc) - commandArgs*: seq[string] = @[] # any arguments after the main command - gKeepComments*: bool = true # whether the parser needs to keep comments - implicitImports*: seq[string] = @[] # modules that are to be implicitly imported - implicitIncludes*: seq[string] = @[] # modules that are to be implicitly included - const oKeepVariableNames* = true -template compilingLib*: bool = +template compilingLib*(conf: ConfigRef): bool = gGlobalOptions * {optGenGuiApp, optGenDynLib} != {} -proc mainCommandArg*: string = +proc mainCommandArg*(conf: ConfigRef): string = ## This is intended for commands like check or parse ## which will work on the main project file unless ## explicitly given a specific file argument - if commandArgs.len > 0: - result = commandArgs[0] + if conf.commandArgs.len > 0: + result = conf.commandArgs[0] else: - result = gProjectName + result = conf.projectName -proc existsConfigVar*(key: string): bool = - result = hasKey(gConfigVars, key) +proc existsConfigVar*(conf: ConfigRef; key: string): bool = + result = hasKey(conf.configVars, key) -proc getConfigVar*(key: string): string = - result = gConfigVars.getOrDefault key +proc getConfigVar*(conf: ConfigRef; key: string): string = + result = conf.configVars.getOrDefault key -proc setConfigVar*(key, val: string) = - gConfigVars[key] = val +proc setConfigVar*(conf: ConfigRef; key, val: string) = + conf.configVars[key] = val -proc getOutFile*(filename, ext: string): string = - if options.outFile != "": result = options.outFile +proc getOutFile*(conf: ConfigRef; filename, ext: string): string = + if conf.outFile != "": result = conf.outFile else: result = changeFileExt(filename, ext) -proc getPrefixDir*(): string = +proc getPrefixDir*(conf: ConfigRef): string = ## Gets the prefix dir, usually the parent directory where the binary resides. ## - ## This is overridden by some tools (namely nimsuggest) via the ``gPrefixDir`` + ## This is overridden by some tools (namely nimsuggest) via the ``conf.prefixDir`` ## global. - if gPrefixDir != "": result = gPrefixDir - else: - result = splitPath(getAppDir()).head + if conf.prefixDir != "": result = conf.prefixDir + else: result = splitPath(getAppDir()).head -proc setDefaultLibpath*() = +proc setDefaultLibpath*(conf: ConfigRef) = # set default value (can be overwritten): - if libpath == "": + if conf.libpath == "": # choose default libpath: - var prefix = getPrefixDir() + var prefix = getPrefixDir(conf) when defined(posix): - if prefix == "/usr": libpath = "/usr/lib/nim" - elif prefix == "/usr/local": libpath = "/usr/local/lib/nim" - else: libpath = joinPath(prefix, "lib") - else: libpath = joinPath(prefix, "lib") + if prefix == "/usr": conf.libpath = "/usr/lib/nim" + elif prefix == "/usr/local": conf.libpath = "/usr/local/lib/nim" + else: conf.libpath = joinPath(prefix, "lib") + else: conf.libpath = joinPath(prefix, "lib") # Special rule to support other tools (nimble) which import the compiler # modules and make use of them. let realNimPath = findExe("nim") # Find out if $nim/../../lib/system.nim exists. - let parentNimLibPath = realNimPath.parentDir().parentDir() / "lib" - if not fileExists(libpath / "system.nim") and + let parentNimLibPath = realNimPath.parentDir.parentDir / "lib" + if not fileExists(conf.libpath / "system.nim") and fileExists(parentNimlibPath / "system.nim"): - libpath = parentNimLibPath + conf.libpath = parentNimLibPath -proc canonicalizePath*(path: string): string = +proc canonicalizePath*(conf: ConfigRef; path: string): string = # on Windows, 'expandFilename' calls getFullPathName which doesn't do # case corrections, so we have to use this convoluted way of retrieving # the true filename (see tests/modules and Nimble uses 'import Uri' instead @@ -263,12 +372,12 @@ proc canonicalizePath*(path: string): string = else: result = path.expandFilename -proc shortenDir*(dir: string): string = +proc shortenDir*(conf: ConfigRef; dir: string): string = ## returns the interesting part of a dir - var prefix = gProjectPath & DirSep + var prefix = conf.projectPath & DirSep if startsWith(dir, prefix): return substr(dir, len(prefix)) - prefix = getPrefixDir() & DirSep + prefix = getPrefixDir(conf) & DirSep if startsWith(dir, prefix): return substr(dir, len(prefix)) result = dir @@ -279,114 +388,89 @@ proc removeTrailingDirSep*(path: string): string = else: result = path -proc disableNimblePath*() = - gNoNimblePath = true - lazyPaths.setLen(0) +proc disableNimblePath*(conf: ConfigRef) = + incl conf.globalOptions, optNoNimblePath + conf.lazyPaths.setLen(0) include packagehandling -proc getNimcacheDir*: string = - result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir / - genSubDir - +proc getNimcacheDir*(conf: ConfigRef): string = + result = if conf.nimcacheDir.len > 0: conf.nimcacheDir + else: shortenDir(conf, conf.projectPath) / genSubDir -proc pathSubs*(p, config: string): string = +proc pathSubs*(conf: ConfigRef; p, config: string): string = let home = removeTrailingDirSep(os.getHomeDir()) result = unixToNativePath(p % [ - "nim", getPrefixDir(), - "lib", libpath, + "nim", getPrefixDir(conf), + "lib", conf.libpath, "home", home, "config", config, - "projectname", options.gProjectName, - "projectpath", options.gProjectPath, - "projectdir", options.gProjectPath, - "nimcache", getNimcacheDir()]) + "projectname", conf.projectName, + "projectpath", conf.projectPath, + "projectdir", conf.projectPath, + "nimcache", getNimcacheDir(conf)]) if "~/" in result: result = result.replace("~/", home & '/') -proc toGeneratedFile*(path, ext: string): string = +proc toGeneratedFile*(conf: ConfigRef; path, ext: string): string = ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod" var (head, tail) = splitPath(path) #if len(head) > 0: head = shortenDir(head & dirSep) - result = joinPath([getNimcacheDir(), changeFileExt(tail, ext)]) + result = joinPath([getNimcacheDir(conf), changeFileExt(tail, ext)]) #echo "toGeneratedFile(", path, ", ", ext, ") = ", result -when noTimeMachine: - var alreadyExcludedDirs = initSet[string]() - proc excludeDirFromTimeMachine(dir: string) {.raises: [].} = - ## Calls a macosx command on the directory to exclude it from backups. - ## - ## The macosx tmutil command is invoked to mark the specified path as an - ## item to be excluded from time machine backups. If a path already exists - ## with files before excluding it, newer files won't be added to the - ## directory, but previous files won't be removed from the backup until the - ## user deletes that directory. - ## - ## The whole proc is optional and will ignore all kinds of errors. The only - ## way to be sure that it works is to call ``tmutil isexcluded path``. - if alreadyExcludedDirs.contains(dir): return - alreadyExcludedDirs.incl(dir) - try: - var p = startProcess("/usr/bin/tmutil", args = ["addexclusion", dir]) - discard p.waitForExit - p.close - except Exception: - discard - -proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string = +proc completeGeneratedFilePath*(conf: ConfigRef; f: string, createSubDir: bool = true): string = var (head, tail) = splitPath(f) #if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep)) - var subdir = getNimcacheDir() # / head + var subdir = getNimcacheDir(conf) # / head if createSubDir: try: createDir(subdir) - when noTimeMachine: - excludeDirFromTimeMachine(subdir) except OSError: writeLine(stdout, "cannot create directory: " & subdir) quit(1) result = joinPath(subdir, tail) #echo "completeGeneratedFilePath(", f, ") = ", result -proc rawFindFile(f: string): string = - for it in searchPaths: +proc rawFindFile(conf: ConfigRef; f: string): string = + for it in conf.searchPaths: result = joinPath(it, f) if existsFile(result): - return result.canonicalizePath + return canonicalizePath(conf, result) result = "" -proc rawFindFile2(f: string): string = - for i, it in lazyPaths: +proc rawFindFile2(conf: ConfigRef; f: string): string = + for i, it in conf.lazyPaths: result = joinPath(it, f) if existsFile(result): # bring to front for j in countDown(i,1): - swap(lazyPaths[j], lazyPaths[j-1]) + swap(conf.lazyPaths[j], conf.lazyPaths[j-1]) - return result.canonicalizePath + return canonicalizePath(conf, result) result = "" -template patchModule() {.dirty.} = - if result.len > 0 and gModuleOverrides.len > 0: - let key = getPackageName(result) & "_" & splitFile(result).name - if gModuleOverrides.hasKey(key): - let ov = gModuleOverrides[key] +template patchModule(conf: ConfigRef) {.dirty.} = + if result.len > 0 and conf.moduleOverrides.len > 0: + let key = getPackageName(conf, result) & "_" & splitFile(result).name + if conf.moduleOverrides.hasKey(key): + let ov = conf.moduleOverrides[key] if ov.len > 0: result = ov -proc findFile*(f: string): string {.procvar.} = +proc findFile*(conf: ConfigRef; f: string): string {.procvar.} = if f.isAbsolute: result = if f.existsFile: f else: "" else: - result = f.rawFindFile + result = rawFindFile(conf, f) if result.len == 0: - result = f.toLowerAscii.rawFindFile + result = rawFindFile(conf, f.toLowerAscii) if result.len == 0: - result = f.rawFindFile2 + result = rawFindFile2(conf, f) if result.len == 0: - result = f.toLowerAscii.rawFindFile2 - patchModule() + result = rawFindFile2(conf, f.toLowerAscii) + patchModule(conf) -proc findModule*(modulename, currentModule: string): string = +proc findModule*(conf: ConfigRef; modulename, currentModule: string): string = # returns path to module when defined(nimfix): # '.nimfix' modules are preferred over '.nim' modules so that specialized @@ -396,16 +480,16 @@ proc findModule*(modulename, currentModule: string): string = let currentPath = currentModule.splitFile.dir result = currentPath / m if not existsFile(result): - result = findFile(m) + result = findFile(conf, m) if existsFile(result): return result let m = addFileExt(modulename, NimExt) let currentPath = currentModule.splitFile.dir result = currentPath / m if not existsFile(result): - result = findFile(m) - patchModule() + result = findFile(conf, m) + patchModule(conf) -proc findProjectNimFile*(pkg: string): string = +proc findProjectNimFile*(conf: ConfigRef; pkg: string): string = const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"] var candidates: seq[string] = @[] for k, f in os.walkDir(pkg, relative=true): @@ -430,25 +514,12 @@ proc canonDynlibName(s: string): string = else: result = s.substr(start) -proc inclDynlibOverride*(lib: string) = - gDllOverrides[lib.canonDynlibName] = "true" - -proc isDynlibOverride*(lib: string): bool = - result = gDynlibOverrideAll or gDllOverrides.hasKey(lib.canonDynlibName) - -proc binaryStrSearch*(x: openArray[string], y: string): int = - var a = 0 - var b = len(x) - 1 - while a <= b: - var mid = (a + b) div 2 - var c = cmpIgnoreCase(x[mid], y) - if c < 0: - a = mid + 1 - elif c > 0: - b = mid - 1 - else: - return mid - result = - 1 +proc inclDynlibOverride*(conf: ConfigRef; lib: string) = + conf.dllOverrides[lib.canonDynlibName] = "true" + +proc isDynlibOverride*(conf: ConfigRef; lib: string): bool = + result = optDynlibOverrideAll in conf.globalOptions or + conf.dllOverrides.hasKey(lib.canonDynlibName) proc parseIdeCmd*(s: string): IdeCmd = case s: diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim index 758411e39..2efab58b0 100644 --- a/compiler/packagehandling.nim +++ b/compiler/packagehandling.nim @@ -15,23 +15,16 @@ iterator myParentDirs(p: string): string = if current.len == 0: break yield current -template newPackageCache(): untyped = - newStringTable(when FileSystemCaseSensitive: - modeCaseInsensitive - else: - modeCaseSensitive) +proc resetPackageCache*(conf: ConfigRef) = + conf.packageCache = newPackageCache() -var packageCache = newPackageCache() - -proc resetPackageCache*() = packageCache = newPackageCache() - -proc getPackageName*(path: string): string = +proc getPackageName*(conf: ConfigRef; path: string): string = var parents = 0 block packageSearch: for d in myParentDirs(path): - if packageCache.hasKey(d): + if conf.packageCache.hasKey(d): #echo "from cache ", d, " |", packageCache[d], "|", path.splitFile.name - return packageCache[d] + return conf.packageCache[d] inc parents for file in walkFiles(d / "*.nimble"): result = file.splitFile.name @@ -43,12 +36,12 @@ proc getPackageName*(path: string): string = if result.isNil: result = "" for d in myParentDirs(path): #echo "set cache ", d, " |", result, "|", parents - packageCache[d] = result + conf.packageCache[d] = result dec parents if parents <= 0: break -proc withPackageName*(path: string): string = - let x = path.getPackageName +proc withPackageName*(conf: ConfigRef; path: string): string = + let x = getPackageName(conf, path) if x.len == 0: result = path else: diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index 0b8c8543e..944aec048 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -10,7 +10,8 @@ ## This module implements the pattern matching features for term rewriting ## macro support. -import strutils, ast, astalgo, types, msgs, idents, renderer, wordrecg, trees +import strutils, ast, astalgo, types, msgs, idents, renderer, wordrecg, trees, + options # we precompile the pattern here for efficiency into some internal # stack based VM :-) Why? Because it's fun; I did no benchmarks to see if that @@ -41,8 +42,8 @@ type const MaxStackSize* = 64 ## max required stack size by the VM -proc patternError(n: PNode) = - localError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments})) +proc patternError(n: PNode; conf: ConfigRef) = + localError(conf, n.info, "illformed AST: " & renderTree(n, {renderNoComments})) proc add(code: var TPatternCode, op: TOpcode) {.inline.} = add(code, chr(ord(op))) @@ -53,42 +54,42 @@ proc whichAlias*(p: PSym): TAliasRequest = else: result = aqNone -proc compileConstraints(p: PNode, result: var TPatternCode) = +proc compileConstraints(p: PNode, result: var TPatternCode; conf: ConfigRef) = case p.kind of nkCallKinds: if p.sons[0].kind != nkIdent: - patternError(p.sons[0]) + patternError(p.sons[0], conf) return let op = p.sons[0].ident if p.len == 3: if op.s == "|" or op.id == ord(wOr): - compileConstraints(p.sons[1], result) - compileConstraints(p.sons[2], result) + compileConstraints(p.sons[1], result, conf) + compileConstraints(p.sons[2], result, conf) result.add(ppOr) elif op.s == "&" or op.id == ord(wAnd): - compileConstraints(p.sons[1], result) - compileConstraints(p.sons[2], result) + compileConstraints(p.sons[1], result, conf) + compileConstraints(p.sons[2], result, conf) result.add(ppAnd) else: - patternError(p) + patternError(p, conf) elif p.len == 2 and (op.s == "~" or op.id == ord(wNot)): - compileConstraints(p.sons[1], result) + compileConstraints(p.sons[1], result, conf) result.add(ppNot) else: - patternError(p) + patternError(p, conf) of nkAccQuoted, nkPar: if p.len == 1: - compileConstraints(p.sons[0], result) + compileConstraints(p.sons[0], result, conf) else: - patternError(p) + patternError(p, conf) of nkIdent: let spec = p.ident.s.normalize case spec - of "atom": result.add(ppAtom) - of "lit": result.add(ppLit) - of "sym": result.add(ppSym) + of "atom": result.add(ppAtom) + of "lit": result.add(ppLit) + of "sym": result.add(ppSym) of "ident": result.add(ppIdent) - of "call": result.add(ppCall) + of "call": result.add(ppCall) of "alias": result[0] = chr(aqShouldAlias.ord) of "noalias": result[0] = chr(aqNoAlias.ord) of "lvalue": result.add(ppLValue) @@ -97,24 +98,24 @@ proc compileConstraints(p: PNode, result: var TPatternCode) = of "nosideeffect": result.add(ppNoSideEffect) else: # check all symkinds: - internalAssert int(high(TSymKind)) < 255 + internalAssert conf, int(high(TSymKind)) < 255 for i in low(TSymKind)..high(TSymKind): if cmpIgnoreStyle(($i).substr(2), spec) == 0: result.add(ppSymKind) result.add(chr(i.ord)) return # check all nodekinds: - internalAssert int(high(TNodeKind)) < 255 + internalAssert conf, int(high(TNodeKind)) < 255 for i in low(TNodeKind)..high(TNodeKind): if cmpIgnoreStyle($i, spec) == 0: result.add(ppNodeKind) result.add(chr(i.ord)) return - patternError(p) + patternError(p, conf) else: - patternError(p) + patternError(p, conf) -proc semNodeKindConstraints*(p: PNode): PNode = +proc semNodeKindConstraints*(p: PNode; conf: ConfigRef): PNode = ## does semantic checking for a node kind pattern and compiles it into an ## efficient internal format. assert p.kind == nkCurlyExpr @@ -123,11 +124,11 @@ proc semNodeKindConstraints*(p: PNode): PNode = result.strVal.add(chr(aqNone.ord)) if p.len >= 2: for i in 1..<p.len: - compileConstraints(p.sons[i], result.strVal) + compileConstraints(p.sons[i], result.strVal, conf) if result.strVal.len > MaxStackSize-1: - internalError(p.info, "parameter pattern too complex") + internalError(conf, p.info, "parameter pattern too complex") else: - patternError(p) + patternError(p, conf) result.strVal.add(ppEof) type @@ -178,6 +179,36 @@ type arDiscriminant, # is a discriminant arStrange # it is a strange beast like 'typedesc[var T]' +proc exprRoot*(n: PNode): PSym = + var it = n + while true: + case it.kind + of nkSym: return it.sym + of nkHiddenDeref, nkDerefExpr: + if it[0].typ.skipTypes(abstractInst).kind in {tyPtr, tyRef}: + # 'ptr' is unsafe anyway and 'ref' is always on the heap, + # so allow these derefs: + break + else: + it = it[0] + of nkDotExpr, nkBracketExpr, nkHiddenAddr, + nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: + it = it[0] + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + it = it[1] + of nkStmtList, nkStmtListExpr: + if it.len > 0 and it.typ != nil: it = it.lastSon + else: break + of nkCallKinds: + if it.typ != nil and it.typ.kind == tyVar and it.len > 1: + # See RFC #7373, calls returning 'var T' are assumed to + # return a view into the first argument (if there is one): + it = it[1] + else: + break + else: + break + proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult = ## 'owner' can be nil! result = arNone @@ -189,7 +220,7 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet} else: {skVar, skResult, skTemp} if n.sym.kind in kinds: - if owner != nil and owner.id == n.sym.owner.id and + if owner != nil and owner == n.sym.owner and sfGlobal notin n.sym.flags: result = arLocalLValue else: @@ -205,7 +236,8 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult result = arLValue else: result = isAssignable(owner, n.sons[0], isUnsafeAddr) - if result != arNone and sfDiscriminant in n.sons[1].sym.flags: + if result != arNone and n[1].kind == nkSym and + sfDiscriminant in n[1].sym.flags: result = arDiscriminant of nkBracketExpr: if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in @@ -222,7 +254,10 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct): # types that are equal modulo distinction preserve l-value: result = isAssignable(owner, n.sons[1], isUnsafeAddr) - of nkHiddenDeref, nkDerefExpr, nkHiddenAddr: + of nkHiddenDeref: + if n[0].typ.kind == tyLent: result = arDiscriminant + else: result = arLValue + of nkDerefExpr, nkHiddenAddr: result = arLValue of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: result = isAssignable(owner, n.sons[0], isUnsafeAddr) diff --git a/compiler/parser.nim b/compiler/parser.nim index b1cfd7609..fbc57ebb6 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -17,6 +17,8 @@ # In fact the grammar is generated from this file: when isMainModule: + # Leave a note in grammar.txt that it is generated: + #| # This file is generated by compiler/parser.nim. import pegs var outp = open("doc/grammar.txt", fmWrite) for line in lines("compiler/parser.nim"): @@ -25,7 +27,7 @@ when isMainModule: outp.close import - llstream, lexer, idents, strutils, ast, astalgo, msgs + llstream, lexer, idents, strutils, ast, astalgo, msgs, options, configuration type TParser* = object # A TParser object represents a file that @@ -81,21 +83,21 @@ proc getTok(p: var TParser) = rawGetTok(p.lex, p.tok) p.hasProgress = true -proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream, - cache: IdentCache; +proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream, + cache: IdentCache; config: ConfigRef; strongSpaces=false) = ## Open a parser, using the given arguments to set up its internal state. ## initToken(p.tok) - openLexer(p.lex, fileIdx, inputStream, cache) + openLexer(p.lex, fileIdx, inputStream, cache, config) getTok(p) # read the first token p.firstTok = true p.strongSpaces = strongSpaces proc openParser*(p: var TParser, filename: string, inputStream: PLLStream, - cache: IdentCache; + cache: IdentCache; config: ConfigRef; strongSpaces=false) = - openParser(p, filename.fileInfoIdx, inputStream, cache, strongSpaces) + openParser(p, fileInfoIdx(config, filename), inputStream, cache, config, strongSpaces) proc closeParser(p: var TParser) = ## Close a parser, freeing up its resources. @@ -105,9 +107,13 @@ proc parMessage(p: TParser, msg: TMsgKind, arg = "") = ## Produce and emit the parser message `arg` to output. lexMessageTok(p.lex, msg, p.tok, arg) -proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) = +proc parMessage(p: TParser, msg: string, tok: TToken) = ## Produce and emit a parser message to output about the token `tok` - parMessage(p, msg, prettyTok(tok)) + parMessage(p, errGenerated, msg % prettyTok(tok)) + +proc parMessage(p: TParser, arg: string) = + ## Produce and emit the parser message `arg` to output. + lexMessageTok(p.lex, errGenerated, p.tok, arg) template withInd(p, body: untyped) = let oldInd = p.currInd @@ -123,7 +129,13 @@ proc rawSkipComment(p: var TParser, node: PNode) = if p.tok.tokType == tkComment: if node != nil: if node.comment == nil: node.comment = "" - add(node.comment, p.tok.literal) + when defined(nimpretty): + if p.tok.commentOffsetB > p.tok.commentOffsetA: + add node.comment, fileSection(p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB) + else: + add node.comment, p.tok.literal + else: + add(node.comment, p.tok.literal) else: parMessage(p, errInternal, "skipComment") getTok(p) @@ -134,6 +146,12 @@ proc skipComment(p: var TParser, node: PNode) = proc flexComment(p: var TParser, node: PNode) = if p.tok.indent < 0 or realInd(p): rawSkipComment(p, node) +const + errInvalidIndentation = "invalid indentation" + errIdentifierExpected = "identifier expected, but got '$1'" + errExprExpected = "expression expected, but found '$1'" + errTokenExpected = "'$1' expected" + proc skipInd(p: var TParser) = if p.tok.indent >= 0: if not realInd(p): parMessage(p, errInvalidIndentation) @@ -152,11 +170,11 @@ proc getTokNoInd(p: var TParser) = proc expectIdentOrKeyw(p: TParser) = if p.tok.tokType != tkSymbol and not isKeyword(p.tok.tokType): - lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok)) + lexMessage(p.lex, errGenerated, errIdentifierExpected % prettyTok(p.tok)) proc expectIdent(p: TParser) = if p.tok.tokType != tkSymbol: - lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok)) + lexMessage(p.lex, errGenerated, errIdentifierExpected % prettyTok(p.tok)) proc eat(p: var TParser, tokType: TTokType) = ## Move the parser to the next token if the current token is of type @@ -164,7 +182,8 @@ proc eat(p: var TParser, tokType: TTokType) = if p.tok.tokType == tokType: getTok(p) else: - lexMessageTok(p.lex, errTokenExpected, p.tok, TokTypeToStr[tokType]) + lexMessage(p.lex, errGenerated, + "expected: '" & TokTypeToStr[tokType] & "', but got: '" & prettyTok(p.tok) & "'") proc parLineInfo(p: TParser): TLineInfo = ## Retrieve the line information associated with the parser's current state. @@ -260,13 +279,9 @@ proc isUnary(p: TParser): bool = proc checkBinary(p: TParser) {.inline.} = ## Check if the current parser token is a binary operator. # we don't check '..' here as that's too annoying - if p.strongSpaces and p.tok.tokType == tkOpr: + if p.tok.tokType == tkOpr: if p.tok.strongSpaceB > 0 and p.tok.strongSpaceA != p.tok.strongSpaceB: - parMessage(p, errGenerated, - "Number of spaces around '$#' not consistent" % - prettyTok(p.tok)) - elif p.tok.strongSpaceA notin {0,1,2,4,8}: - parMessage(p, errGenerated, "Number of spaces must be 0,1,2,4 or 8") + parMessage(p, warnInconsistentSpacing, prettyTok(p.tok)) #| module = stmt ^* (';' / IND{=}) #| @@ -387,20 +402,6 @@ proc exprList(p: var TParser, endTok: TTokType, result: PNode) = getTok(p) optInd(p, a) -proc dotExpr(p: var TParser, a: PNode): PNode = - #| dotExpr = expr '.' optInd symbol - var info = p.parLineInfo - getTok(p) - result = newNodeI(nkDotExpr, info) - optInd(p, result) - addSon(result, a) - addSon(result, parseSymbol(p, smAfterDot)) - -proc qualifiedIdent(p: var TParser): PNode = - #| qualifiedIdent = symbol ('.' optInd symbol)? - result = parseSymbol(p) - if p.tok.tokType == tkDot: result = dotExpr(p, result) - proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) = assert(endTok in {tkCurlyRi, tkCurlyDotRi, tkBracketRi, tkParRi}) getTok(p) @@ -411,6 +412,9 @@ proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) = addSon(result, a) if p.tok.tokType != tkComma: break getTok(p) + # (1,) produces a tuple expression + if endTok == tkParRi and p.tok.tokType == tkParRi and result.kind == nkPar: + result.kind = nkTupleConstr skipComment(p, a) optPar(p) eat(p, endTok) @@ -421,6 +425,33 @@ proc exprColonEqExprList(p: var TParser, kind: TNodeKind, result = newNodeP(kind, p) exprColonEqExprListAux(p, endTok, result) +proc dotExpr(p: var TParser, a: PNode): PNode = + #| dotExpr = expr '.' optInd (symbol | '[:' exprList ']') + #| explicitGenericInstantiation = '[:' exprList ']' ( '(' exprColonEqExpr ')' )? + var info = p.parLineInfo + getTok(p) + result = newNodeI(nkDotExpr, info) + optInd(p, result) + addSon(result, a) + addSon(result, parseSymbol(p, smAfterDot)) + if p.tok.tokType == tkBracketLeColon and p.tok.strongSpaceA <= 0: + var x = newNodeI(nkBracketExpr, p.parLineInfo) + # rewrite 'x.y[:z]()' to 'y[z](x)' + x.add result[1] + exprList(p, tkBracketRi, x) + eat(p, tkBracketRi) + var y = newNodeI(nkCall, p.parLineInfo) + y.add x + y.add result[0] + if p.tok.tokType == tkParLe and p.tok.strongSpaceA <= 0: + exprColonEqExprListAux(p, tkParRi, y) + result = y + +proc qualifiedIdent(p: var TParser): PNode = + #| qualifiedIdent = symbol ('.' optInd symbol)? + result = parseSymbol(p) + if p.tok.tokType == tkDot: result = dotExpr(p, result) + proc setOrTableConstr(p: var TParser): PNode = #| setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}' result = newNodeP(nkCurly, p) @@ -551,6 +582,9 @@ proc parsePar(p: var TParser): PNode = if p.tok.tokType == tkComma: getTok(p) skipComment(p, a) + # (1,) produces a tuple expression: + if p.tok.tokType == tkParRi: + result.kind = nkTupleConstr # progress guaranteed while p.tok.tokType != tkParRi and p.tok.tokType != tkEof: var a = exprColonEqExpr(p) @@ -679,10 +713,17 @@ proc namedParams(p: var TParser, callee: PNode, # progress guaranteed exprColonEqExprListAux(p, endTok, result) -proc commandParam(p: var TParser): PNode = +proc commandParam(p: var TParser, isFirstParam: var bool): PNode = result = parseExpr(p) if p.tok.tokType == tkDo: result = postExprBlocks(p, result) + elif p.tok.tokType == tkEquals and not isFirstParam: + let lhs = result + result = newNodeP(nkExprEqExpr, p) + getTok(p) + addSon(result, lhs) + addSon(result, parseExpr(p)) + isFirstParam = false proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks? @@ -717,17 +758,19 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = # progress guaranteed somePar() result = namedParams(p, result, nkCurlyExpr, tkCurlyRi) - of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType: - if p.inPragma == 0: + of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType, + tkOpr, tkDotDot: + if p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot}): # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet # solution, but pragmas.nim can't handle that let a = result result = newNodeP(nkCommand, p) addSon(result, a) + var isFirstParam = true when true: # progress NOT guaranteed p.hasProgress = false - addSon result, commandParam(p) + addSon result, commandParam(p, isFirstParam) if not p.hasProgress: break else: while p.tok.tokType != tkEof: @@ -818,7 +861,6 @@ proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode = if realInd(p): p.currInd = p.tok.indent wasIndented = true - echo result.info, " yes ", p.currInd addSon(branch, parseExpr(p)) result.add branch while sameInd(p) or not wasIndented: @@ -855,7 +897,7 @@ proc parsePragma(p: var TParser): PNode = skipComment(p, a) optPar(p) if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: getTok(p) - else: parMessage(p, errTokenExpected, ".}") + else: parMessage(p, "expected '.}'") dec p.inPragma proc identVis(p: var TParser; allowDot=false): PNode = @@ -914,15 +956,15 @@ proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode = optInd(p, result) addSon(result, parseTypeDesc(p)) else: - addSon(result, ast.emptyNode) + addSon(result, newNodeP(nkEmpty, p)) if p.tok.tokType != tkEquals and withBothOptional notin flags: - parMessage(p, errColonOrEqualsExpected, p.tok) + parMessage(p, "':' or '=' expected, but got '$1'", p.tok) if p.tok.tokType == tkEquals: getTok(p) optInd(p, result) addSon(result, parseExpr(p)) else: - addSon(result, ast.emptyNode) + addSon(result, newNodeP(nkEmpty, p)) proc parseTuple(p: var TParser, indentAllowed = false): PNode = #| inlTupleDecl = 'tuple' @@ -962,6 +1004,8 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode = parMessage(p, errIdentifierExpected, p.tok) break if not sameInd(p): break + elif p.tok.tokType == tkParLe: + parMessage(p, errGenerated, "the syntax for tuple types is 'tuple[...]', not 'tuple(...)'") else: result = newNodeP(nkTupleClassTy, p) @@ -983,8 +1027,11 @@ proc parseParamList(p: var TParser, retColon = true): PNode = a = parseIdentColonEquals(p, {withBothOptional, withPragma}) of tkParRi: break + of tkVar: + parMessage(p, errGenerated, "the syntax is 'parameter: var T', not 'var parameter: T'") + break else: - parMessage(p, errTokenExpected, ")") + parMessage(p, "expected closing ')'") break addSon(result, a) if p.tok.tokType notin {tkComma, tkSemiColon}: break @@ -1061,6 +1108,7 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, #| distinct = 'distinct' optInd typeDesc result = newNodeP(kind, p) getTok(p) + if p.tok.indent != -1 and p.tok.indent <= p.currInd: return optInd(p, result) if not isOperator(p.tok) and isExprStart(p): addSon(result, primary(p, mode)) @@ -1144,7 +1192,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = if mode == pmTypeDef: result = parseTypeClass(p) else: - parMessage(p, errInvalidToken, p.tok) + parMessage(p, "the 'concept' keyword is only valid in 'type' sections") of tkStatic: let info = parLineInfo(p) getTokNoInd(p) @@ -1254,7 +1302,7 @@ proc postExprBlocks(p: var TParser, x: PNode): PNode = if nextBlock.kind == nkElse: break else: if openingParams.kind != nkEmpty: - parMessage(p, errTokenExpected, ":") + parMessage(p, "expected ':'") proc parseExprStmt(p: var TParser): PNode = #| exprStmt = simpleExpr @@ -1274,17 +1322,18 @@ proc parseExprStmt(p: var TParser): PNode = addSon(result, b) else: # simpleExpr parsed 'p a' from 'p a, b'? + var isFirstParam = false if p.tok.indent < 0 and p.tok.tokType == tkComma and a.kind == nkCommand: result = a while true: getTok(p) optInd(p, result) - addSon(result, commandParam(p)) + addSon(result, commandParam(p, isFirstParam)) if p.tok.tokType != tkComma: break elif p.tok.indent < 0 and isExprStart(p): result = newNode(nkCommand, a.info, @[a]) while true: - addSon(result, commandParam(p)) + addSon(result, commandParam(p, isFirstParam)) if p.tok.tokType != tkComma: break getTok(p) optInd(p, result) @@ -1489,7 +1538,7 @@ proc parseTry(p: var TParser; isExpr: bool): PNode = addSon(b, parseStmt(p)) addSon(result, b) if b.kind == nkFinally: break - if b == nil: parMessage(p, errTokenExpected, "except") + if b == nil: parMessage(p, "expected 'except'") proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode = #| exceptBlock = 'except' colcom stmt @@ -1544,7 +1593,7 @@ proc parseAsm(p: var TParser): PNode = of tkTripleStrLit: addSon(result, newStrNodeP(nkTripleStrLit, p.tok.literal, p)) else: - parMessage(p, errStringLiteralExpected) + parMessage(p, "the 'asm' statement takes a string literal") addSon(result, ast.emptyNode) return getTok(p) @@ -1675,7 +1724,7 @@ proc parseSection(p: var TParser, kind: TNodeKind, parMessage(p, errIdentifierExpected, p.tok) proc parseConstant(p: var TParser): PNode = - #| constant = identWithPragma (colon typedesc)? '=' optInd expr indAndComment + #| constant = identWithPragma (colon typeDesc)? '=' optInd expr indAndComment result = newNodeP(nkConstDef, p) addSon(result, identWithPragma(p)) if p.tok.tokType == tkColon: @@ -1723,7 +1772,7 @@ proc parseEnum(p: var TParser): PNode = p.tok.tokType == tkEof: break if result.len <= 1: - lexMessageTok(p.lex, errIdentifierExpected, p.tok, prettyTok(p.tok)) + parMessage(p, errIdentifierExpected, p.tok) proc parseObjectPart(p: var TParser): PNode proc parseObjectWhen(p: var TParser): PNode = @@ -2086,7 +2135,7 @@ proc parseStmt(p: var TParser): PNode = case p.tok.tokType of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkFunc, tkIterator, tkMacro, tkType, tkConst, tkWhen, tkVar: - parMessage(p, errComplexStmtRequiresInd) + parMessage(p, "complex statement requires indentation") result = ast.emptyNode else: if p.inSemiStmtList > 0: @@ -2130,7 +2179,12 @@ proc parseTopLevelStmt(p: var TParser): PNode = if p.tok.indent != 0: if p.firstTok and p.tok.indent < 0: discard elif p.tok.tokType != tkSemiColon: - parMessage(p, errInvalidIndentation) + # special casing for better error messages: + if p.tok.tokType == tkOpr and p.tok.ident.s == "*": + parMessage(p, errGenerated, + "invalid indentation; an export marker '*' follows the declared identifier") + else: + parMessage(p, errInvalidIndentation) p.firstTok = false case p.tok.tokType of tkSemiColon: @@ -2144,8 +2198,8 @@ proc parseTopLevelStmt(p: var TParser): PNode = if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok) break -proc parseString*(s: string; cache: IdentCache; filename: string = ""; - line: int = 0; +proc parseString*(s: string; cache: IdentCache; config: ConfigRef; + filename: string = ""; line: int = 0; errorHandler: TErrorHandler = nil): PNode = ## Parses a string into an AST, returning the top node. ## `filename` and `line`, although optional, provide info so that the @@ -2158,7 +2212,7 @@ proc parseString*(s: string; cache: IdentCache; filename: string = ""; # XXX for now the builtin 'parseStmt/Expr' functions do not know about strong # spaces... parser.lex.errorHandler = errorHandler - openParser(parser, filename, stream, cache, false) + openParser(parser, filename, stream, cache, config, false) result = parser.parseAll closeParser(parser) diff --git a/compiler/passaux.nim b/compiler/passaux.nim index 2065d5893..568fb4c23 100644 --- a/compiler/passaux.nim +++ b/compiler/passaux.nim @@ -10,22 +10,26 @@ ## implements some little helper passes import - strutils, ast, astalgo, passes, idents, msgs, options, idgen + strutils, ast, astalgo, passes, idents, msgs, options, idgen, configuration from modulegraphs import ModuleGraph +type + VerboseRef = ref object of TPassContext + config: ConfigRef + proc verboseOpen(graph: ModuleGraph; s: PSym; cache: IdentCache): PPassContext = #MessageOut('compiling ' + s.name.s); - result = nil # we don't need a context - rawMessage(hintProcessing, s.name.s) + result = VerboseRef(config: graph.config) + rawMessage(graph.config, hintProcessing, s.name.s) proc verboseProcess(context: PPassContext, n: PNode): PNode = result = n - if context != nil: internalError("logpass: context is not nil") - if gVerbosity == 3: + let v = VerboseRef(context) + if v.config.verbosity == 3: # system.nim deactivates all hints, for verbosity:3 we want the processing # messages nonetheless, so we activate them again unconditionally: - incl(msgs.gNotes, hintProcessing) - message(n.info, hintProcessing, $idgen.gFrontendId) + incl(v.config.notes, hintProcessing) + message(v.config, n.info, hintProcessing, $idgen.gFrontendId) const verbosePass* = makePass(open = verboseOpen, process = verboseProcess) diff --git a/compiler/passes.nim b/compiler/passes.nim index 29b27627d..8f9f57f3d 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -7,13 +7,14 @@ # distribution, for details about the copyright. # -# This module implements the passes functionality. A pass must implement the -# `TPass` interface. +## This module implements the passes functionality. A pass must implement the +## `TPass` interface. import strutils, options, ast, astalgo, llstream, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math, magicsys, nversion, - nimsets, syntaxes, times, rodread, idgen, modulegraphs, reorder + nimsets, syntaxes, times, rodread, idgen, modulegraphs, reorder, rod, + configuration type @@ -29,7 +30,8 @@ type TPassProcess* = proc (p: PPassContext, topLevelStmt: PNode): PNode {.nimcall.} TPass* = tuple[open: TPassOpen, openCached: TPassOpenCached, - process: TPassProcess, close: TPassClose] + process: TPassProcess, close: TPassClose, + isFrontend: bool] TPassData* = tuple[input: PNode, closeOutput: PNode] TPasses* = openArray[TPass] @@ -41,38 +43,26 @@ type proc makePass*(open: TPassOpen = nil, openCached: TPassOpenCached = nil, process: TPassProcess = nil, - close: TPassClose = nil): TPass = + close: TPassClose = nil, + isFrontend = false): TPass = result.open = open result.openCached = openCached result.close = close result.process = process + result.isFrontend = isFrontend # the semantic checker needs these: var - gImportModule*: proc (graph: ModuleGraph; m: PSym, fileIdx: int32; cache: IdentCache): PSym {.nimcall.} - gIncludeFile*: proc (graph: ModuleGraph; m: PSym, fileIdx: int32; cache: IdentCache): PNode {.nimcall.} + gImportModule*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex; cache: IdentCache): PSym {.nimcall.} + gIncludeFile*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex; cache: IdentCache): PNode {.nimcall.} # implementation -proc skipCodegen*(n: PNode): bool {.inline.} = +proc skipCodegen*(config: ConfigRef; n: PNode): bool {.inline.} = # can be used by codegen passes to determine whether they should do # something with `n`. Currently, this ignores `n` and uses the global # error count instead. - result = msgs.gErrorCounter > 0 - -proc astNeeded*(s: PSym): bool = - # The ``rodwrite`` module uses this to determine if the body of a proc - # needs to be stored. The passes manager frees s.sons[codePos] when - # appropriate to free the procedure body's memory. This is important - # to keep memory usage down. - if (s.kind in {skMethod, skProc, skFunc}) and - ({sfCompilerProc, sfCompileTime} * s.flags == {}) and - (s.typ.callConv != ccInline) and - (s.ast.sons[genericParamsPos].kind == nkEmpty): - result = false - # XXX this doesn't really make sense with excessive CTFE - else: - result = true + result = config.errorCounter > 0 const maxPasses = 10 @@ -150,20 +140,21 @@ proc closePassesCached(graph: ModuleGraph; a: var TPassContextArray) = m = gPasses[i].close(graph, a[i], m) a[i] = nil # free the memory here -proc resolveMod(module, relativeTo: string): int32 = - let fullPath = findModule(module, relativeTo) +proc resolveMod(conf: ConfigRef; module, relativeTo: string): FileIndex = + let fullPath = findModule(conf, module, relativeTo) if fullPath.len == 0: result = InvalidFileIDX else: - result = fullPath.fileInfoIdx + result = fileInfoIdx(conf, fullPath) -proc processImplicits(implicits: seq[string], nodeKind: TNodeKind, +proc processImplicits(conf: ConfigRef; implicits: seq[string], nodeKind: TNodeKind, a: var TPassContextArray; m: PSym) = # XXX fixme this should actually be relative to the config file! + let gCmdLineInfo = newLineInfo(FileIndex(0), 1, 1) let relativeTo = m.info.toFullPath for module in items(implicits): # implicit imports should not lead to a module importing itself - if m.position != resolveMod(module, relativeTo): + if m.position != resolveMod(conf, module, relativeTo).int32: var importStmt = newNodeI(nodeKind, gCmdLineInfo) var str = newStrNode(nkStrLit, module) str.info = gCmdLineInfo @@ -178,26 +169,55 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream, a: TPassContextArray s: PLLStream fileIdx = module.fileIdx - if rd == nil: + if module.id < 0: + # new module caching mechanism: + for i in 0..<gPassesLen: + if not isNil(gPasses[i].open) and not gPasses[i].isFrontend: + a[i] = gPasses[i].open(graph, module, cache) + else: + a[i] = nil + + var stmtIndex = 0 + var doContinue = true + while doContinue: + let n = loadNode(module, stmtIndex) + if n == nil or graph.stopCompile(): break + #if n.kind == nkImportStmt: + # echo "yes and it's ", n + inc stmtIndex + var m = n + for i in 0..<gPassesLen: + if not isNil(gPasses[i].process) and not gPasses[i].isFrontend: + m = gPasses[i].process(a[i], m) + if isNil(m): + doContinue = false + break + + var m: PNode = nil + for i in 0..<gPassesLen: + if not isNil(gPasses[i].close) and not gPasses[i].isFrontend: + m = gPasses[i].close(graph, a[i], m) + a[i] = nil + elif rd == nil: openPasses(graph, a, module, cache) if stream == nil: let filename = fileIdx.toFullPathConsiderDirty s = llStreamOpen(filename, fmRead) if s == nil: - rawMessage(errCannotOpenFile, filename) + rawMessage(graph.config, errCannotOpenFile, filename) return false else: s = stream while true: - openParsers(p, fileIdx, s, cache) + openParsers(p, fileIdx, s, cache, graph.config) if sfSystemModule notin module.flags: # XXX what about caching? no processing then? what if I change the # modules to include between compilation runs? we'd need to track that # in ROD files. I think we should enable this feature only # for the interactive mode. - processImplicits implicitImports, nkImportStmt, a, module - processImplicits implicitIncludes, nkIncludeStmt, a, module + processImplicits graph.config, graph.config.implicitImports, nkImportStmt, a, module + processImplicits graph.config, graph.config.implicitIncludes, nkIncludeStmt, a, module while true: if graph.stopCompile(): break diff --git a/compiler/patterns.nim b/compiler/patterns.nim index 31b76743e..5409a4811 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -150,7 +150,7 @@ proc matches(c: PPatternContext, p, n: PNode): bool = of "*": result = matchNested(c, p, n, rpn=false) of "**": result = matchNested(c, p, n, rpn=true) of "~": result = not matches(c, p.sons[1], n) - else: internalError(p.info, "invalid pattern") + else: doAssert(false, "invalid pattern") # template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) = # add(a, b) elif p.kind == nkCurlyExpr: @@ -289,7 +289,7 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode = # constraint not fulfilled: if not ok: return nil - markUsed(n.info, s, c.graph.usageSym) + markUsed(c.config, n.info, s, c.graph.usageSym) if ctx.subMatch: assert m.len == 3 m.sons[1] = result diff --git a/compiler/pbraces.nim b/compiler/pbraces.nim deleted file mode 100644 index eba6f0b62..000000000 --- a/compiler/pbraces.nim +++ /dev/null @@ -1,1790 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2017 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -# This module implements the parser of the braces Nim syntax. - -import - llstream, lexer, idents, strutils, ast, astalgo, msgs - -from parser import TParser - -proc getTok(p: var TParser) = - ## Get the next token from the parser's lexer, and store it in the parser's - ## `tok` member. - rawGetTok(p.lex, p.tok) - -proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream; - cache: IdentCache) = - ## Open a parser, using the given arguments to set up its internal state. - ## - initToken(p.tok) - openLexer(p.lex, fileIdx, inputStream, cache) - getTok(p) # read the first token - p.lex.allowTabs = true - -proc openParser*(p: var TParser, filename: string, inputStream: PLLStream; - cache: IdentCache) = - openParser(p, filename.fileInfoIdx, inputStream, cache) - -proc closeParser*(p: var TParser) = - ## Close a parser, freeing up its resources. - closeLexer(p.lex) - -proc parMessage(p: TParser, msg: TMsgKind, arg = "") = - ## Produce and emit the parser message `arg` to output. - lexMessageTok(p.lex, msg, p.tok, arg) - -proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) = - ## Produce and emit a parser message to output about the token `tok` - parMessage(p, msg, prettyTok(tok)) - -proc rawSkipComment(p: var TParser, node: PNode) = - if p.tok.tokType == tkComment: - if node != nil: - if node.comment == nil: node.comment = "" - add(node.comment, p.tok.literal) - else: - parMessage(p, errInternal, "skipComment") - getTok(p) - -proc skipComment(p: var TParser, node: PNode) = - rawSkipComment(p, node) - -proc flexComment(p: var TParser, node: PNode) = - rawSkipComment(p, node) - -proc skipInd(p: var TParser) = discard -proc optPar(p: var TParser) = discard - -proc optInd(p: var TParser, n: PNode) = - skipComment(p, n) - -proc getTokNoInd(p: var TParser) = - getTok(p) - -proc expectIdentOrKeyw(p: TParser) = - if p.tok.tokType != tkSymbol and not isKeyword(p.tok.tokType): - lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok)) - -proc expectIdent(p: TParser) = - if p.tok.tokType != tkSymbol: - lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok)) - -proc eat(p: var TParser, tokType: TTokType) = - ## Move the parser to the next token if the current token is of type - ## `tokType`, otherwise error. - if p.tok.tokType == tokType: - getTok(p) - else: - lexMessageTok(p.lex, errTokenExpected, p.tok, TokTypeToStr[tokType]) - -proc parLineInfo(p: TParser): TLineInfo = - ## Retrieve the line information associated with the parser's current state. - result = getLineInfo(p.lex, p.tok) - -proc indAndComment(p: var TParser, n: PNode) = - rawSkipComment(p, n) - -proc newNodeP(kind: TNodeKind, p: TParser): PNode = - result = newNodeI(kind, parLineInfo(p)) - -proc newIntNodeP(kind: TNodeKind, intVal: BiggestInt, p: TParser): PNode = - result = newNodeP(kind, p) - result.intVal = intVal - -proc newFloatNodeP(kind: TNodeKind, floatVal: BiggestFloat, - p: TParser): PNode = - result = newNodeP(kind, p) - result.floatVal = floatVal - -proc newStrNodeP(kind: TNodeKind, strVal: string, p: TParser): PNode = - result = newNodeP(kind, p) - result.strVal = strVal - -proc newIdentNodeP(ident: PIdent, p: TParser): PNode = - result = newNodeP(nkIdent, p) - result.ident = ident - -proc parseExpr(p: var TParser): PNode -proc parseStmt(p: var TParser): PNode -proc parseTypeDesc(p: var TParser): PNode -proc parseDoBlocks(p: var TParser, call: PNode) -proc parseParamList(p: var TParser, retColon = true): PNode -proc parseStmtPragma(p: var TParser): PNode -proc parseCase(p: var TParser): PNode -proc parseTry(p: var TParser): PNode - -proc isSigilLike(tok: TToken): bool {.inline.} = - result = tok.tokType == tkOpr and tok.ident.s[0] == '@' - -proc isAt(tok: TToken): bool {.inline.} = - tok.tokType == tkOpr and tok.ident.s == "@" and tok.strongSpaceB == 0 - -proc isRightAssociative(tok: TToken): bool {.inline.} = - ## Determines whether the token is right assocative. - result = tok.tokType == tkOpr and tok.ident.s[0] == '^' - # or (let L = tok.ident.s.len; L > 1 and tok.ident.s[L-1] == '>')) - -proc getPrecedence(tok: TToken): int = - ## Calculates the precedence of the given token. - template considerStrongSpaces(x): untyped = x - - case tok.tokType - of tkOpr: - let L = tok.ident.s.len - let relevantChar = tok.ident.s[0] - - # arrow like? - if L > 1 and tok.ident.s[L-1] == '>' and - tok.ident.s[L-2] in {'-', '~', '='}: return considerStrongSpaces(1) - - template considerAsgn(value: untyped) = - result = if tok.ident.s[L-1] == '=': 1 else: value - - case relevantChar - of '$', '^': considerAsgn(10) - of '*', '%', '/', '\\': considerAsgn(9) - of '~': result = 8 - of '+', '-', '|': considerAsgn(8) - of '&': considerAsgn(7) - of '=', '<', '>', '!': result = 5 - of '.': considerAsgn(6) - of '?': result = 2 - else: considerAsgn(2) - of tkDiv, tkMod, tkShl, tkShr: result = 9 - of tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs: result = 5 - of tkDotDot: result = 6 - of tkAnd: result = 4 - of tkOr, tkXor, tkPtr, tkRef: result = 3 - else: return -10 - result = considerStrongSpaces(result) - -proc isOperator(tok: TToken): bool = - ## Determines if the given token is an operator type token. - tok.tokType in {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs, - tkIsnot, tkNot, tkOf, tkAs, tkDotDot, tkAnd, tkOr, tkXor} - -proc isUnary(p: TParser): bool = - ## Check if the current parser token is a unary operator - if p.tok.tokType in {tkOpr, tkDotDot}: - result = true - -proc checkBinary(p: TParser) {.inline.} = - ## Check if the current parser token is a binary operator. - # we don't check '..' here as that's too annoying - discard - -#| module = stmt ^* (';' / IND{=}) -#| -#| comma = ',' COMMENT? -#| semicolon = ';' COMMENT? -#| colon = ':' COMMENT? -#| colcom = ':' COMMENT? -#| -#| operator = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9 -#| | 'or' | 'xor' | 'and' -#| | 'is' | 'isnot' | 'in' | 'notin' | 'of' -#| | 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'static' | '..' -#| -#| prefixOperator = operator -#| -#| optInd = COMMENT? -#| optPar = (IND{>} | IND{=})? -#| -#| simpleExpr = arrowExpr (OP0 optInd arrowExpr)* -#| arrowExpr = assignExpr (OP1 optInd assignExpr)* -#| assignExpr = orExpr (OP2 optInd orExpr)* -#| orExpr = andExpr (OP3 optInd andExpr)* -#| andExpr = cmpExpr (OP4 optInd cmpExpr)* -#| cmpExpr = sliceExpr (OP5 optInd sliceExpr)* -#| sliceExpr = ampExpr (OP6 optInd ampExpr)* -#| ampExpr = plusExpr (OP7 optInd plusExpr)* -#| plusExpr = mulExpr (OP8 optInd mulExpr)* -#| mulExpr = dollarExpr (OP9 optInd dollarExpr)* -#| dollarExpr = primary (OP10 optInd primary)* - -proc colcom(p: var TParser, n: PNode) = - skipComment(p, n) - -proc parseSymbol(p: var TParser, allowNil = false): PNode = - #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' - #| | IDENT | 'addr' | 'type' - case p.tok.tokType - of tkSymbol, tkAddr, tkType: - result = newIdentNodeP(p.tok.ident, p) - getTok(p) - of tkAccent: - result = newNodeP(nkAccQuoted, p) - getTok(p) - while true: - case p.tok.tokType - of tkAccent: - if result.len == 0: - parMessage(p, errIdentifierExpected, p.tok) - break - of tkOpr, tkDot, tkDotDot, tkEquals, tkParLe..tkParDotRi: - var accm = "" - while p.tok.tokType in {tkOpr, tkDot, tkDotDot, tkEquals, - tkParLe..tkParDotRi}: - accm.add(tokToStr(p.tok)) - getTok(p) - result.add(newIdentNodeP(p.lex.cache.getIdent(accm), p)) - of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCharLit: - result.add(newIdentNodeP(p.lex.cache.getIdent(tokToStr(p.tok)), p)) - getTok(p) - else: - parMessage(p, errIdentifierExpected, p.tok) - break - eat(p, tkAccent) - else: - if allowNil and p.tok.tokType == tkNil: - result = newNodeP(nkNilLit, p) - getTok(p) - else: - parMessage(p, errIdentifierExpected, p.tok) - # 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 - -proc colonOrEquals(p: var TParser, a: PNode): PNode = - if p.tok.tokType == tkColon: - result = newNodeP(nkExprColonExpr, p) - getTok(p) - #optInd(p, result) - addSon(result, a) - addSon(result, parseExpr(p)) - elif p.tok.tokType == tkEquals: - result = newNodeP(nkExprEqExpr, p) - getTok(p) - #optInd(p, result) - addSon(result, a) - addSon(result, parseExpr(p)) - else: - result = a - -proc exprColonEqExpr(p: var TParser): PNode = - #| exprColonEqExpr = expr (':'|'=' expr)? - var a = parseExpr(p) - result = colonOrEquals(p, a) - -proc exprList(p: var TParser, endTok: TTokType, result: PNode) = - #| exprList = expr ^+ comma - getTok(p) - optInd(p, result) - while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof): - var a = parseExpr(p) - addSon(result, a) - if p.tok.tokType != tkComma: break - getTok(p) - optInd(p, a) - -proc dotExpr(p: var TParser, a: PNode): PNode = - #| dotExpr = expr '.' optInd symbol - var info = p.parLineInfo - getTok(p) - result = newNodeI(nkDotExpr, info) - optInd(p, result) - addSon(result, a) - addSon(result, parseSymbol(p)) - -proc qualifiedIdent(p: var TParser): PNode = - #| qualifiedIdent = symbol ('.' optInd symbol)? - result = parseSymbol(p) - if p.tok.tokType == tkDot: result = dotExpr(p, result) - -proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) = - assert(endTok in {tkCurlyLe, tkCurlyRi, tkCurlyDotRi, tkBracketRi, tkParRi}) - getTok(p) - optInd(p, result) - while p.tok.tokType != endTok and p.tok.tokType != tkEof: - var a = exprColonEqExpr(p) - addSon(result, a) - if p.tok.tokType != tkComma: break - getTok(p) - skipComment(p, a) - optPar(p) - eat(p, endTok) - -proc exprColonEqExprList(p: var TParser, kind: TNodeKind, - endTok: TTokType): PNode = - #| exprColonEqExprList = exprColonEqExpr (comma exprColonEqExpr)* (comma)? - result = newNodeP(kind, p) - exprColonEqExprListAux(p, endTok, result) - -proc setOrTableConstr(p: var TParser): PNode = - result = newNodeP(nkCurly, p) - getTok(p) - optInd(p, result) - if p.tok.tokType == tkColon: - getTok(p) # skip ':' - result.kind = nkTableConstr - else: - while p.tok.tokType notin {tkBracketDotRi, tkEof}: - var a = exprColonEqExpr(p) - if a.kind == nkExprColonExpr: result.kind = nkTableConstr - addSon(result, a) - if p.tok.tokType != tkComma: break - getTok(p) - skipComment(p, a) - optPar(p) - eat(p, tkBracketDotRi) - -proc parseCast(p: var TParser): PNode = - #| castExpr = 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')' - result = newNodeP(nkCast, p) - getTok(p) - eat(p, tkBracketLe) - optInd(p, result) - addSon(result, parseTypeDesc(p)) - optPar(p) - eat(p, tkBracketRi) - eat(p, tkParLe) - optInd(p, result) - addSon(result, parseExpr(p)) - optPar(p) - eat(p, tkParRi) - -proc setBaseFlags(n: PNode, base: TNumericalBase) = - case base - of base10: discard - of base2: incl(n.flags, nfBase2) - of base8: incl(n.flags, nfBase8) - of base16: incl(n.flags, nfBase16) - -proc parseGStrLit(p: var TParser, a: PNode): PNode = - case p.tok.tokType - of tkGStrLit: - result = newNodeP(nkCallStrLit, p) - addSon(result, a) - addSon(result, newStrNodeP(nkRStrLit, p.tok.literal, p)) - getTok(p) - of tkGTripleStrLit: - result = newNodeP(nkCallStrLit, p) - addSon(result, a) - addSon(result, newStrNodeP(nkTripleStrLit, p.tok.literal, p)) - getTok(p) - else: - result = a - -type - TPrimaryMode = enum pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix - -proc complexOrSimpleStmt(p: var TParser): PNode -proc simpleExpr(p: var TParser, mode = pmNormal): PNode - -proc semiStmtList(p: var TParser, result: PNode) = - inc p.inSemiStmtList - result.add(complexOrSimpleStmt(p)) - while p.tok.tokType == tkSemiColon: - getTok(p) - optInd(p, result) - result.add(complexOrSimpleStmt(p)) - dec p.inSemiStmtList - result.kind = nkStmtListExpr - -proc parsePar(p: var TParser): PNode = - #| parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try' - #| | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let' - #| | 'when' | 'var' | 'mixin' - #| par = '(' optInd - #| ( &parKeyw complexOrSimpleStmt ^+ ';' - #| | ';' complexOrSimpleStmt ^+ ';' - #| | pragmaStmt - #| | simpleExpr ( ('=' expr (';' complexOrSimpleStmt ^+ ';' )? ) - #| | (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) ) - #| optPar ')' - # - # unfortunately it's ambiguous: (expr: expr) vs (exprStmt); however a - # leading ';' could be used to enforce a 'stmt' context ... - result = newNodeP(nkPar, p) - getTok(p) - optInd(p, result) - if p.tok.tokType in {tkDiscard, tkInclude, tkIf, tkWhile, tkCase, - tkTry, tkDefer, tkFinally, tkExcept, tkFor, tkBlock, - tkConst, tkLet, tkWhen, tkVar, - tkMixin}: - # XXX 'bind' used to be an expression, so we exclude it here; - # tests/reject/tbind2 fails otherwise. - semiStmtList(p, result) - elif p.tok.tokType == tkSemiColon: - # '(;' enforces 'stmt' context: - getTok(p) - optInd(p, result) - semiStmtList(p, result) - elif p.tok.tokType == tkCurlyDotLe: - result.add(parseStmtPragma(p)) - elif p.tok.tokType != tkParRi: - var a = simpleExpr(p) - if p.tok.tokType == tkEquals: - # special case: allow assignments - getTok(p) - optInd(p, result) - let b = parseExpr(p) - let asgn = newNodeI(nkAsgn, a.info, 2) - asgn.sons[0] = a - asgn.sons[1] = b - result.add(asgn) - if p.tok.tokType == tkSemiColon: - semiStmtList(p, result) - elif p.tok.tokType == tkSemiColon: - # stmt context: - result.add(a) - semiStmtList(p, result) - else: - a = colonOrEquals(p, a) - result.add(a) - if p.tok.tokType == tkComma: - getTok(p) - skipComment(p, a) - while p.tok.tokType != tkParRi and p.tok.tokType != tkEof: - var a = exprColonEqExpr(p) - addSon(result, a) - if p.tok.tokType != tkComma: break - getTok(p) - skipComment(p, a) - optPar(p) - eat(p, tkParRi) - -proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode = - #| literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT - #| | UINT_LIT | UINT8_LIT | UINT16_LIT | UINT32_LIT | UINT64_LIT - #| | FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT - #| | STR_LIT | RSTR_LIT | TRIPLESTR_LIT - #| | CHAR_LIT - #| | NIL - #| generalizedLit = GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT - #| identOrLiteral = generalizedLit | symbol | literal - #| | par | arrayConstr | setOrTableConstr - #| | castExpr - #| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')' - #| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']' - case p.tok.tokType - of tkSymbol, tkType, tkAddr: - result = newIdentNodeP(p.tok.ident, p) - getTok(p) - result = parseGStrLit(p, result) - of tkAccent: - result = parseSymbol(p) # literals - of tkIntLit: - result = newIntNodeP(nkIntLit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkInt8Lit: - result = newIntNodeP(nkInt8Lit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkInt16Lit: - result = newIntNodeP(nkInt16Lit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkInt32Lit: - result = newIntNodeP(nkInt32Lit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkInt64Lit: - result = newIntNodeP(nkInt64Lit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkUIntLit: - result = newIntNodeP(nkUIntLit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkUInt8Lit: - result = newIntNodeP(nkUInt8Lit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkUInt16Lit: - result = newIntNodeP(nkUInt16Lit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkUInt32Lit: - result = newIntNodeP(nkUInt32Lit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkUInt64Lit: - result = newIntNodeP(nkUInt64Lit, p.tok.iNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkFloatLit: - result = newFloatNodeP(nkFloatLit, p.tok.fNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkFloat32Lit: - result = newFloatNodeP(nkFloat32Lit, p.tok.fNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkFloat64Lit: - result = newFloatNodeP(nkFloat64Lit, p.tok.fNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkFloat128Lit: - result = newFloatNodeP(nkFloat128Lit, p.tok.fNumber, p) - setBaseFlags(result, p.tok.base) - getTok(p) - of tkStrLit: - result = newStrNodeP(nkStrLit, p.tok.literal, p) - getTok(p) - of tkRStrLit: - result = newStrNodeP(nkRStrLit, p.tok.literal, p) - getTok(p) - of tkTripleStrLit: - result = newStrNodeP(nkTripleStrLit, p.tok.literal, p) - getTok(p) - of tkCharLit: - result = newIntNodeP(nkCharLit, ord(p.tok.literal[0]), p) - getTok(p) - of tkNil: - result = newNodeP(nkNilLit, p) - getTok(p) - of tkParLe: - # () constructor - if mode in {pmTypeDesc, pmTypeDef}: - result = exprColonEqExprList(p, nkPar, tkParRi) - else: - result = parsePar(p) - of tkBracketDotLe: - # {} constructor - result = setOrTableConstr(p) - of tkBracketLe: - # [] constructor - result = exprColonEqExprList(p, nkBracket, tkBracketRi) - of tkCast: - result = parseCast(p) - else: - parMessage(p, errExprExpected, p.tok) - getTok(p) # we must consume a token here to prevend endless loops! - result = ast.emptyNode - -proc namedParams(p: var TParser, callee: PNode, - kind: TNodeKind, endTok: TTokType): PNode = - let a = callee - result = newNodeP(kind, p) - addSon(result, a) - exprColonEqExprListAux(p, endTok, result) - -proc parseMacroColon(p: var TParser, x: PNode): PNode -proc primarySuffix(p: var TParser, r: PNode): PNode = - #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks? - #| | doBlocks - #| | '.' optInd symbol generalizedLit? - #| | '[' optInd indexExprList optPar ']' - #| | '{' optInd indexExprList optPar '}' - #| | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax - result = r - - template somePar() = discard - while p.tok.indent < 0: - case p.tok.tokType - of tkParLe: - somePar() - result = namedParams(p, result, nkCall, tkParRi) - if result.len > 1 and result.sons[1].kind == nkExprColonExpr: - result.kind = nkObjConstr - else: - parseDoBlocks(p, result) - of tkDo: - var a = result - result = newNodeP(nkCall, p) - addSon(result, a) - parseDoBlocks(p, result) - of tkDot: - result = dotExpr(p, result) - result = parseGStrLit(p, result) - of tkBracketLe: - somePar() - result = namedParams(p, result, nkBracketExpr, tkBracketRi) - of tkBracketDotLe: - somePar() - result = namedParams(p, result, nkCurlyExpr, tkBracketDotRi) - of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType: - if p.inPragma == 0: - # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet - # solution, but pragmas.nim can't handle that - let a = result - result = newNodeP(nkCommand, p) - addSon(result, a) - when true: - addSon result, parseExpr(p) - else: - while p.tok.tokType != tkEof: - let x = parseExpr(p) - addSon(result, x) - if p.tok.tokType != tkComma: break - getTok(p) - optInd(p, x) - if p.tok.tokType == tkDo: - parseDoBlocks(p, result) - else: - result = parseMacroColon(p, result) - break - else: - break - -proc primary(p: var TParser, mode: TPrimaryMode): PNode -proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode - -proc parseOperators(p: var TParser, headNode: PNode, - limit: int, mode: TPrimaryMode): PNode = - result = headNode - # expand while operators have priorities higher than 'limit' - var opPrec = getPrecedence(p.tok) - let modeB = if mode == pmTypeDef: pmTypeDesc else: mode - # the operator itself must not start on a new line: - while opPrec >= limit and p.tok.indent < 0 and not isAt(p.tok): - checkBinary(p) - var leftAssoc = 1-ord(isRightAssociative(p.tok)) - var a = newNodeP(nkInfix, p) - var opNode = newIdentNodeP(p.tok.ident, p) # skip operator: - getTok(p) - optInd(p, a) - # read sub-expression with higher priority: - var b = simpleExprAux(p, opPrec + leftAssoc, modeB) - addSon(a, opNode) - addSon(a, result) - addSon(a, b) - result = a - opPrec = getPrecedence(p.tok) - -proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode = - result = primary(p, mode) - result = parseOperators(p, result, limit, mode) - -proc simpleExpr(p: var TParser, mode = pmNormal): PNode = - result = simpleExprAux(p, -1, mode) - -proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode = - #| condExpr = expr colcom expr optInd - #| ('elif' expr colcom expr optInd)* - #| 'else' colcom expr - #| ifExpr = 'if' condExpr - #| whenExpr = 'when' condExpr - result = newNodeP(kind, p) - while true: - getTok(p) # skip `if`, `elif` - var branch = newNodeP(nkElifExpr, p) - addSon(branch, parseExpr(p)) - colcom(p, branch) - addSon(branch, parseExpr(p)) - optInd(p, branch) - addSon(result, branch) - if p.tok.tokType != tkElif: break - var branch = newNodeP(nkElseExpr, p) - eat(p, tkElse) - colcom(p, branch) - addSon(branch, parseExpr(p)) - addSon(result, branch) - -proc parsePragma(p: var TParser): PNode = - result = newNodeP(nkPragma, p) - inc p.inPragma - if isAt(p.tok): - while isAt(p.tok): - getTok(p) - var a = parseExpr(p) - optInd(p, a) - if a.kind in nkCallKinds and a.len == 2: - let repaired = newNodeI(nkExprColonExpr, a.info) - repaired.add a[0] - repaired.add a[1] - a = repaired - addSon(result, a) - skipComment(p, a) - else: - getTok(p) - optInd(p, result) - while p.tok.tokType notin {tkCurlyDotRi, tkCurlyRi, tkEof}: - var a = exprColonEqExpr(p) - addSon(result, a) - if p.tok.tokType == tkComma: - getTok(p) - skipComment(p, a) - optPar(p) - if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: getTok(p) - else: parMessage(p, errTokenExpected, ".}") - dec p.inPragma - -proc identVis(p: var TParser; allowDot=false): PNode = - #| identVis = symbol opr? # postfix position - #| identVisDot = symbol '.' optInd symbol opr? - var a = parseSymbol(p) - if p.tok.tokType == tkOpr: - result = newNodeP(nkPostfix, p) - addSon(result, newIdentNodeP(p.tok.ident, p)) - addSon(result, a) - getTok(p) - elif p.tok.tokType == tkDot and allowDot: - result = dotExpr(p, a) - else: - result = a - -proc identWithPragma(p: var TParser; allowDot=false): PNode = - #| identWithPragma = identVis pragma? - #| identWithPragmaDot = identVisDot pragma? - var a = identVis(p, allowDot) - if p.tok.tokType == tkCurlyDotLe or isAt(p.tok): - result = newNodeP(nkPragmaExpr, p) - addSon(result, a) - addSon(result, parsePragma(p)) - else: - result = a - -type - TDeclaredIdentFlag = enum - withPragma, # identifier may have pragma - withBothOptional # both ':' and '=' parts are optional - TDeclaredIdentFlags = set[TDeclaredIdentFlag] - -proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode = - #| declColonEquals = identWithPragma (comma identWithPragma)* comma? - #| (':' optInd typeDesc)? ('=' optInd expr)? - #| identColonEquals = ident (comma ident)* comma? - #| (':' optInd typeDesc)? ('=' optInd expr)?) - var a: PNode - result = newNodeP(nkIdentDefs, p) - while true: - case p.tok.tokType - of tkSymbol, tkAccent: - if withPragma in flags: a = identWithPragma(p) - else: a = parseSymbol(p) - if a.kind == nkEmpty: return - else: break - addSon(result, a) - if p.tok.tokType != tkComma: break - getTok(p) - optInd(p, a) - if p.tok.tokType == tkColon: - getTok(p) - optInd(p, result) - addSon(result, parseTypeDesc(p)) - else: - addSon(result, ast.emptyNode) - if p.tok.tokType != tkEquals and withBothOptional notin flags: - parMessage(p, errColonOrEqualsExpected, p.tok) - if p.tok.tokType == tkEquals: - getTok(p) - optInd(p, result) - addSon(result, parseExpr(p)) - else: - addSon(result, ast.emptyNode) - -proc parseTuple(p: var TParser): PNode = - result = newNodeP(nkTupleTy, p) - getTok(p) - if p.tok.tokType in {tkBracketLe, tkCurlyLe}: - let usedCurly = p.tok.tokType == tkCurlyLe - getTok(p) - optInd(p, result) - while p.tok.tokType in {tkSymbol, tkAccent}: - var a = parseIdentColonEquals(p, {}) - addSon(result, a) - if p.tok.tokType notin {tkComma, tkSemiColon}: break - getTok(p) - skipComment(p, a) - optPar(p) - if usedCurly: eat(p, tkCurlyRi) - else: eat(p, tkBracketRi) - else: - result = newNodeP(nkTupleClassTy, p) - -proc parseParamList(p: var TParser, retColon = true): PNode = - #| paramList = '(' declColonEquals ^* (comma/semicolon) ')' - #| paramListArrow = paramList? ('->' optInd typeDesc)? - #| paramListColon = paramList? (':' optInd typeDesc)? - var a: PNode - result = newNodeP(nkFormalParams, p) - addSon(result, ast.emptyNode) # return type - let hasParLe = p.tok.tokType == tkParLe and p.tok.indent < 0 - if hasParLe: - getTok(p) - optInd(p, result) - while true: - case p.tok.tokType - of tkSymbol, tkAccent: - a = parseIdentColonEquals(p, {withBothOptional, withPragma}) - of tkParRi: - break - else: - parMessage(p, errTokenExpected, ")") - break - addSon(result, a) - if p.tok.tokType notin {tkComma, tkSemiColon}: break - getTok(p) - skipComment(p, a) - optPar(p) - eat(p, tkParRi) - let hasRet = if retColon: p.tok.tokType == tkColon - else: p.tok.tokType == tkOpr and p.tok.ident.s == "->" - if hasRet and p.tok.indent < 0: - getTok(p) - optInd(p, result) - result.sons[0] = parseTypeDesc(p) - elif not retColon and not hasParle: - # Mark as "not there" in order to mark for deprecation in the semantic pass: - result = ast.emptyNode - -proc optPragmas(p: var TParser): PNode = - if p.tok.tokType == tkCurlyDotLe or isAt(p.tok): - result = parsePragma(p) - else: - result = ast.emptyNode - -proc parseDoBlock(p: var TParser): PNode = - #| doBlock = 'do' paramListArrow pragmas? colcom stmt - let info = parLineInfo(p) - getTok(p) - let params = parseParamList(p, retColon=false) - let pragmas = optPragmas(p) - colcom(p, result) - result = newProcNode(nkDo, info, parseStmt(p), - params = params, - pragmas = pragmas) - -proc parseDoBlocks(p: var TParser, call: PNode) = - #| doBlocks = doBlock ^* IND{=} - while p.tok.tokType == tkDo: - addSon(call, parseDoBlock(p)) - -proc parseCurlyStmt(p: var TParser): PNode = - result = newNodeP(nkStmtList, p) - eat(p, tkCurlyLe) - result.add parseStmt(p) - while p.tok.tokType notin {tkEof, tkCurlyRi}: - if p.tok.tokType == tkSemicolon: getTok(p) - elif p.tok.indent < 0: break - result.add parseStmt(p) - eat(p, tkCurlyRi) - -proc parseProcExpr(p: var TParser, isExpr: bool): PNode = - #| procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)? - # either a proc type or a anonymous proc - let info = parLineInfo(p) - getTok(p) - let hasSignature = p.tok.tokType in {tkParLe, tkColon} and p.tok.indent < 0 - let params = parseParamList(p) - let pragmas = optPragmas(p) - if p.tok.tokType == tkCurlyLe and isExpr: - result = newProcNode(nkLambda, info, parseCurlyStmt(p), - params = params, - pragmas = pragmas) - else: - result = newNodeI(nkProcTy, info) - if hasSignature: - addSon(result, params) - addSon(result, pragmas) - -proc isExprStart(p: TParser): bool = - case p.tok.tokType - of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, - tkProc, tkIterator, tkBind, tkAddr, - tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr, - tkTuple, tkObject, tkType, tkWhen, tkCase, tkOut: - result = true - else: result = false - -proc parseSymbolList(p: var TParser, result: PNode, allowNil = false) = - while true: - var s = parseSymbol(p, allowNil) - if s.kind == nkEmpty: break - addSon(result, s) - if p.tok.tokType != tkComma: break - getTok(p) - optInd(p, s) - -proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, - mode: TPrimaryMode): PNode = - #| distinct = 'distinct' optInd typeDesc - result = newNodeP(kind, p) - getTok(p) - optInd(p, result) - if not isOperator(p.tok) and isExprStart(p): - addSon(result, primary(p, mode)) - if kind == nkDistinctTy and p.tok.tokType == tkSymbol: - var nodeKind: TNodeKind - if p.tok.ident.s == "with": - nodeKind = nkWith - elif p.tok.ident.s == "without": - nodeKind = nkWithout - else: - return result - getTok(p) - let list = newNodeP(nodeKind, p) - result.addSon list - parseSymbolList(p, list, allowNil = true) - -proc parseExpr(p: var TParser): PNode = - #| expr = (ifExpr - #| | whenExpr - #| | caseExpr - #| | tryExpr) - #| / simpleExpr - case p.tok.tokType: - of tkIf: result = parseIfExpr(p, nkIfExpr) - of tkWhen: result = parseIfExpr(p, nkWhenExpr) - of tkCase: result = parseCase(p) - of tkTry: result = parseTry(p) - else: result = simpleExpr(p) - -proc parseEnum(p: var TParser): PNode -proc parseObject(p: var TParser): PNode -proc parseTypeClass(p: var TParser): PNode - -proc primary(p: var TParser, mode: TPrimaryMode): PNode = - #| typeKeyw = 'var' | 'out' | 'ref' | 'ptr' | 'shared' | 'tuple' - #| | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum' - #| primary = typeKeyw typeDescK - #| / prefixOperator* identOrLiteral primarySuffix* - #| / 'static' primary - #| / 'bind' primary - if isOperator(p.tok): - let isSigil = isSigilLike(p.tok) - result = newNodeP(nkPrefix, p) - var a = newIdentNodeP(p.tok.ident, p) - addSon(result, a) - getTok(p) - optInd(p, a) - if isSigil: - #XXX prefix operators - addSon(result, primary(p, pmSkipSuffix)) - result = primarySuffix(p, result) - else: - addSon(result, primary(p, pmNormal)) - return - - case p.tok.tokType: - of tkTuple: result = parseTuple(p) - of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}) - of tkIterator: - result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}) - if result.kind == nkLambda: result.kind = nkIteratorDef - else: result.kind = nkIteratorTy - of tkEnum: - if mode == pmTypeDef: - result = parseEnum(p) - else: - result = newNodeP(nkEnumTy, p) - getTok(p) - of tkObject: - if mode == pmTypeDef: - result = parseObject(p) - else: - result = newNodeP(nkObjectTy, p) - getTok(p) - of tkConcept: - if mode == pmTypeDef: - result = parseTypeClass(p) - else: - parMessage(p, errInvalidToken, p.tok) - of tkStatic: - let info = parLineInfo(p) - getTokNoInd(p) - let next = primary(p, pmNormal) - if next.kind == nkBracket and next.sonsLen == 1: - result = newNode(nkStaticTy, info, @[next.sons[0]]) - else: - result = newNode(nkStaticExpr, info, @[next]) - of tkBind: - result = newNodeP(nkBind, p) - getTok(p) - optInd(p, result) - addSon(result, primary(p, pmNormal)) - of tkVar: result = parseTypeDescKAux(p, nkVarTy, mode) - of tkOut: 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: - result = identOrLiteral(p, mode) - if mode != pmSkipSuffix: - result = primarySuffix(p, result) - -proc parseTypeDesc(p: var TParser): PNode = - #| typeDesc = simpleExpr - result = simpleExpr(p, pmTypeDesc) - -proc parseTypeDefAux(p: var TParser): PNode = - #| typeDefAux = simpleExpr - #| | 'concept' typeClass - result = simpleExpr(p, pmTypeDef) - -proc makeCall(n: PNode): PNode = - ## Creates a call if the given node isn't already a call. - if n.kind in nkCallKinds: - result = n - else: - result = newNodeI(nkCall, n.info) - result.add n - -proc parseMacroColon(p: var TParser, x: PNode): PNode = - #| macroColon = ':' stmt? ( IND{=} 'of' exprList ':' stmt - #| | IND{=} 'elif' expr ':' stmt - #| | IND{=} 'except' exprList ':' stmt - #| | IND{=} 'else' ':' stmt )* - result = x - if p.tok.tokType == tkColon and p.tok.indent < 0: - result = makeCall(result) - getTok(p) - skipComment(p, result) - let stmtList = newNodeP(nkStmtList, p) - if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}: - let body = parseStmt(p) - stmtList.add body - #addSon(result, makeStmtList(body)) - while true: - var b: PNode - case p.tok.tokType - of tkOf: - b = newNodeP(nkOfBranch, p) - exprList(p, tkCurlyLe, b) - of tkElif: - b = newNodeP(nkElifBranch, p) - getTok(p) - optInd(p, b) - addSon(b, parseExpr(p)) - of tkExcept: - b = newNodeP(nkExceptBranch, p) - exprList(p, tkCurlyLe, b) - of tkElse: - b = newNodeP(nkElse, p) - getTok(p) - else: break - addSon(b, parseCurlyStmt(p)) - addSon(stmtList, b) - 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 = - #| exprStmt = simpleExpr - #| (( '=' optInd expr ) - #| / ( expr ^+ comma - #| doBlocks - #| / macroColon - #| ))? - var a = simpleExpr(p) - if p.tok.tokType == tkEquals: - getTok(p) - optInd(p, result) - var b = parseExpr(p) - result = newNodeI(nkAsgn, a.info) - addSon(result, a) - addSon(result, b) - else: - # simpleExpr parsed 'p a' from 'p a, b'? - 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: - var e = parseExpr(p) - addSon(result, e) - if p.tok.tokType != tkComma: break - getTok(p) - optInd(p, result) - else: - result = a - if p.tok.tokType == tkDo and p.tok.indent < 0: - result = makeCall(result) - parseDoBlocks(p, result) - return result - result = parseMacroColon(p, result) - -proc parseModuleName(p: var TParser, kind: TNodeKind): PNode = - result = parseExpr(p) - -proc parseImport(p: var TParser, kind: TNodeKind): PNode = - #| importStmt = 'import' optInd expr - #| ((comma expr)* - #| / 'except' optInd (expr ^+ comma)) - result = newNodeP(kind, p) - getTok(p) # skip `import` or `export` - optInd(p, result) - var a = parseModuleName(p, kind) - addSon(result, a) - if p.tok.tokType in {tkComma, tkExcept}: - if p.tok.tokType == tkExcept: - result.kind = succ(kind) - getTok(p) - optInd(p, result) - while true: - # was: while p.tok.tokType notin {tkEof, tkSad, tkDed}: - a = parseModuleName(p, kind) - if a.kind == nkEmpty: break - addSon(result, a) - if p.tok.tokType != tkComma: break - getTok(p) - optInd(p, a) - #expectNl(p) - -proc parseIncludeStmt(p: var TParser): PNode = - #| includeStmt = 'include' optInd expr ^+ comma - result = newNodeP(nkIncludeStmt, p) - getTok(p) # skip `import` or `include` - optInd(p, result) - while true: - # was: while p.tok.tokType notin {tkEof, tkSad, tkDed}: - var a = parseExpr(p) - if a.kind == nkEmpty: break - addSon(result, a) - if p.tok.tokType != tkComma: break - getTok(p) - optInd(p, a) - #expectNl(p) - -proc parseFromStmt(p: var TParser): PNode = - #| fromStmt = 'from' moduleName 'import' optInd expr (comma expr)* - result = newNodeP(nkFromStmt, p) - getTok(p) # skip `from` - optInd(p, result) - var a = parseModuleName(p, nkImportStmt) - addSon(result, a) #optInd(p, a); - eat(p, tkImport) - optInd(p, result) - while true: - # p.tok.tokType notin {tkEof, tkSad, tkDed}: - a = parseExpr(p) - if a.kind == nkEmpty: break - addSon(result, a) - if p.tok.tokType != tkComma: break - getTok(p) - optInd(p, a) - #expectNl(p) - -proc parseReturnOrRaise(p: var TParser, kind: TNodeKind): PNode = - #| returnStmt = 'return' optInd expr? - #| raiseStmt = 'raise' optInd expr? - #| yieldStmt = 'yield' optInd expr? - #| discardStmt = 'discard' optInd expr? - #| breakStmt = 'break' optInd expr? - #| continueStmt = 'break' optInd expr? - result = newNodeP(kind, p) - getTok(p) - if p.tok.tokType == tkComment: - skipComment(p, result) - addSon(result, ast.emptyNode) - elif p.tok.indent >= 0 or not isExprStart(p): - # NL terminates: - addSon(result, ast.emptyNode) - else: - addSon(result, parseExpr(p)) - -proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode = - #| condStmt = expr colcom stmt COMMENT? - #| (IND{=} 'elif' expr colcom stmt)* - #| (IND{=} 'else' colcom stmt)? - #| ifStmt = 'if' condStmt - #| whenStmt = 'when' condStmt - result = newNodeP(kind, p) - while true: - getTok(p) # skip `if`, `when`, `elif` - var branch = newNodeP(nkElifBranch, p) - optInd(p, branch) - addSon(branch, parseExpr(p)) - colcom(p, branch) - addSon(branch, parseCurlyStmt(p)) - skipComment(p, branch) - addSon(result, branch) - if p.tok.tokType != tkElif: break - if p.tok.tokType == tkElse: - var branch = newNodeP(nkElse, p) - eat(p, tkElse) - addSon(branch, parseCurlyStmt(p)) - addSon(result, branch) - -proc parseWhile(p: var TParser): PNode = - #| whileStmt = 'while' expr colcom stmt - result = newNodeP(nkWhileStmt, p) - getTok(p) - optInd(p, result) - addSon(result, parseExpr(p)) - colcom(p, result) - addSon(result, parseCurlyStmt(p)) - -proc parseCase(p: var TParser): PNode = - #| ofBranch = 'of' exprList colcom stmt - #| ofBranches = ofBranch (IND{=} ofBranch)* - #| (IND{=} 'elif' expr colcom stmt)* - #| (IND{=} 'else' colcom stmt)? - #| caseStmt = 'case' expr ':'? COMMENT? - #| (IND{>} ofBranches DED - #| | IND{=} ofBranches) - var - b: PNode - inElif= false - result = newNodeP(nkCaseStmt, p) - getTok(p) - addSon(result, parseExpr(p)) - eat(p, tkCurlyLe) - skipComment(p, result) - - while true: - case p.tok.tokType - of tkOf: - if inElif: break - b = newNodeP(nkOfBranch, p) - exprList(p, tkCurlyLe, b) - of tkElif: - inElif = true - b = newNodeP(nkElifBranch, p) - getTok(p) - optInd(p, b) - addSon(b, parseExpr(p)) - of tkElse: - b = newNodeP(nkElse, p) - getTok(p) - else: break - skipComment(p, b) - addSon(b, parseCurlyStmt(p)) - addSon(result, b) - if b.kind == nkElse: break - eat(p, tkCurlyRi) - -proc parseTry(p: var TParser): PNode = - #| tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally') - #| (IND{=}? 'except' exprList colcom stmt)* - #| (IND{=}? 'finally' colcom stmt)? - #| tryExpr = 'try' colcom stmt &(optInd 'except'|'finally') - #| (optInd 'except' exprList colcom stmt)* - #| (optInd 'finally' colcom stmt)? - result = newNodeP(nkTryStmt, p) - getTok(p) - colcom(p, result) - addSon(result, parseCurlyStmt(p)) - var b: PNode = nil - while true: - case p.tok.tokType - of tkExcept: - b = newNodeP(nkExceptBranch, p) - exprList(p, tkCurlyLe, b) - of tkFinally: - b = newNodeP(nkFinally, p) - getTok(p) - else: break - skipComment(p, b) - addSon(b, parseCurlyStmt(p)) - addSon(result, b) - if b.kind == nkFinally: break - if b == nil: parMessage(p, errTokenExpected, "except") - -proc parseFor(p: var TParser): PNode = - #| forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt - result = newNodeP(nkForStmt, p) - getTokNoInd(p) - var a = identWithPragma(p) - addSon(result, a) - while p.tok.tokType == tkComma: - getTok(p) - optInd(p, a) - a = identWithPragma(p) - addSon(result, a) - eat(p, tkIn) - addSon(result, parseExpr(p)) - colcom(p, result) - addSon(result, parseCurlyStmt(p)) - -proc parseBlock(p: var TParser): PNode = - #| blockStmt = 'block' symbol? colcom stmt - result = newNodeP(nkBlockStmt, p) - getTokNoInd(p) - if p.tok.tokType == tkCurlyLe: addSon(result, ast.emptyNode) - else: addSon(result, parseSymbol(p)) - colcom(p, result) - addSon(result, parseCurlyStmt(p)) - -proc parseStaticOrDefer(p: var TParser; k: TNodeKind): PNode = - #| staticStmt = 'static' colcom stmt - #| deferStmt = 'defer' colcom stmt - result = newNodeP(k, p) - getTok(p) - colcom(p, result) - addSon(result, parseCurlyStmt(p)) - -proc parseAsm(p: var TParser): PNode = - #| asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLE_STR_LIT) - result = newNodeP(nkAsmStmt, p) - getTokNoInd(p) - if p.tok.tokType == tkCurlyDotLe or isAt(p.tok): addSon(result, parsePragma(p)) - else: addSon(result, ast.emptyNode) - case p.tok.tokType - of tkStrLit: addSon(result, newStrNodeP(nkStrLit, p.tok.literal, p)) - of tkRStrLit: addSon(result, newStrNodeP(nkRStrLit, p.tok.literal, p)) - of tkTripleStrLit: addSon(result, - newStrNodeP(nkTripleStrLit, p.tok.literal, p)) - else: - parMessage(p, errStringLiteralExpected) - addSon(result, ast.emptyNode) - return - getTok(p) - -proc parseGenericParam(p: var TParser): PNode = - #| genericParam = symbol (comma symbol)* (colon expr)? ('=' optInd expr)? - var a: PNode - result = newNodeP(nkIdentDefs, p) - while true: - case p.tok.tokType - of tkIn, tkOut: - let t = p.tok.tokType - getTok(p) - expectIdent(p) - a = parseSymbol(p) - of tkSymbol, tkAccent: - a = parseSymbol(p) - if a.kind == nkEmpty: return - else: break - addSon(result, a) - if p.tok.tokType != tkComma: break - getTok(p) - optInd(p, a) - if p.tok.tokType == tkColon: - getTok(p) - optInd(p, result) - addSon(result, parseExpr(p)) - else: - addSon(result, ast.emptyNode) - if p.tok.tokType == tkEquals: - getTok(p) - optInd(p, result) - addSon(result, parseExpr(p)) - else: - addSon(result, ast.emptyNode) - -proc parseGenericParamList(p: var TParser): PNode = - #| genericParamList = '[' optInd - #| genericParam ^* (comma/semicolon) optPar ']' - result = newNodeP(nkGenericParams, p) - getTok(p) - optInd(p, result) - while p.tok.tokType in {tkSymbol, tkAccent}: - var a = parseGenericParam(p) - addSon(result, a) - if p.tok.tokType notin {tkComma, tkSemiColon}: break - getTok(p) - skipComment(p, a) - optPar(p) - eat(p, tkBracketRi) - -proc parsePattern(p: var TParser): PNode = - eat(p, tkBracketDotLe) - result = parseStmt(p) - eat(p, tkBracketDotRi) - -proc validInd(p: TParser): bool = p.tok.indent < 0 - -proc parseRoutine(p: var TParser, kind: TNodeKind): PNode = - #| indAndComment = (IND{>} COMMENT)? | COMMENT? - #| routine = optInd identVis pattern? genericParamList? - #| paramListColon pragma? ('=' COMMENT? stmt)? indAndComment - result = newNodeP(kind, p) - getTok(p) - optInd(p, result) - addSon(result, identVis(p)) - if p.tok.tokType == tkBracketDotLe and p.validInd: - addSon(result, p.parsePattern) - else: - addSon(result, ast.emptyNode) - if p.tok.tokType == tkBracketLe and p.validInd: - result.add(p.parseGenericParamList) - else: - addSon(result, ast.emptyNode) - addSon(result, p.parseParamList) - if (p.tok.tokType == tkCurlyDotLe or isAt(p.tok)) and p.validInd: - addSon(result, p.parsePragma) - else: - addSon(result, ast.emptyNode) - # empty exception tracking: - addSon(result, ast.emptyNode) - if p.tok.tokType == tkCurlyLe: - addSon(result, parseCurlyStmt(p)) - else: - addSon(result, ast.emptyNode) - indAndComment(p, result) - -proc newCommentStmt(p: var TParser): PNode = - #| commentStmt = COMMENT - result = newNodeP(nkCommentStmt, p) - result.comment = p.tok.literal - getTok(p) - -type - TDefParser = proc (p: var TParser): PNode {.nimcall.} - -proc parseSection(p: var TParser, kind: TNodeKind, - defparser: TDefParser): PNode = - #| section(p) = COMMENT? p / (IND{>} (p / COMMENT)^+IND{=} DED) - result = newNodeP(kind, p) - if kind != nkTypeSection: getTok(p) - skipComment(p, result) - if p.tok.tokType == tkParLe: - getTok(p) - skipComment(p, result) - while true: - case p.tok.tokType - of tkSymbol, tkAccent, tkParLe: - var a = defparser(p) - skipComment(p, a) - addSon(result, a) - of tkComment: - var a = newCommentStmt(p) - addSon(result, a) - of tkParRi: break - else: - parMessage(p, errIdentifierExpected, p.tok) - break - eat(p, tkParRi) - if result.len == 0: parMessage(p, errIdentifierExpected, p.tok) - elif p.tok.tokType in {tkSymbol, tkAccent, tkBracketLe}: - # tkBracketLe is allowed for ``var [x, y] = ...`` tuple parsing - addSon(result, defparser(p)) - else: - parMessage(p, errIdentifierExpected, p.tok) - -proc parseConstant(p: var TParser): PNode = - #| constant = identWithPragma (colon typedesc)? '=' optInd expr indAndComment - result = newNodeP(nkConstDef, p) - addSon(result, identWithPragma(p)) - if p.tok.tokType == tkColon: - getTok(p) - optInd(p, result) - addSon(result, parseTypeDesc(p)) - else: - addSon(result, ast.emptyNode) - eat(p, tkEquals) - optInd(p, result) - addSon(result, parseExpr(p)) - indAndComment(p, result) - -proc parseEnum(p: var TParser): PNode = - #| enum = 'enum' optInd (symbol optInd ('=' optInd expr COMMENT?)? comma?)+ - result = newNodeP(nkEnumTy, p) - getTok(p) - addSon(result, ast.emptyNode) - optInd(p, result) - flexComment(p, result) - eat(p, tkCurlyLe) - optInd(p, result) - while p.tok.tokType notin {tkEof, tkCurlyRi}: - var a = parseSymbol(p) - if a.kind == nkEmpty: return - if p.tok.tokType == tkEquals: - getTok(p) - optInd(p, a) - var b = a - a = newNodeP(nkEnumFieldDef, p) - addSon(a, b) - addSon(a, parseExpr(p)) - if p.tok.indent < 0: - rawSkipComment(p, a) - if p.tok.tokType == tkComma: - getTok(p) - rawSkipComment(p, a) - addSon(result, a) - eat(p, tkCurlyRi) - if result.len <= 1: - lexMessageTok(p.lex, errIdentifierExpected, p.tok, prettyTok(p.tok)) - -proc parseObjectPart(p: var TParser; needsCurly: bool): PNode -proc parseObjectWhen(p: var TParser): PNode = - result = newNodeP(nkRecWhen, p) - while true: - getTok(p) # skip `when`, `elif` - var branch = newNodeP(nkElifBranch, p) - optInd(p, branch) - addSon(branch, parseExpr(p)) - colcom(p, branch) - addSon(branch, parseObjectPart(p, true)) - flexComment(p, branch) - addSon(result, branch) - if p.tok.tokType != tkElif: break - if p.tok.tokType == tkElse: - var branch = newNodeP(nkElse, p) - eat(p, tkElse) - colcom(p, branch) - addSon(branch, parseObjectPart(p, true)) - flexComment(p, branch) - addSon(result, branch) - -proc parseObjectCase(p: var TParser): PNode = - result = newNodeP(nkRecCase, p) - getTokNoInd(p) - var a = newNodeP(nkIdentDefs, p) - addSon(a, identWithPragma(p)) - eat(p, tkColon) - addSon(a, parseTypeDesc(p)) - addSon(a, ast.emptyNode) - addSon(result, a) - eat(p, tkCurlyLe) - flexComment(p, result) - while true: - var b: PNode - case p.tok.tokType - of tkOf: - b = newNodeP(nkOfBranch, p) - exprList(p, tkColon, b) - of tkElse: - b = newNodeP(nkElse, p) - getTok(p) - else: break - colcom(p, b) - var fields = parseObjectPart(p, true) - if fields.kind == nkEmpty: - parMessage(p, errIdentifierExpected, p.tok) - fields = newNodeP(nkNilLit, p) # don't break further semantic checking - addSon(b, fields) - addSon(result, b) - if b.kind == nkElse: break - eat(p, tkCurlyRi) - -proc parseObjectPart(p: var TParser; needsCurly: bool): PNode = - if p.tok.tokType == tkCurlyLe: - result = newNodeP(nkRecList, p) - getTok(p) - rawSkipComment(p, result) - while true: - case p.tok.tokType - of tkCase, tkWhen, tkSymbol, tkAccent, tkNil, tkDiscard: - addSon(result, parseObjectPart(p, false)) - of tkCurlyRi: break - else: - parMessage(p, errIdentifierExpected, p.tok) - break - eat(p, tkCurlyRi) - else: - if needsCurly: - parMessage(p, errTokenExpected, "{") - case p.tok.tokType - of tkWhen: - result = parseObjectWhen(p) - of tkCase: - result = parseObjectCase(p) - of tkSymbol, tkAccent: - result = parseIdentColonEquals(p, {withPragma}) - if p.tok.indent < 0: rawSkipComment(p, result) - of tkNil, tkDiscard: - result = newNodeP(nkNilLit, p) - getTok(p) - else: - result = ast.emptyNode - -proc parseObject(p: var TParser): PNode = - result = newNodeP(nkObjectTy, p) - getTok(p) - if (p.tok.tokType == tkCurlyDotLe or isAt(p.tok)) and p.validInd: - addSon(result, parsePragma(p)) - else: - addSon(result, ast.emptyNode) - if p.tok.tokType == tkOf and p.tok.indent < 0: - var a = newNodeP(nkOfInherit, p) - getTok(p) - addSon(a, parseTypeDesc(p)) - addSon(result, a) - else: - addSon(result, ast.emptyNode) - skipComment(p, result) - # an initial IND{>} HAS to follow: - addSon(result, parseObjectPart(p, true)) - -proc parseTypeClassParam(p: var TParser): PNode = - if p.tok.tokType in {tkOut, tkVar}: - result = newNodeP(nkVarTy, p) - getTok(p) - result.addSon(p.parseSymbol) - else: - result = p.parseSymbol - -proc parseTypeClass(p: var TParser): PNode = - #| typeClassParam = ('var' | 'out')? symbol - #| typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')? - #| &IND{>} stmt - result = newNodeP(nkTypeClassTy, p) - getTok(p) - var args = newNodeP(nkArgList, p) - addSon(result, args) - addSon(args, p.parseTypeClassParam) - while p.tok.tokType == tkComma: - getTok(p) - addSon(args, p.parseTypeClassParam) - if (p.tok.tokType == tkCurlyDotLe or isAt(p.tok)) and p.validInd: - addSon(result, parsePragma(p)) - else: - addSon(result, ast.emptyNode) - if p.tok.tokType == tkOf and p.tok.indent < 0: - var a = newNodeP(nkOfInherit, p) - getTok(p) - while true: - addSon(a, parseTypeDesc(p)) - if p.tok.tokType != tkComma: break - getTok(p) - addSon(result, a) - else: - addSon(result, ast.emptyNode) - if p.tok.tokType == tkComment: - skipComment(p, result) - addSon(result, parseCurlyStmt(p)) - -proc parseTypeDef(p: var TParser): PNode = - #| - #| typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux - #| indAndComment? - result = newNodeP(nkTypeDef, p) - addSon(result, identWithPragma(p, allowDot=true)) - if p.tok.tokType == tkBracketLe and p.validInd: - addSon(result, parseGenericParamList(p)) - else: - addSon(result, ast.emptyNode) - if p.tok.tokType == tkEquals: - getTok(p) - optInd(p, result) - addSon(result, parseTypeDefAux(p)) - else: - addSon(result, ast.emptyNode) - indAndComment(p, result) # special extension! - -proc parseVarTuple(p: var TParser): PNode = - #| varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr - result = newNodeP(nkVarTuple, p) - getTok(p) # skip '(' - optInd(p, result) - while p.tok.tokType in {tkSymbol, tkAccent}: - var a = identWithPragma(p) - addSon(result, a) - if p.tok.tokType != tkComma: break - getTok(p) - skipComment(p, a) - addSon(result, ast.emptyNode) # no type desc - optPar(p) - eat(p, tkBracketRi) - eat(p, tkEquals) - optInd(p, result) - addSon(result, parseExpr(p)) - -proc parseVariable(p: var TParser): PNode = - #| variable = (varTuple / identColonEquals) indAndComment - if p.tok.tokType == tkBracketLe: result = parseVarTuple(p) - else: result = parseIdentColonEquals(p, {withPragma}) - indAndComment(p, result) - -proc parseBind(p: var TParser, k: TNodeKind): PNode = - #| bindStmt = 'bind' optInd qualifiedIdent ^+ comma - #| mixinStmt = 'mixin' optInd qualifiedIdent ^+ comma - result = newNodeP(k, p) - getTok(p) - optInd(p, result) - while true: - var a = qualifiedIdent(p) - addSon(result, a) - if p.tok.tokType != tkComma: break - getTok(p) - optInd(p, a) - -proc parseStmtPragma(p: var TParser): PNode = - result = parsePragma(p) - if p.tok.tokType == tkCurlyLe: - let a = result - result = newNodeI(nkPragmaBlock, a.info) - getTok(p) - skipComment(p, result) - result.add a - result.add parseStmt(p) - eat(p, tkCurlyRi) - -proc simpleStmt(p: var TParser): PNode = - case p.tok.tokType - of tkReturn: result = parseReturnOrRaise(p, nkReturnStmt) - of tkRaise: result = parseReturnOrRaise(p, nkRaiseStmt) - of tkYield: result = parseReturnOrRaise(p, nkYieldStmt) - of tkDiscard: result = parseReturnOrRaise(p, nkDiscardStmt) - of tkBreak: result = parseReturnOrRaise(p, nkBreakStmt) - of tkContinue: result = parseReturnOrRaise(p, nkContinueStmt) - of tkCurlyDotLe: result = parseStmtPragma(p) - of tkImport: result = parseImport(p, nkImportStmt) - of tkExport: result = parseImport(p, nkExportStmt) - of tkFrom: result = parseFromStmt(p) - of tkInclude: result = parseIncludeStmt(p) - of tkComment: result = newCommentStmt(p) - else: - if isExprStart(p): result = parseExprStmt(p) - else: result = ast.emptyNode - if result.kind notin {nkEmpty, nkCommentStmt}: skipComment(p, result) - -proc complexOrSimpleStmt(p: var TParser): PNode = - case p.tok.tokType - of tkIf: result = parseIfOrWhen(p, nkIfStmt) - of tkWhile: result = parseWhile(p) - of tkCase: result = parseCase(p) - of tkTry: result = parseTry(p) - of tkFor: result = parseFor(p) - of tkBlock: result = parseBlock(p) - of tkStatic: result = parseStaticOrDefer(p, nkStaticStmt) - of tkDefer: result = parseStaticOrDefer(p, nkDefer) - of tkAsm: result = parseAsm(p) - of tkProc: result = parseRoutine(p, nkProcDef) - of tkMethod: result = parseRoutine(p, nkMethodDef) - of tkIterator: result = parseRoutine(p, nkIteratorDef) - of tkMacro: result = parseRoutine(p, nkMacroDef) - of tkTemplate: result = parseRoutine(p, nkTemplateDef) - of tkConverter: result = parseRoutine(p, nkConverterDef) - of tkType: - getTok(p) - if p.tok.tokType == tkBracketLe: - getTok(p) - result = newNodeP(nkTypeOfExpr, p) - result.addSon(primary(p, pmTypeDesc)) - eat(p, tkBracketRi) - result = parseOperators(p, result, -1, pmNormal) - else: - result = parseSection(p, nkTypeSection, parseTypeDef) - of tkConst: result = parseSection(p, nkConstSection, parseConstant) - of tkLet: result = parseSection(p, nkLetSection, parseVariable) - of tkWhen: result = parseIfOrWhen(p, nkWhenStmt) - of tkVar: result = parseSection(p, nkVarSection, parseVariable) - of tkBind: result = parseBind(p, nkBindStmt) - of tkMixin: result = parseBind(p, nkMixinStmt) - of tkUsing: result = parseSection(p, nkUsingStmt, parseVariable) - else: result = simpleStmt(p) - -proc parseStmt(p: var TParser): PNode = - result = complexOrSimpleStmt(p) - -proc parseAll*(p: var TParser): PNode = - ## Parses the rest of the input stream held by the parser into a PNode. - result = newNodeP(nkStmtList, p) - while p.tok.tokType != tkEof: - var a = complexOrSimpleStmt(p) - if a.kind != nkEmpty: - addSon(result, a) - else: - parMessage(p, errExprExpected, p.tok) - # bugfix: consume a token here to prevent an endless loop: - getTok(p) - -proc parseTopLevelStmt*(p: var TParser): PNode = - ## Implements an iterator which, when called repeatedly, returns the next - ## top-level statement or emptyNode if end of stream. - result = ast.emptyNode - while true: - case p.tok.tokType - of tkSemiColon: getTok(p) - of tkEof: break - else: - result = complexOrSimpleStmt(p) - if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok) - break diff --git a/compiler/platform.nim b/compiler/platform.nim index 01ddba23e..8b3bf6b74 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -176,7 +176,7 @@ type cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64, cpuPowerpc64el, cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuMipsel, cpuArm, cpuArm64, cpuJS, cpuNimrodVM, cpuAVR, cpuMSP430, cpuSparc64, - cpuMips64, cpuMips64el + cpuMips64, cpuMips64el, cpuRiscV64 type TEndian* = enum @@ -207,7 +207,8 @@ const (name: "msp430", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16), (name: "sparc64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64), (name: "mips64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64), - (name: "mips64el", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64)] + (name: "mips64el", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), + (name: "riscv64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64)] var targetCPU*, hostCPU*: TSystemCPU diff --git a/compiler/plugins/active.nim b/compiler/plugins/active.nim index 7b6411178..5da623e49 100644 --- a/compiler/plugins/active.nim +++ b/compiler/plugins/active.nim @@ -10,4 +10,4 @@ ## Include file that imports all plugins that are active. import - locals.locals, itersgen + locals / locals, itersgen diff --git a/compiler/plugins/itersgen.nim b/compiler/plugins/itersgen.nim index f44735b77..ebb65dd4a 100644 --- a/compiler/plugins/itersgen.nim +++ b/compiler/plugins/itersgen.nim @@ -9,30 +9,29 @@ ## Plugin to transform an inline iterator into a data structure. -import compiler/pluginsupport, compiler/ast, compiler/astalgo, - compiler/magicsys, compiler/lookups, compiler/semdata, - compiler/lambdalifting, compiler/rodread, compiler/msgs - +import ".." / [pluginsupport, ast, astalgo, + magicsys, lookups, semdata, + lambdalifting, rodread, msgs] proc iterToProcImpl(c: PContext, n: PNode): PNode = result = newNodeI(nkStmtList, n.info) let iter = n[1] if iter.kind != nkSym or iter.sym.kind != skIterator: - localError(iter.info, "first argument needs to be an iterator") + localError(c.config, iter.info, "first argument needs to be an iterator") return if n[2].typ.isNil: - localError(n[2].info, "second argument needs to be a type") + localError(c.config, n[2].info, "second argument needs to be a type") return if n[3].kind != nkIdent: - localError(n[3].info, "third argument needs to be an identifier") + localError(c.config, n[3].info, "third argument needs to be an identifier") return let t = n[2].typ.skipTypes({tyTypeDesc, tyGenericInst}) if t.kind notin {tyRef, tyPtr} or t.lastSon.kind != tyObject: - localError(n[2].info, + localError(c.config, n[2].info, "type must be a non-generic ref|ptr to object with state field") return - let body = liftIterToProc(iter.sym, iter.sym.getBody, t) + let body = liftIterToProc(c.graph, iter.sym, iter.sym.getBody, t) let prc = newSym(skProc, n[3].ident, iter.sym.owner, iter.sym.info) prc.typ = copyType(iter.sym.typ, prc, false) diff --git a/compiler/plugins/locals/locals.nim b/compiler/plugins/locals/locals.nim index 338e7bcac..ff7f3be58 100644 --- a/compiler/plugins/locals/locals.nim +++ b/compiler/plugins/locals/locals.nim @@ -9,8 +9,8 @@ ## The builtin 'system.locals' implemented as a plugin. -import compiler/pluginsupport, compiler/ast, compiler/astalgo, - compiler/magicsys, compiler/lookups, compiler/semdata, compiler/lowerings +import "../../" / [pluginsupport, ast, astalgo, + magicsys, lookups, semdata, lowerings] proc semLocals(c: PContext, n: PNode): PNode = var counter = 0 diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index bdaecf91d..de98a5e42 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -12,7 +12,7 @@ import os, platform, condsyms, ast, astalgo, idents, semdata, msgs, renderer, wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees, - rodread, types, lookups + rodread, types, lookups, configuration const FirstCallConv* = wNimcall @@ -29,7 +29,7 @@ const converterPragmas* = procPragmas methodPragmas* = procPragmas+{wBase}-{wImportCpp} templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty, - wDelegator, wExportNims, wUsed} + wDelegator, wExportNims, wUsed, wPragma} macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc, wNodecl, wMagic, wNosideeffect, wCompilerProc, wCore, wDeprecated, wExtern, wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator, @@ -40,10 +40,13 @@ const wTags, wLocks, wGcSafe, wExportNims, wUsed} exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe} stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, - wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, + wBoundchecks, wOverflowchecks, wNilchecks, wMovechecks, wAssertions, + wWarnings, wHints, wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError, wFatal, wDefine, wUndef, wCompile, wLink, wLinksys, wPure, wPush, wPop, - wBreakpoint, wWatchPoint, wPassl, wPassc, wDeadCodeElim, wDeprecated, + wBreakpoint, wWatchPoint, wPassl, wPassc, + wDeadCodeElimUnused, # deprecated, always on + wDeprecated, wFloatchecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll, wLinearScanEnd, wPatterns, wEffects, wNoForward, wReorder, wComputedGoto, wInjectStmt, wDeprecated, wExperimental, wThis} @@ -64,7 +67,7 @@ const wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims, wUsed} constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl, wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims, - wIntDefine, wStrDefine, wUsed} + wIntDefine, wStrDefine, wUsed, wCompilerProc, wCore} letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect, wThread, wRaises, wLocks, wTags, wGcSafe} @@ -74,31 +77,35 @@ proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode = let p = procAst[pragmasPos] if p.kind == nkEmpty: return nil for it in p: - if it.kind == nkExprColonExpr and it[0].kind == nkIdent and + if it.kind in nkPragmaCallKinds and it.len == 2 and it[0].kind == nkIdent and it[0].ident.id == ord(name): return it[1] proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) # implementation -proc invalidPragma*(n: PNode) = - localError(n.info, errInvalidPragmaX, renderTree(n, {renderNoComments})) +const + errStringLiteralExpected = "string literal expected" + errIntLiteralExpected = "integer literal expected" + +proc invalidPragma*(c: PContext; n: PNode) = + localError(c.config, n.info, "invalid pragma: " % renderTree(n, {renderNoComments})) proc pragmaAsm*(c: PContext, n: PNode): char = result = '\0' if n != nil: for i in countup(0, sonsLen(n) - 1): let it = n.sons[i] - if it.kind == nkExprColonExpr and it.sons[0].kind == nkIdent: + if it.kind in nkPragmaCallKinds and it.len == 2 and it.sons[0].kind == nkIdent: case whichKeyword(it.sons[0].ident) of wSubsChar: if it.sons[1].kind == nkCharLit: result = chr(int(it.sons[1].intVal)) - else: invalidPragma(it) - else: invalidPragma(it) + else: invalidPragma(c, it) + else: invalidPragma(c, it) else: - invalidPragma(it) + invalidPragma(c, it) -proc setExternName(s: PSym, extname: string, info: TLineInfo) = +proc setExternName(c: PContext; s: PSym, extname: string, info: TLineInfo) = # special cases to improve performance: if extname == "$1": s.loc.r = rope(s.name.s) @@ -108,76 +115,76 @@ proc setExternName(s: PSym, extname: string, info: TLineInfo) = try: s.loc.r = rope(extname % s.name.s) except ValueError: - localError(info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)") - if gCmd == cmdPretty and '$' notin extname: + localError(c.config, info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)") + if c.config.cmd == cmdPretty and '$' notin extname: # note that '{.importc.}' is transformed into '{.importc: "$1".}' s.loc.flags.incl(lfFullExternalName) -proc makeExternImport(s: PSym, extname: string, info: TLineInfo) = - setExternName(s, extname, info) +proc makeExternImport(c: PContext; s: PSym, extname: string, info: TLineInfo) = + setExternName(c, s, extname, info) incl(s.flags, sfImportc) excl(s.flags, sfForward) -proc makeExternExport(s: PSym, extname: string, info: TLineInfo) = - setExternName(s, extname, info) +proc makeExternExport(c: PContext; s: PSym, extname: string, info: TLineInfo) = + setExternName(c, s, extname, info) incl(s.flags, sfExportc) -proc processImportCompilerProc(s: PSym, extname: string, info: TLineInfo) = - setExternName(s, extname, info) +proc processImportCompilerProc(c: PContext; s: PSym, extname: string, info: TLineInfo) = + setExternName(c, s, extname, info) incl(s.flags, sfImportc) excl(s.flags, sfForward) incl(s.loc.flags, lfImportCompilerProc) -proc processImportCpp(s: PSym, extname: string, info: TLineInfo) = - setExternName(s, extname, info) +proc processImportCpp(c: PContext; s: PSym, extname: string, info: TLineInfo) = + setExternName(c, s, extname, info) incl(s.flags, sfImportc) incl(s.flags, sfInfixCall) excl(s.flags, sfForward) - if gCmd == cmdCompileToC: + if c.config.cmd == cmdCompileToC: let m = s.getModule() incl(m.flags, sfCompileToCpp) extccomp.gMixedMode = true -proc processImportObjC(s: PSym, extname: string, info: TLineInfo) = - setExternName(s, extname, info) +proc processImportObjC(c: PContext; s: PSym, extname: string, info: TLineInfo) = + setExternName(c, s, extname, info) incl(s.flags, sfImportc) incl(s.flags, sfNamedParamCall) excl(s.flags, sfForward) let m = s.getModule() incl(m.flags, sfCompileToObjC) -proc newEmptyStrNode(n: PNode): PNode {.noinline.} = - result = newNodeIT(nkStrLit, n.info, getSysType(tyString)) +proc newEmptyStrNode(c: PContext; n: PNode): PNode {.noinline.} = + result = newNodeIT(nkStrLit, n.info, getSysType(c.graph, n.info, tyString)) result.strVal = "" proc getStrLitNode(c: PContext, n: PNode): PNode = - if n.kind != nkExprColonExpr: - localError(n.info, errStringLiteralExpected) + if n.kind notin nkPragmaCallKinds or n.len != 2: + localError(c.config, n.info, errStringLiteralExpected) # error correction: - result = newEmptyStrNode(n) + result = newEmptyStrNode(c, n) else: n.sons[1] = c.semConstExpr(c, n.sons[1]) case n.sons[1].kind of nkStrLit, nkRStrLit, nkTripleStrLit: result = n.sons[1] else: - localError(n.info, errStringLiteralExpected) + localError(c.config, n.info, errStringLiteralExpected) # error correction: - result = newEmptyStrNode(n) + result = newEmptyStrNode(c, n) proc expectStrLit(c: PContext, n: PNode): string = result = getStrLitNode(c, n).strVal proc expectIntLit(c: PContext, n: PNode): int = - if n.kind != nkExprColonExpr: - localError(n.info, errIntLiteralExpected) + if n.kind notin nkPragmaCallKinds or n.len != 2: + localError(c.config, n.info, errIntLiteralExpected) else: n.sons[1] = c.semConstExpr(c, n.sons[1]) case n.sons[1].kind of nkIntLit..nkInt64Lit: result = int(n.sons[1].intVal) - else: localError(n.info, errIntLiteralExpected) + else: localError(c.config, n.info, errIntLiteralExpected) proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string = - if n.kind == nkExprColonExpr: result = expectStrLit(c, n) + if n.kind in nkPragmaCallKinds: result = expectStrLit(c, n) else: result = defaultStr proc processCodegenDecl(c: PContext, n: PNode, sym: PSym) = @@ -186,8 +193,8 @@ proc processCodegenDecl(c: PContext, n: PNode, sym: PSym) = proc processMagic(c: PContext, n: PNode, s: PSym) = #if sfSystemModule notin c.module.flags: # liMessage(n.info, errMagicOnlyInSystem) - if n.kind != nkExprColonExpr: - localError(n.info, errStringLiteralExpected) + if n.kind notin nkPragmaCallKinds or n.len != 2: + localError(c.config, n.info, errStringLiteralExpected) return var v: string if n.sons[1].kind == nkIdent: v = n.sons[1].ident.s @@ -196,7 +203,7 @@ proc processMagic(c: PContext, n: PNode, s: PSym) = if substr($m, 1) == v: s.magic = m break - if s.magic == mNone: message(n.info, warnUnknownMagic, v) + if s.magic == mNone: message(c.config, n.info, warnUnknownMagic, v) proc wordToCallConv(sw: TSpecialWord): TCallingConvention = # this assumes that the order of special words and calling conventions is @@ -204,33 +211,29 @@ proc wordToCallConv(sw: TSpecialWord): TCallingConvention = result = TCallingConvention(ord(ccDefault) + ord(sw) - ord(wNimcall)) proc isTurnedOn(c: PContext, n: PNode): bool = - if n.kind == nkExprColonExpr: + if n.kind in nkPragmaCallKinds and n.len == 2: let x = c.semConstBoolExpr(c, n.sons[1]) n.sons[1] = x if x.kind == nkIntLit: return x.intVal != 0 - localError(n.info, errOnOrOffExpected) + localError(c.config, n.info, "'on' or 'off' expected") proc onOff(c: PContext, n: PNode, op: TOptions) = - if isTurnedOn(c, n): gOptions = gOptions + op - else: gOptions = gOptions - op - -proc pragmaDeadCodeElim(c: PContext, n: PNode) = - if isTurnedOn(c, n): incl(c.module.flags, sfDeadCodeElim) - else: excl(c.module.flags, sfDeadCodeElim) + if isTurnedOn(c, n): c.config.options = c.config.options + op + else: c.config.options = c.config.options - op proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) = if isTurnedOn(c, n): incl(c.module.flags, flag) else: excl(c.module.flags, flag) proc processCallConv(c: PContext, n: PNode) = - if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent): + if n.kind in nkPragmaCallKinds and n.len == 2 and n.sons[1].kind == nkIdent: var sw = whichKeyword(n.sons[1].ident) case sw of FirstCallConv..LastCallConv: c.optionStack[^1].defaultCC = wordToCallConv(sw) - else: localError(n.info, errCallConvExpected) + else: localError(c.config, n.info, "calling convention expected") else: - localError(n.info, errCallConvExpected) + localError(c.config, n.info, "calling convention expected") proc getLib(c: PContext, kind: TLibKind, path: PNode): PLib = for it in c.libs: @@ -241,13 +244,13 @@ proc getLib(c: PContext, kind: TLibKind, path: PNode): PLib = result.path = path c.libs.add result if path.kind in {nkStrLit..nkTripleStrLit}: - result.isOverriden = options.isDynlibOverride(path.strVal) + result.isOverriden = options.isDynlibOverride(c.config, path.strVal) proc expectDynlibNode(c: PContext, n: PNode): PNode = - if n.kind != nkExprColonExpr: - localError(n.info, errStringLiteralExpected) + if n.kind notin nkPragmaCallKinds or n.len != 2: + localError(c.config, n.info, errStringLiteralExpected) # error correction: - result = newEmptyStrNode(n) + result = newEmptyStrNode(c, n) else: # For the OpenGL wrapper we support: # {.dynlib: myGetProcAddr(...).} @@ -255,8 +258,8 @@ proc expectDynlibNode(c: PContext, n: PNode): PNode = if result.kind == nkSym and result.sym.kind == skConst: result = result.sym.ast # look it up if result.typ == nil or result.typ.kind notin {tyPointer, tyString, tyProc}: - localError(n.info, errStringLiteralExpected) - result = newEmptyStrNode(n) + localError(c.config, n.info, errStringLiteralExpected) + result = newEmptyStrNode(c, n) proc processDynLib(c: PContext, n: PNode, sym: PSym) = if (sym == nil) or (sym.kind == skModule): @@ -264,7 +267,7 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) = if not lib.isOverriden: c.optionStack[^1].dynlib = lib else: - if n.kind == nkExprColonExpr: + if n.kind in nkPragmaCallKinds: var lib = getLib(c, libDynamic, expectDynlibNode(c, n)) if not lib.isOverriden: addToLib(lib, sym) @@ -279,39 +282,37 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) = sym.typ.callConv = ccCDecl proc processNote(c: PContext, n: PNode) = - if (n.kind == nkExprColonExpr) and (sonsLen(n) == 2) and - (n.sons[0].kind == nkBracketExpr) and - (n.sons[0].sons.len == 2) and - (n.sons[0].sons[1].kind == nkIdent) and - (n.sons[0].sons[0].kind == nkIdent): - #and (n.sons[1].kind == nkIdent): + if n.kind in nkPragmaCallKinds and len(n) == 2 and + n[0].kind == nkBracketExpr and + n[0].len == 2 and + n[0][1].kind == nkIdent and n[0][0].kind == nkIdent: var nk: TNoteKind - case whichKeyword(n.sons[0].sons[0].ident) + case whichKeyword(n[0][0].ident) of wHint: - var x = findStr(msgs.HintsToStr, n.sons[0].sons[1].ident.s) + var x = findStr(HintsToStr, n[0][1].ident.s) if x >= 0: nk = TNoteKind(x + ord(hintMin)) - else: invalidPragma(n); return + else: invalidPragma(c, n); return of wWarning: - var x = findStr(msgs.WarningsToStr, n.sons[0].sons[1].ident.s) + var x = findStr(WarningsToStr, n[0][1].ident.s) if x >= 0: nk = TNoteKind(x + ord(warnMin)) - else: invalidPragma(n); return + else: invalidPragma(c, n); return else: - invalidPragma(n) + invalidPragma(c, n) return - let x = c.semConstBoolExpr(c, n.sons[1]) + let x = c.semConstBoolExpr(c, n[1]) n.sons[1] = x - if x.kind == nkIntLit and x.intVal != 0: incl(gNotes, nk) - else: excl(gNotes, nk) + if x.kind == nkIntLit and x.intVal != 0: incl(c.config.notes, nk) + else: excl(c.config.notes, nk) else: - invalidPragma(n) + invalidPragma(c, n) proc processOption(c: PContext, n: PNode): bool = - if n.kind != nkExprColonExpr: result = true + if n.kind notin nkPragmaCallKinds or n.len != 2: result = true elif n.sons[0].kind == nkBracketExpr: processNote(c, n) elif n.sons[0].kind != nkIdent: result = true else: - var sw = whichKeyword(n.sons[0].ident) + let sw = whichKeyword(n.sons[0].ident) case sw of wChecks: onOff(c, n, ChecksOptions) of wObjChecks: onOff(c, n, {optObjCheck}) @@ -323,6 +324,7 @@ proc processOption(c: PContext, n: PNode): bool = of wFloatchecks: onOff(c, n, {optNaNCheck, optInfCheck}) of wNanChecks: onOff(c, n, {optNaNCheck}) of wInfChecks: onOff(c, n, {optInfCheck}) + of wMovechecks: onOff(c, n, {optMoveCheck}) of wAssertions: onOff(c, n, {optAssert}) of wWarnings: onOff(c, n, {optWarns}) of wHints: onOff(c, n, {optHints}) @@ -337,32 +339,32 @@ proc processOption(c: PContext, n: PNode): bool = of wDynlib: processDynLib(c, n, nil) of wOptimization: if n.sons[1].kind != nkIdent: - invalidPragma(n) + invalidPragma(c, n) else: case n.sons[1].ident.s.normalize of "speed": - incl(gOptions, optOptimizeSpeed) - excl(gOptions, optOptimizeSize) + incl(c.config.options, optOptimizeSpeed) + excl(c.config.options, optOptimizeSize) of "size": - excl(gOptions, optOptimizeSpeed) - incl(gOptions, optOptimizeSize) + excl(c.config.options, optOptimizeSpeed) + incl(c.config.options, optOptimizeSize) of "none": - excl(gOptions, optOptimizeSpeed) - excl(gOptions, optOptimizeSize) - else: localError(n.info, errNoneSpeedOrSizeExpected) + excl(c.config.options, optOptimizeSpeed) + excl(c.config.options, optOptimizeSize) + else: localError(c.config, n.info, "'none', 'speed' or 'size' expected") of wImplicitStatic: onOff(c, n, {optImplicitStatic}) of wPatterns: onOff(c, n, {optPatterns}) else: result = true proc processPush(c: PContext, n: PNode, start: int) = - if n.sons[start-1].kind == nkExprColonExpr: - localError(n.info, errGenerated, "':' after 'push' not supported") - var x = newOptionEntry() + if n.sons[start-1].kind in nkPragmaCallKinds: + localError(c.config, n.info, "'push' cannot have arguments") + var x = newOptionEntry(c.config) var y = c.optionStack[^1] - x.options = gOptions + x.options = c.config.options x.defaultCC = y.defaultCC x.dynlib = y.dynlib - x.notes = gNotes + x.notes = c.config.notes c.optionStack.add(x) for i in countup(start, sonsLen(n) - 1): if processOption(c, n.sons[i]): @@ -370,29 +372,29 @@ proc processPush(c: PContext, n: PNode, start: int) = if x.otherPragmas.isNil: x.otherPragmas = newNodeI(nkPragma, n.info) x.otherPragmas.add n.sons[i] - #localError(n.info, errOptionExpected) + #localError(c.config, n.info, errOptionExpected) proc processPop(c: PContext, n: PNode) = if c.optionStack.len <= 1: - localError(n.info, errAtPopWithoutPush) + localError(c.config, n.info, "{.pop.} without a corresponding {.push.}") else: - gOptions = c.optionStack[^1].options - gNotes = c.optionStack[^1].notes + c.config.options = c.optionStack[^1].options + c.config.notes = c.optionStack[^1].notes c.optionStack.setLen(c.optionStack.len - 1) proc processDefine(c: PContext, n: PNode) = - if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent): - defineSymbol(n.sons[1].ident.s) - message(n.info, warnDeprecated, "define") + if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent): + defineSymbol(c.config.symbols, n[1].ident.s) + message(c.config, n.info, warnDeprecated, "define") else: - invalidPragma(n) + invalidPragma(c, n) proc processUndef(c: PContext, n: PNode) = - if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent): - undefSymbol(n.sons[1].ident.s) - message(n.info, warnDeprecated, "undef") + if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent): + undefSymbol(c.config.symbols, n[1].ident.s) + message(c.config, n.info, warnDeprecated, "undef") else: - invalidPragma(n) + invalidPragma(c, n) type TLinkFeature = enum @@ -406,57 +408,58 @@ proc relativeFile(c: PContext; n: PNode; ext=""): string = if not fileExists(result): if isAbsolute(s): result = s else: - result = findFile(s) + result = findFile(c.config, s) if result.len == 0: result = s proc processCompile(c: PContext, n: PNode) = proc getStrLit(c: PContext, n: PNode; i: int): string = - n.sons[i] = c.semConstExpr(c, n.sons[i]) - case n.sons[i].kind + n.sons[i] = c.semConstExpr(c, n[i]) + case n[i].kind of nkStrLit, nkRStrLit, nkTripleStrLit: - shallowCopy(result, n.sons[i].strVal) + shallowCopy(result, n[i].strVal) else: - localError(n.info, errStringLiteralExpected) + localError(c.config, n.info, errStringLiteralExpected) result = "" - let it = if n.kind == nkExprColonExpr: n.sons[1] else: n - if it.kind == nkPar and it.len == 2: + let it = if n.kind in nkPragmaCallKinds and n.len == 2: n.sons[1] else: n + if it.kind in {nkPar, nkTupleConstr} and it.len == 2: let s = getStrLit(c, it, 0) let dest = getStrLit(c, it, 1) var found = parentDir(n.info.toFullPath) / s for f in os.walkFiles(found): let nameOnly = extractFilename(f) var cf = Cfile(cname: f, - obj: completeCFilePath(dest % nameOnly), + obj: completeCFilePath(c.config, dest % nameOnly), flags: {CfileFlag.External}) - extccomp.addExternalFileToCompile(cf) + extccomp.addExternalFileToCompile(c.config, cf) else: let s = expectStrLit(c, n) var found = parentDir(n.info.toFullPath) / s if not fileExists(found): if isAbsolute(s): found = s else: - found = findFile(s) + found = findFile(c.config, s) if found.len == 0: found = s - extccomp.addExternalFileToCompile(found) + extccomp.addExternalFileToCompile(c.config, found) proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) = let found = relativeFile(c, n, CC[cCompiler].objExt) case feature - of linkNormal: extccomp.addExternalFileToLink(found) + of linkNormal: extccomp.addExternalFileToLink(c.config, found) of linkSys: - extccomp.addExternalFileToLink(libpath / completeCFilePath(found, false)) - else: internalError(n.info, "processCommonLink") + extccomp.addExternalFileToLink(c.config, + c.config.libpath / completeCFilePath(c.config, found, false)) + else: internalError(c.config, n.info, "processCommonLink") proc pragmaBreakpoint(c: PContext, n: PNode) = discard getOptionalStr(c, n, "") proc pragmaWatchpoint(c: PContext, n: PNode) = - if n.kind == nkExprColonExpr: + if n.kind in nkPragmaCallKinds and n.len == 2: n.sons[1] = c.semExpr(c, n.sons[1]) else: - invalidPragma(n) + invalidPragma(c, n) proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode = case n.sons[1].kind @@ -464,7 +467,7 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode = result = newNode(if n.kind == nkAsmStmt: nkAsmStmt else: nkArgList, n.info) var str = n.sons[1].strVal if str == "": - localError(n.info, errEmptyAsm) + localError(con.config, n.info, "empty 'asm' statement") return # now parse the string literal and substitute symbols: var a = 0 @@ -490,12 +493,12 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode = if c < 0: break a = c + 1 else: - illFormedAstLocal(n) + illFormedAstLocal(n, con.config) result = newNode(nkAsmStmt, n.info) proc pragmaEmit(c: PContext, n: PNode) = - if n.kind != nkExprColonExpr: - localError(n.info, errStringLiteralExpected) + if n.kind notin nkPragmaCallKinds or n.len != 2: + localError(c.config, n.info, errStringLiteralExpected) else: let n1 = n[1] if n1.kind == nkBracket: @@ -509,137 +512,140 @@ proc pragmaEmit(c: PContext, n: PNode) = of nkStrLit, nkRStrLit, nkTripleStrLit: n.sons[1] = semAsmOrEmit(c, n, '`') else: - localError(n.info, errStringLiteralExpected) + localError(c.config, n.info, errStringLiteralExpected) -proc noVal(n: PNode) = - if n.kind == nkExprColonExpr: invalidPragma(n) +proc noVal(c: PContext; n: PNode) = + if n.kind in nkPragmaCallKinds and n.len > 1: invalidPragma(c, n) proc pragmaUnroll(c: PContext, n: PNode) = if c.p.nestedLoopCounter <= 0: - invalidPragma(n) - elif n.kind == nkExprColonExpr: + invalidPragma(c, n) + elif n.kind in nkPragmaCallKinds and n.len == 2: var unrollFactor = expectIntLit(c, n) if unrollFactor <% 32: n.sons[1] = newIntNode(nkIntLit, unrollFactor) else: - invalidPragma(n) + invalidPragma(c, n) proc pragmaLine(c: PContext, n: PNode) = - if n.kind == nkExprColonExpr: + if n.kind in nkPragmaCallKinds and n.len == 2: n.sons[1] = c.semConstExpr(c, n.sons[1]) let a = n.sons[1] - if a.kind == nkPar: + if a.kind in {nkPar, nkTupleConstr}: + # unpack the tuple var x = a.sons[0] var y = a.sons[1] if x.kind == nkExprColonExpr: x = x.sons[1] if y.kind == nkExprColonExpr: y = y.sons[1] if x.kind != nkStrLit: - localError(n.info, errStringLiteralExpected) + localError(c.config, n.info, errStringLiteralExpected) elif y.kind != nkIntLit: - localError(n.info, errIntLiteralExpected) + localError(c.config, n.info, errIntLiteralExpected) else: # XXX this produces weird paths which are not properly resolved: - n.info.fileIndex = msgs.fileInfoIdx(x.strVal) - n.info.line = int16(y.intVal) + n.info.fileIndex = msgs.fileInfoIdx(c.config, x.strVal) + n.info.line = uint16(y.intVal) else: - localError(n.info, errXExpected, "tuple") + localError(c.config, n.info, "tuple expected") else: # sensible default: n.info = getInfoContext(-1) proc processPragma(c: PContext, n: PNode, i: int) = - var it = n.sons[i] - if it.kind != nkExprColonExpr: invalidPragma(n) - elif it.sons[0].kind != nkIdent: invalidPragma(n) - elif it.sons[1].kind != nkIdent: invalidPragma(n) - - var userPragma = newSym(skTemplate, it.sons[1].ident, nil, it.info) - var body = newNodeI(nkPragma, n.info) - for j in i+1 .. sonsLen(n)-1: addSon(body, n.sons[j]) - userPragma.ast = body + let it = n[i] + if it.kind notin nkPragmaCallKinds and it.len == 2: invalidPragma(c, n) + elif it[0].kind != nkIdent: invalidPragma(c, n) + elif it[1].kind != nkIdent: invalidPragma(c, n) + + var userPragma = newSym(skTemplate, it[1].ident, nil, it.info, c.config.options) + userPragma.ast = newNode(nkPragma, n.info, n.sons[i+1..^1]) strTableAdd(c.userPragmas, userPragma) proc pragmaRaisesOrTags(c: PContext, n: PNode) = proc processExc(c: PContext, x: PNode) = var t = skipTypes(c.semTypeNode(c, x, nil), skipPtrs) if t.kind != tyObject: - localError(x.info, errGenerated, "invalid type for raises/tags list") + localError(c.config, x.info, errGenerated, "invalid type for raises/tags list") x.typ = t - if n.kind == nkExprColonExpr: + if n.kind in nkPragmaCallKinds and n.len == 2: let it = n.sons[1] if it.kind notin {nkCurly, nkBracket}: processExc(c, it) else: for e in items(it): processExc(c, e) else: - invalidPragma(n) + invalidPragma(c, n) proc pragmaLockStmt(c: PContext; it: PNode) = - if it.kind != nkExprColonExpr: - invalidPragma(it) + if it.kind notin nkPragmaCallKinds or it.len != 2: + invalidPragma(c, it) else: let n = it[1] if n.kind != nkBracket: - localError(n.info, errGenerated, "locks pragma takes a list of expressions") + localError(c.config, n.info, errGenerated, "locks pragma takes a list of expressions") else: for i in 0 ..< n.len: n.sons[i] = c.semExpr(c, n.sons[i]) proc pragmaLocks(c: PContext, it: PNode): TLockLevel = - if it.kind != nkExprColonExpr: - invalidPragma(it) + if it.kind notin nkPragmaCallKinds or it.len != 2: + invalidPragma(c, it) else: case it[1].kind of nkStrLit, nkRStrLit, nkTripleStrLit: if it[1].strVal == "unknown": result = UnknownLockLevel else: - localError(it[1].info, "invalid string literal for locks pragma (only allowed string is \"unknown\")") + localError(c.config, it[1].info, "invalid string literal for locks pragma (only allowed string is \"unknown\")") else: let x = expectIntLit(c, it) if x < 0 or x > MaxLockLevel: - localError(it[1].info, "integer must be within 0.." & $MaxLockLevel) + localError(c.config, it[1].info, "integer must be within 0.." & $MaxLockLevel) else: result = TLockLevel(x) -proc typeBorrow(sym: PSym, n: PNode) = - if n.kind == nkExprColonExpr: +proc typeBorrow(c: PContext; sym: PSym, n: PNode) = + if n.kind in nkPragmaCallKinds and n.len == 2: let it = n.sons[1] if it.kind != nkAccQuoted: - localError(n.info, "a type can only borrow `.` for now") + localError(c.config, n.info, "a type can only borrow `.` for now") incl(sym.typ.flags, tfBorrowDot) -proc markCompilerProc(s: PSym) = +proc markCompilerProc(c: PContext; s: PSym) = # minor hack ahead: FlowVar is the only generic .compilerProc type which # should not have an external name set: if s.kind != skType or s.name.s != "FlowVar": - makeExternExport(s, "$1", s.info) + makeExternExport(c, s, "$1", s.info) incl(s.flags, sfCompilerProc) incl(s.flags, sfUsed) - registerCompilerProc(s) + registerCompilerProc(c.graph, s) -proc deprecatedStmt(c: PContext; pragma: PNode) = - let pragma = pragma[1] +proc deprecatedStmt(c: PContext; outerPragma: PNode) = + let pragma = outerPragma[1] + if pragma.kind in {nkStrLit..nkTripleStrLit}: + incl(c.module.flags, sfDeprecated) + c.module.constraint = getStrLitNode(c, outerPragma) + return if pragma.kind != nkBracket: - localError(pragma.info, "list of key:value pairs expected"); return + localError(c.config, pragma.info, "list of key:value pairs expected"); return for n in pragma: - if n.kind in {nkExprColonExpr, nkExprEqExpr}: + if n.kind in nkPragmaCallKinds and n.len == 2: let dest = qualifiedLookUp(c, n[1], {checkUndeclared}) if dest == nil or dest.kind in routineKinds: - localError(n.info, warnUser, "the .deprecated pragma is unreliable for routines") - let src = considerQuotedIdent(n[0]) - let alias = newSym(skAlias, src, dest, n[0].info) + localError(c.config, n.info, warnUser, "the .deprecated pragma is unreliable for routines") + let src = considerQuotedIdent(c.config, n[0]) + let alias = newSym(skAlias, src, dest, n[0].info, c.config.options) incl(alias.flags, sfExported) - if sfCompilerProc in dest.flags: markCompilerProc(alias) + if sfCompilerProc in dest.flags: markCompilerProc(c, alias) addInterfaceDecl(c, alias) n.sons[1] = newSymNode(dest) else: - localError(n.info, "key:value pair expected") + localError(c.config, n.info, "key:value pair expected") proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym = - if it.kind != nkExprColonExpr: - invalidPragma(it); return + if it.kind notin nkPragmaCallKinds or it.len != 2: + invalidPragma(c, it); return let n = it[1] if n.kind == nkSym: result = n.sym @@ -651,105 +657,148 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym = # We return a dummy symbol; later passes over the type will repair it. # Generic instantiation needs to know about this too. But we're lazy # and perform the lookup on demand instead. - result = newSym(skUnknown, considerQuotedIdent(n), nil, n.info) + result = newSym(skUnknown, considerQuotedIdent(c.config, n), nil, n.info, + c.config.options) else: result = qualifiedLookUp(c, n, {checkUndeclared}) -proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, +proc semCustomPragma(c: PContext, n: PNode): PNode = + if n.kind == nkIdent: + result = newTree(nkCall, n) + elif n.kind == nkExprColonExpr: + # pragma: arg -> pragma(arg) + result = newTree(nkCall, n[0], n[1]) + elif n.kind in nkPragmaCallKinds + {nkIdent}: + result = n + else: + invalidPragma(c, n) + return n + + let r = c.semOverloadedCall(c, result, n, {skTemplate}, {}) + if r.isNil or sfCustomPragma notin r[0].sym.flags: + invalidPragma(c, n) + else: + result = r + if n.kind == nkIdent: + result = result[0] + elif n.kind == nkExprColonExpr: + result.kind = n.kind # pragma(arg) -> pragma: arg + +proc processExperimental(c: PContext; n: PNode; s: PSym) = + if not isTopLevel(c): + localError(c.config, n.info, "'experimental' pragma only valid as toplevel statement") + if n.kind notin nkPragmaCallKinds or n.len != 2: + c.features.incl oldExperimentalFeatures + else: + n[1] = c.semConstExpr(c, n[1]) + case n[1].kind + of nkStrLit, nkRStrLit, nkTripleStrLit: + try: + c.features.incl parseEnum[Feature](n[1].strVal) + except ValueError: + localError(c.config, n[1].info, "unknown experimental feature") + else: + localError(c.config, n.info, errStringLiteralExpected) + +proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, validPragmas: TSpecialWords): bool = var it = n.sons[i] - var key = if it.kind == nkExprColonExpr: it.sons[0] else: it + var key = if it.kind in nkPragmaCallKinds and it.len > 1: it.sons[0] else: it if key.kind == nkBracketExpr: processNote(c, it) return - let ident = considerQuotedIdent(key) + elif key.kind notin nkIdentKinds: + n.sons[i] = semCustomPragma(c, it) + return + let ident = considerQuotedIdent(c.config, key) var userPragma = strTableGet(c.userPragmas, ident) if userPragma != nil: + # number of pragmas increase/decrease with user pragma expansion inc c.instCounter if c.instCounter > 100: - globalError(it.info, errRecursiveDependencyX, userPragma.name.s) + globalError(c.config, it.info, "recursive dependency: " & userPragma.name.s) + pragma(c, sym, userPragma.ast, validPragmas) - # ensure the pragma is also remember for generic instantiations in other - # modules: - n.sons[i] = userPragma.ast + n.sons[i..i] = userPragma.ast.sons # expand user pragma with its content + i.inc(userPragma.ast.len - 1) # inc by -1 is ok, user pragmas was empty dec c.instCounter else: var k = whichKeyword(ident) if k in validPragmas: case k of wExportc: - makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info) + makeExternExport(c, sym, getOptionalStr(c, it, "$1"), it.info) incl(sym.flags, sfUsed) # avoid wrong hints of wImportc: let name = getOptionalStr(c, it, "$1") - cppDefine(c.graph.config, name) - makeExternImport(sym, name, it.info) + cppDefine(c.config, name) + makeExternImport(c, sym, name, it.info) of wImportCompilerProc: let name = getOptionalStr(c, it, "$1") - cppDefine(c.graph.config, name) - processImportCompilerProc(sym, name, it.info) - of wExtern: setExternName(sym, expectStrLit(c, it), it.info) + cppDefine(c.config, name) + processImportCompilerProc(c, sym, name, it.info) + of wExtern: setExternName(c, sym, expectStrLit(c, it), it.info) of wImmediate: if sym.kind in {skTemplate, skMacro}: incl(sym.flags, sfImmediate) incl(sym.flags, sfAllUntyped) - message(n.info, warnDeprecated, "use 'untyped' parameters instead; immediate") - else: invalidPragma(it) + message(c.config, n.info, warnDeprecated, "use 'untyped' parameters instead; immediate") + else: invalidPragma(c, it) of wDirty: if sym.kind == skTemplate: incl(sym.flags, sfDirty) - else: invalidPragma(it) + else: invalidPragma(c, it) of wImportCpp: - processImportCpp(sym, getOptionalStr(c, it, "$1"), it.info) + processImportCpp(c, sym, getOptionalStr(c, it, "$1"), it.info) of wImportObjC: - processImportObjC(sym, getOptionalStr(c, it, "$1"), it.info) + processImportObjC(c, sym, getOptionalStr(c, it, "$1"), it.info) of wAlign: - if sym.typ == nil: invalidPragma(it) + if sym.typ == nil: invalidPragma(c, it) var align = expectIntLit(c, it) if (not isPowerOfTwo(align) and align != 0) or align >% high(int16): - localError(it.info, errPowerOfTwoExpected) + localError(c.config, it.info, "power of two expected") else: sym.typ.align = align.int16 of wSize: - if sym.typ == nil: invalidPragma(it) + if sym.typ == nil: invalidPragma(c, it) var size = expectIntLit(c, it) if not isPowerOfTwo(size) or size <= 0 or size > 8: - localError(it.info, errPowerOfTwoExpected) + localError(c.config, it.info, "power of two expected") else: sym.typ.size = size of wNodecl: - noVal(it) + noVal(c, it) incl(sym.loc.flags, lfNoDecl) of wPure, wAsmNoStackFrame: - noVal(it) + noVal(c, it) if sym != nil: - if k == wPure and sym.kind in routineKinds: invalidPragma(it) + if k == wPure and sym.kind in routineKinds: invalidPragma(c, it) else: incl(sym.flags, sfPure) of wVolatile: - noVal(it) + noVal(c, it) incl(sym.flags, sfVolatile) of wRegister: - noVal(it) + noVal(c, it) incl(sym.flags, sfRegister) of wThreadVar: - noVal(it) - incl(sym.flags, sfThread) - of wDeadCodeElim: pragmaDeadCodeElim(c, it) + noVal(c, it) + incl(sym.flags, {sfThread, sfGlobal}) + of wDeadCodeElimUnused: discard # deprecated, dead code elim always on of wNoForward: pragmaNoForward(c, it) of wReorder: pragmaNoForward(c, it, sfReorder) of wMagic: processMagic(c, it, sym) of wCompileTime: - noVal(it) + noVal(c, it) incl(sym.flags, sfCompileTime) incl(sym.loc.flags, lfNoDecl) of wGlobal: - noVal(it) + noVal(c, it) incl(sym.flags, sfGlobal) incl(sym.flags, sfPure) of wMerge: # only supported for backwards compat, doesn't do anything anymore - noVal(it) + noVal(c, it) of wConstructor: - noVal(it) + noVal(c, it) incl(sym.flags, sfConstructor) of wHeader: var lib = getLib(c, libHeader, getStrLitNode(c, it)) @@ -762,101 +811,105 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, of wOverride: sym.flags.incl sfOverriden of wNosideeffect: - noVal(it) + noVal(c, it) incl(sym.flags, sfNoSideEffect) if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect) of wSideeffect: - noVal(it) + noVal(c, it) incl(sym.flags, sfSideEffect) of wNoreturn: - noVal(it) + noVal(c, it) incl(sym.flags, sfNoReturn) if sym.typ[0] != nil: - localError(sym.ast[paramsPos][0].info, errNoReturnWithReturnTypeNotAllowed) + localError(c.config, sym.ast[paramsPos][0].info, + ".noreturn with return type not allowed") of wDynlib: processDynLib(c, it, sym) of wCompilerProc, wCore: - noVal(it) # compilerproc may not get a string! + noVal(c, it) # compilerproc may not get a string! cppDefine(c.graph.config, sym.name.s) - if sfFromGeneric notin sym.flags: markCompilerProc(sym) + if sfFromGeneric notin sym.flags: markCompilerProc(c, sym) of wProcVar: - noVal(it) + noVal(c, it) incl(sym.flags, sfProcvar) of wExplain: sym.flags.incl sfExplain of wDeprecated: - if it.kind == nkExprColonExpr: deprecatedStmt(c, it) + if sym != nil and sym.kind in routineKinds: + if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it) + incl(sym.flags, sfDeprecated) + elif it.kind in nkPragmaCallKinds: deprecatedStmt(c, it) elif sym != nil: incl(sym.flags, sfDeprecated) else: incl(c.module.flags, sfDeprecated) of wVarargs: - noVal(it) - if sym.typ == nil: invalidPragma(it) + noVal(c, it) + if sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfVarargs) of wBorrow: if sym.kind == skType: - typeBorrow(sym, it) + typeBorrow(c, sym, it) else: - noVal(it) + noVal(c, it) incl(sym.flags, sfBorrow) of wFinal: - noVal(it) - if sym.typ == nil: invalidPragma(it) + noVal(c, it) + if sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfFinal) of wInheritable: - noVal(it) - if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(it) + noVal(c, it) + if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(c, it) else: incl(sym.typ.flags, tfInheritable) of wPackage: - noVal(it) - if sym.typ == nil: invalidPragma(it) + noVal(c, it) + if sym.typ == nil: invalidPragma(c, it) else: incl(sym.flags, sfForward) of wAcyclic: - noVal(it) - if sym.typ == nil: invalidPragma(it) + noVal(c, it) + if sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfAcyclic) of wShallow: - noVal(it) - if sym.typ == nil: invalidPragma(it) + noVal(c, it) + if sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfShallow) of wThread: - noVal(it) + noVal(c, it) incl(sym.flags, sfThread) incl(sym.flags, sfProcvar) if sym.typ != nil: incl(sym.typ.flags, tfThread) if sym.typ.callConv == ccClosure: sym.typ.callConv = ccDefault of wGcSafe: - noVal(it) + noVal(c, it) if sym != nil: if sym.kind != skType: incl(sym.flags, sfThread) if sym.typ != nil: incl(sym.typ.flags, tfGcSafe) - else: invalidPragma(it) + else: invalidPragma(c, it) else: discard "no checking if used as a code block" of wPacked: - noVal(it) - if sym.typ == nil: invalidPragma(it) + noVal(c, it) + if sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfPacked) - of wHint: message(it.info, hintUser, expectStrLit(c, it)) - of wWarning: message(it.info, warnUser, expectStrLit(c, it)) + of wHint: message(c.config, it.info, hintUser, expectStrLit(c, it)) + of wWarning: message(c.config, it.info, warnUser, expectStrLit(c, it)) of wError: if sym != nil and sym.isRoutine: # This is subtle but correct: the error *statement* is only # allowed for top level statements. Seems to be easier than # distinguishing properly between # ``proc p() {.error}`` and ``proc p() = {.error: "msg".}`` - noVal(it) + noVal(c, it) incl(sym.flags, sfError) else: - localError(it.info, errUser, expectStrLit(c, it)) - of wFatal: fatal(it.info, errUser, expectStrLit(c, it)) + localError(c.config, it.info, errUser, expectStrLit(c, it)) + of wFatal: fatal(c.config, it.info, errUser, expectStrLit(c, it)) of wDefine: processDefine(c, it) of wUndef: processUndef(c, it) of wCompile: processCompile(c, it) of wLink: processCommonLink(c, it, linkNormal) of wLinksys: processCommonLink(c, it, linkSys) - of wPassl: extccomp.addLinkOption(expectStrLit(c, it)) - of wPassc: extccomp.addCompileOption(expectStrLit(c, it)) + of wPassl: extccomp.addLinkOption(c.config, expectStrLit(c, it)) + of wPassc: extccomp.addCompileOption(c.config, expectStrLit(c, it)) of wBreakpoint: pragmaBreakpoint(c, it) of wWatchPoint: pragmaWatchpoint(c, it) of wPush: @@ -864,130 +917,129 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, result = true of wPop: processPop(c, it) of wPragma: - processPragma(c, n, i) - result = true + if not sym.isNil and sym.kind == skTemplate: + sym.flags.incl sfCustomPragma + else: + processPragma(c, n, i) + result = true of wDiscardable: - noVal(it) + noVal(c, it) if sym != nil: incl(sym.flags, sfDiscardable) of wNoInit: - noVal(it) + noVal(c, it) if sym != nil: incl(sym.flags, sfNoInit) of wCodegenDecl: processCodegenDecl(c, it, sym) of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, - wLinedir, wStacktrace, wLinetrace, wOptimization, + wLinedir, wStacktrace, wLinetrace, wOptimization, wMovechecks, wCallconv, wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks, wPatterns: if processOption(c, it): # calling conventions (boring...): - localError(it.info, errOptionExpected) + localError(c.config, it.info, "option expected") of FirstCallConv..LastCallConv: assert(sym != nil) - if sym.typ == nil: invalidPragma(it) + if sym.typ == nil: invalidPragma(c, it) else: sym.typ.callConv = wordToCallConv(k) of wEmit: pragmaEmit(c, it) of wUnroll: pragmaUnroll(c, it) - of wLinearScanEnd, wComputedGoto: noVal(it) + of wLinearScanEnd, wComputedGoto: noVal(c, it) of wEffects: # is later processed in effect analysis: - noVal(it) + noVal(c, it) of wIncompleteStruct: - noVal(it) - if sym.typ == nil: invalidPragma(it) + noVal(c, it) + if sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfIncompleteStruct) of wUnchecked: - noVal(it) - if sym.typ == nil: invalidPragma(it) + noVal(c, it) + if sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfUncheckedArray) of wUnion: - noVal(it) - if sym.typ == nil: invalidPragma(it) + noVal(c, it) + if sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfUnion) of wRequiresInit: - noVal(it) - if sym.typ == nil: invalidPragma(it) + noVal(c, it) + if sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfNeedsInit) of wByRef: - noVal(it) + noVal(c, it) if sym == nil or sym.typ == nil: - if processOption(c, it): localError(it.info, errOptionExpected) + if processOption(c, it): localError(c.config, it.info, "option expected") else: incl(sym.typ.flags, tfByRef) of wByCopy: - noVal(it) - if sym.kind != skType or sym.typ == nil: invalidPragma(it) + noVal(c, it) + if sym.kind != skType or sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfByCopy) of wPartial: - noVal(it) - if sym.kind != skType or sym.typ == nil: invalidPragma(it) + noVal(c, it) + if sym.kind != skType or sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfPartial) - # .partial types can only work with dead code elimination - # to prevent the codegen from doing anything before we compiled - # the whole program: - incl gGlobalOptions, optDeadCodeElim of wInject, wGensym: # We check for errors, but do nothing with these pragmas otherwise # as they are handled directly in 'evalTemplate'. - noVal(it) - if sym == nil: invalidPragma(it) + noVal(c, it) + if sym == nil: invalidPragma(c, it) of wLine: pragmaLine(c, it) of wRaises, wTags: pragmaRaisesOrTags(c, it) of wLocks: if sym == nil: pragmaLockStmt(c, it) - elif sym.typ == nil: invalidPragma(it) + elif sym.typ == nil: invalidPragma(c, it) else: sym.typ.lockLevel = pragmaLocks(c, it) of wBitsize: - if sym == nil or sym.kind != skField or it.kind != nkExprColonExpr: - invalidPragma(it) + if sym == nil or sym.kind != skField: + invalidPragma(c, it) else: sym.bitsize = expectIntLit(c, it) of wGuard: if sym == nil or sym.kind notin {skVar, skLet, skField}: - invalidPragma(it) + invalidPragma(c, it) else: sym.guard = pragmaGuard(c, it, sym.kind) of wGoto: if sym == nil or sym.kind notin {skVar, skLet}: - invalidPragma(it) + invalidPragma(c, it) else: sym.flags.incl sfGoto of wExportNims: - if sym == nil: invalidPragma(it) - else: magicsys.registerNimScriptSymbol(sym) + if sym == nil: invalidPragma(c, it) + else: magicsys.registerNimScriptSymbol(c.graph, sym) of wInjectStmt: - if it.kind != nkExprColonExpr: - localError(it.info, errExprExpected) + if it.kind notin nkPragmaCallKinds or it.len != 2: + localError(c.config, it.info, "expression expected") else: it.sons[1] = c.semExpr(c, it.sons[1]) of wExperimental: - noVal(it) - if isTopLevel(c): - c.module.flags.incl sfExperimental - else: - localError(it.info, "'experimental' pragma only valid as toplevel statement") + processExperimental(c, it, sym) of wThis: - if it.kind == nkExprColonExpr: - c.selfName = considerQuotedIdent(it[1]) - else: + if it.kind in nkPragmaCallKinds and it.len == 2: + c.selfName = considerQuotedIdent(c.config, it[1]) + elif it.kind == nkIdent or it.len == 1: c.selfName = getIdent("self") + else: + localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments") of wNoRewrite: - noVal(it) + noVal(c, it) of wBase: - noVal(it) + noVal(c, it) sym.flags.incl sfBase of wIntDefine: sym.magic = mIntDefine of wStrDefine: sym.magic = mStrDefine of wUsed: - noVal(it) - if sym == nil: invalidPragma(it) + noVal(c, it) + if sym == nil: invalidPragma(c, it) else: sym.flags.incl sfUsed of wLiftLocals: discard - else: invalidPragma(it) - else: invalidPragma(it) + else: invalidPragma(c, it) + else: + n.sons[i] = semCustomPragma(c, it) + proc implicitPragmas*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = @@ -996,13 +1048,15 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode, let o = it.otherPragmas if not o.isNil: pushInfoContext(n.info) - for i in countup(0, sonsLen(o) - 1): + var i = 0 + while i < o.len(): if singlePragma(c, sym, o, i, validPragmas): - internalError(n.info, "implicitPragmas") + internalError(c.config, n.info, "implicitPragmas") + inc i popInfoContext() if lfExportLib in sym.loc.flags and sfExportc notin sym.flags: - localError(n.info, errDynlibRequiresExportc) + localError(c.config, n.info, ".dynlib requires .exportc") var lib = c.optionStack[^1].dynlib if {lfDynamicLib, lfHeader} * sym.loc.flags == {} and sfImportc in sym.flags and lib != nil: @@ -1014,8 +1068,8 @@ proc hasPragma*(n: PNode, pragma: TSpecialWord): bool = if n == nil or n.sons == nil: return false - for p in n.sons: - var key = if p.kind == nkExprColonExpr: p[0] else: p + for p in n: + var key = if p.kind in nkPragmaCallKinds and p.len > 1: p[0] else: p if key.kind == nkIdent and whichKeyword(key.ident) == pragma: return true @@ -1023,9 +1077,10 @@ proc hasPragma*(n: PNode, pragma: TSpecialWord): bool = proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = if n == nil: return - for i in countup(0, sonsLen(n) - 1): - if n.sons[i].kind == nkPragma: pragmaRec(c, sym, n.sons[i], validPragmas) - elif singlePragma(c, sym, n, i, validPragmas): break + var i = 0 + while i < n.len(): + if singlePragma(c, sym, n, i, validPragmas): break + inc i proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = if n == nil: return diff --git a/compiler/procfind.nim b/compiler/procfind.nim index 137765ddb..042947e72 100644 --- a/compiler/procfind.nim +++ b/compiler/procfind.nim @@ -14,14 +14,12 @@ import ast, astalgo, msgs, semdata, types, trees, strutils proc equalGenericParams(procA, procB: PNode): bool = - if sonsLen(procA) != sonsLen(procB): return + if sonsLen(procA) != sonsLen(procB): return false for i in countup(0, sonsLen(procA) - 1): if procA.sons[i].kind != nkSym: - internalError(procA.info, "equalGenericParams") - return + return false if procB.sons[i].kind != nkSym: - internalError(procB.info, "equalGenericParams") - return + return false let a = procA.sons[i].sym let b = procB.sons[i].sym if a.name.id != b.name.id or @@ -57,7 +55,7 @@ proc searchForProcOld*(c: PContext, scope: PScope, fn: PSym): PSym = of paramsEqual: return of paramsIncompatible: - localError(fn.info, errNotOverloadable, fn.name.s) + localError(c.config, fn.info, "overloaded '$1' leads to ambiguous calls" % fn.name.s) return of paramsNotEqual: discard @@ -66,30 +64,25 @@ proc searchForProcOld*(c: PContext, scope: PScope, fn: PSym): PSym = proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym = const flags = {ExactGenericParams, ExactTypeDescValues, ExactConstraints, IgnoreCC} - var it: TIdentIter - result = initIdentIter(it, scope.symbols, fn.name) while result != nil: - if result.kind == fn.kind and sameType(result.typ, fn.typ, flags): + if result.kind == fn.kind: #and sameType(result.typ, fn.typ, flags): case equalParams(result.typ.n, fn.typ.n) of paramsEqual: if (sfExported notin result.flags) and (sfExported in fn.flags): let message = ("public implementation '$1' has non-public " & "forward declaration in $2") % [getProcHeader(result), $result.info] - localError(fn.info, errGenerated, message) + localError(c.config, fn.info, message) return of paramsIncompatible: - localError(fn.info, errNotOverloadable, fn.name.s) + localError(c.config, fn.info, "overloaded '$1' leads to ambiguous calls" % fn.name.s) return of paramsNotEqual: discard - result = nextIdentIter(it, scope.symbols) - return nil - proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym = result = searchForProcNew(c, scope, fn) when false: diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 6735cc1ce..6ac6e797e 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -10,7 +10,7 @@ # This module implements the renderer of the standard Nim representation. import - lexer, options, idents, strutils, ast, msgs + lexer, options, idents, strutils, ast, msgs, configuration type TRenderFlag* = enum @@ -39,8 +39,8 @@ type inPragma: int when defined(nimpretty): pendingNewlineCount: int - origContent: string - + fid*: FileIndex + config*: ConfigRef # We render the source code in a two phases: The first # determines how long the subtree will likely be, the second @@ -91,7 +91,7 @@ const MaxLineLen = 80 LineCommentColumn = 30 -proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags) = +proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags; config: ConfigRef) = g.comStack = @[] g.tokens = @[] g.indent = 0 @@ -103,6 +103,7 @@ proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags) = g.pendingNL = -1 g.pendingWhitespace = -1 g.inGenericParams = false + g.config = config proc addTok(g: var TSrcGen, kind: TTokType, s: string) = var length = len(g.tokens) @@ -173,34 +174,14 @@ proc put(g: var TSrcGen, kind: TTokType, s: string) = else: g.pendingWhitespace = s.len -proc toNimChar(c: char): string = - case c - of '\0': result = "\\x00" # not "\\0" to avoid ambiguous cases like "\\012". - of '\a': result = "\\a" # \x07 - of '\b': result = "\\b" # \x08 - of '\t': result = "\\t" # \x09 - of '\L': result = "\\L" # \x0A - of '\v': result = "\\v" # \x0B - of '\f': result = "\\f" # \x0C - of '\c': result = "\\c" # \x0D - of '\e': result = "\\e" # \x1B - of '\x01'..'\x06', '\x0E'..'\x1A', '\x1C'..'\x1F', '\x80'..'\xFF': - result = "\\x" & strutils.toHex(ord(c), 2) - of '\'', '\"', '\\': result = '\\' & c - else: result = c & "" - -proc makeNimString(s: string): string = - result = "\"" - for i in countup(0, len(s)-1): add(result, toNimChar(s[i])) - add(result, '\"') - proc putComment(g: var TSrcGen, s: string) = if s.isNil: return var i = 0 + let hi = len(s) - 1 var isCode = (len(s) >= 2) and (s[1] != ' ') var ind = g.lineLen var com = "## " - while true: + while i <= hi: case s[i] of '\0': break @@ -208,7 +189,7 @@ proc putComment(g: var TSrcGen, s: string) = put(g, tkComment, com) com = "## " inc(i) - if s[i] == '\x0A': inc(i) + if i < s.len and s[i] == '\x0A': inc(i) optNL(g, ind) of '\x0A': put(g, tkComment, com) @@ -223,12 +204,12 @@ proc putComment(g: var TSrcGen, s: string) = # gets too long: # compute length of the following word: var j = i - while s[j] > ' ': inc(j) + while j <= hi and s[j] > ' ': inc(j) if not isCode and (g.lineLen + (j - i) > MaxLineLen): put(g, tkComment, com) optNL(g, ind) com = "## " - while s[i] > ' ': + while i <= hi and s[i] > ' ': add(com, s[i]) inc(i) put(g, tkComment, com) @@ -237,8 +218,9 @@ proc putComment(g: var TSrcGen, s: string) = proc maxLineLength(s: string): int = if s.isNil: return 0 var i = 0 + let hi = len(s) - 1 var lineLen = 0 - while true: + while i <= hi: case s[i] of '\0': break @@ -257,7 +239,7 @@ proc maxLineLength(s: string): int = proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) = var i = 0 - var hi = len(s) - 1 + let hi = len(s) - 1 var str = "" while i <= hi: case s[i] @@ -325,8 +307,8 @@ proc lsub(g: TSrcGen; n: PNode): int proc litAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string = proc skip(t: PType): PType = result = t - while result.kind in {tyGenericInst, tyRange, tyVar, tyDistinct, - tyOrdinal, tyAlias}: + while result.kind in {tyGenericInst, tyRange, tyVar, tyLent, tyDistinct, + tyOrdinal, tyAlias, tySink}: result = lastSon(result) if n.typ != nil and n.typ.skip.kind in {tyBool, tyEnum}: let enumfields = n.typ.skip.n @@ -349,22 +331,25 @@ proc ulitAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string = proc atom(g: TSrcGen; n: PNode): string = when defined(nimpretty): let comment = if n.info.commentOffsetA < n.info.commentOffsetB: - " " & substr(g.origContent, n.info.commentOffsetA, n.info.commentOffsetB) + " " & fileSection(g.fid, n.info.commentOffsetA, n.info.commentOffsetB) else: "" if n.info.offsetA <= n.info.offsetB: # for some constructed tokens this can not be the case and we're better # off to not mess with the offset then. - return substr(g.origContent, n.info.offsetA, n.info.offsetB) & comment + return fileSection(g.fid, n.info.offsetA, n.info.offsetB) & comment var f: float32 case n.kind of nkEmpty: result = "" of nkIdent: result = n.ident.s of nkSym: result = n.sym.name.s - of nkStrLit: result = makeNimString(n.strVal) - of nkRStrLit: result = "r\"" & replace(n.strVal, "\"", "\"\"") & '\"' + of nkStrLit: result = ""; result.addQuoted(n.strVal) + of nkRStrLit: result = "r\"" & replace(n.strVal, "\"", "\"\"") & '\"' of nkTripleStrLit: result = "\"\"\"" & n.strVal & "\"\"\"" - of nkCharLit: result = '\'' & toNimChar(chr(int(n.intVal))) & '\'' + of nkCharLit: + result = "\'" + result.addEscapedChar(chr(int(n.intVal))); + result.add '\'' of nkIntLit: result = litAux(g, n, n.intVal, 4) of nkInt8Lit: result = litAux(g, n, n.intVal, 1) & "\'i8" of nkInt16Lit: result = litAux(g, n, n.intVal, 2) & "\'i16" @@ -394,7 +379,7 @@ proc atom(g: TSrcGen; n: PNode): string = if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s else: result = "[type node]" else: - internalError("rnimsyn.atom " & $n.kind) + internalError(g.config, "rnimsyn.atom " & $n.kind) result = "" proc lcomma(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int = @@ -432,6 +417,9 @@ proc lsub(g: TSrcGen; n: PNode): int = of nkCommand: result = lsub(g, n.sons[0]) + lcomma(g, n, 1) + 1 of nkExprEqExpr, nkAsgn, nkFastAsgn: result = lsons(g, n) + 3 of nkPar, nkCurly, nkBracket, nkClosure: result = lcomma(g, n) + 2 + of nkTupleConstr: + # assume the trailing comma: + result = lcomma(g, n) + 3 of nkArgList: result = lcomma(g, n) of nkTableConstr: result = if n.len > 0: lcomma(g, n) + 2 else: len("{:}") @@ -898,6 +886,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkBracketLe, "[") gcomma(g, n, 2) put(g, tkBracketRi, "]") + elif n.len > 1 and n.lastSon.kind == nkStmtList: + gsub(g, n[0]) + if n.len > 2: + put(g, tkParLe, "(") + gcomma(g, n, 1, -2) + put(g, tkParRi, ")") + put(g, tkColon, ":") + gsub(g, n, n.len-1) else: if sonsLen(n) >= 1: gsub(g, n.sons[0]) put(g, tkParLe, "(") @@ -994,6 +990,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkParLe, "(") gcomma(g, n, c) put(g, tkParRi, ")") + of nkTupleConstr: + put(g, tkParLe, "(") + gcomma(g, n, c) + if n.len == 1: put(g, tkComma, ",") + put(g, tkParRi, ")") of nkCurly: put(g, tkCurlyLe, "{") gcomma(g, n, c) @@ -1067,6 +1068,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = if n.len > 1: let opr = if n[0].kind == nkIdent: n[0].ident elif n[0].kind == nkSym: n[0].sym.name + elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name else: nil if n[1].kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)): put(g, tkSpaces, Space) @@ -1411,22 +1413,32 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkParLe, "(ComesFrom|") gsub(g, n, 0) put(g, tkParRi, ")") - of nkGotoState, nkState: + of nkGotoState: var c: TContext initContext c - putWithSpace g, tkSymbol, if n.kind == nkState: "state" else: "goto" + putWithSpace g, tkSymbol, "goto" gsons(g, n, c) + of nkState: + var c: TContext + initContext c + putWithSpace g, tkSymbol, "state" + gsub(g, n[0], c) + putWithSpace(g, tkColon, ":") + indentNL(g) + gsons(g, n, c, 1) + dedent(g) + of nkBreakState: put(g, tkTuple, "breakstate") of nkTypeClassTy: gTypeClassTy(g, n) else: #nkNone, nkExplicitTypeListCall: - internalError(n.info, "rnimsyn.gsub(" & $n.kind & ')') + internalError(g.config, n.info, "rnimsyn.gsub(" & $n.kind & ')') proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string = var g: TSrcGen - initSrcGen(g, renderFlags) + initSrcGen(g, renderFlags, newPartialConfigRef()) # do not indent the initial statement list so that # writeFile("file.nim", repr n) # produces working Nim code: @@ -1439,17 +1451,13 @@ proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string = proc `$`*(n: PNode): string = n.renderTree proc renderModule*(n: PNode, infile, outfile: string, - renderFlags: TRenderFlags = {}) = + renderFlags: TRenderFlags = {}; + fid = FileIndex(-1)) = var f: File g: TSrcGen - initSrcGen(g, renderFlags) - when defined(nimpretty): - try: - g.origContent = readFile(infile) - except IOError: - rawMessage(errCannotOpenFile, infile) - + initSrcGen(g, renderFlags, newPartialConfigRef()) + g.fid = fid for i in countup(0, sonsLen(n) - 1): gsub(g, n.sons[i]) optNL(g) @@ -1458,16 +1466,14 @@ proc renderModule*(n: PNode, infile, outfile: string, nkCommentStmt: putNL(g) else: discard gcoms(g) - if optStdout in gGlobalOptions: - write(stdout, g.buf) - elif open(f, outfile, fmWrite): + if open(f, outfile, fmWrite): write(f, g.buf) close(f) else: - rawMessage(errCannotOpenFile, outfile) + rawMessage(g.config, errGenerated, "cannot open file: " & outfile) proc initTokRender*(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {}) = - initSrcGen(r, renderFlags) + initSrcGen(r, renderFlags, newPartialConfigRef()) gsub(r, n) proc getNextTok*(r: var TSrcGen, kind: var TTokType, literal: var string) = diff --git a/compiler/reorder.nim b/compiler/reorder.nim index cde5b3a9f..d50be1d99 100644 --- a/compiler/reorder.nim +++ b/compiler/reorder.nim @@ -1,7 +1,8 @@ -import - intsets, ast, idents, algorithm, renderer, parser, ospaths, strutils, - sequtils, msgs, modulegraphs, syntaxes, options, modulepaths, tables +import + intsets, ast, idents, algorithm, renderer, parser, ospaths, strutils, + sequtils, msgs, modulegraphs, syntaxes, options, modulepaths, tables, + configuration type DepN = ref object @@ -135,13 +136,13 @@ proc hasIncludes(n:PNode): bool = if a.kind == nkIncludeStmt: return true -proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: int32; +proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex; cache: IdentCache): PNode {.procvar.} = - result = syntaxes.parseFile(fileIdx, cache) + result = syntaxes.parseFile(fileIdx, cache, graph.config) graph.addDep(s, fileIdx) - graph.addIncludeDep(s.position.int32, fileIdx) + graph.addIncludeDep(FileIndex s.position, fileIdx) -proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode, +proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode, modulePath: string, includedFiles: var IntSet, cache: IdentCache): PNode = # Parses includes and injects them in the current tree @@ -151,15 +152,15 @@ proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode, for a in n: if a.kind == nkIncludeStmt: for i in 0..<a.len: - var f = checkModuleName(a.sons[i]) + var f = checkModuleName(graph.config, a.sons[i]) if f != InvalidFileIDX: - if containsOrIncl(includedFiles, f): - localError(a.info, errRecursiveDependencyX, f.toFilename) + if containsOrIncl(includedFiles, f.int): + localError(graph.config, a.info, "recursive dependency: '$1'" % f.toFilename) else: let nn = includeModule(graph, module, f, cache) - let nnn = expandIncludes(graph, module, nn, modulePath, + let nnn = expandIncludes(graph, module, nn, modulePath, includedFiles, cache) - excl(includedFiles, f) + excl(includedFiles, f.int) for b in nnn: result.add b else: @@ -189,7 +190,7 @@ proc haveSameKind(dns: seq[DepN]): bool = if dn.pnode.kind != kind: return false -proc mergeSections(comps: seq[seq[DepN]], res: PNode) = +proc mergeSections(conf: ConfigRef; comps: seq[seq[DepN]], res: PNode) = # Merges typeSections and ConstSections when they form # a strong component (ex: circular type definition) for c in comps: @@ -229,7 +230,7 @@ proc mergeSections(comps: seq[seq[DepN]], res: PNode) = wmsg &= "line " & $cs[^1].pnode.info.line & " depends on line " & $cs[j].pnode.info.line & ": " & cs[^1].expls[ci] & "\n" - message(cs[0].pnode.info, warnUser, wmsg) + message(conf, cs[0].pnode.info, warnUser, wmsg) var i = 0 while i < cs.len: @@ -273,9 +274,9 @@ proc hasCommand(n: PNode): bool = of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt, nkLetSection, nkConstSection, nkVarSection, nkIdentDefs: - for a in n: - if a.hasCommand: - return true + for a in n: + if a.hasCommand: + return true else: return false @@ -430,7 +431,7 @@ proc reorder*(graph: ModuleGraph, n: PNode, module: PSym, cache: IdentCache): PN return n var includedFiles = initIntSet() let mpath = module.fileIdx.toFullPath - let n = expandIncludes(graph, module, n, mpath, + let n = expandIncludes(graph, module, n, mpath, includedFiles, cache).splitSections result = newNodeI(nkStmtList, n.info) var deps = newSeq[(IntSet, IntSet)](n.len) @@ -441,4 +442,4 @@ proc reorder*(graph: ModuleGraph, n: PNode, module: PSym, cache: IdentCache): PN var g = buildGraph(n, deps) let comps = getStrongComponents(g) - mergeSections(comps, result) + mergeSections(graph.config, comps, result) diff --git a/compiler/rod.nim b/compiler/rod.nim new file mode 100644 index 000000000..c144f15ef --- /dev/null +++ b/compiler/rod.nim @@ -0,0 +1,26 @@ +# +# +# The Nim Compiler +# (c) Copyright 2017 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements the canonalization for the various caching mechanisms. + +import ast, idgen, msgs + +when not defined(nimSymbolfiles): + template setupModuleCache* = discard + template storeNode*(module: PSym; n: PNode) = discard + template loadNode*(module: PSym; index: var int): PNode = PNode(nil) + + template getModuleId*(fileIdx: FileIndex; fullpath: string): int = getID() + + template addModuleDep*(module, fileIdx: FileIndex; isIncludeFile: bool) = discard + + template storeRemaining*(module: PSym) = discard + +else: + include rodimpl diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim new file mode 100644 index 000000000..aff4f6909 --- /dev/null +++ b/compiler/rodimpl.nim @@ -0,0 +1,947 @@ +# +# +# The Nim Compiler +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements the new compilation cache. + +import strutils, os, intsets, tables, ropes, db_sqlite, msgs, options, types, + renderer, rodutils, std / sha1, idents, astalgo, magicsys + +## Todo: +## - Implement the 'import' replay logic so that the codegen runs over +## dependent modules. +## - Make conditional symbols and the configuration part of a module's +## dependencies. +## - Test multi methods. +## - Implement the limited VM support based on sets. +## - Depencency computation should use signature hashes in order to +## avoid recompiling dependent modules. + +var db: DbConn + +proc hashFileCached(fileIdx: int32; fullpath: string): string = + result = msgs.getHash(fileIdx) + if result.len == 0: + result = $secureHashFile(fullpath) + msgs.setHash(fileIdx, result) + +proc needsRecompile(fileIdx: int32; fullpath: string; cycleCheck: var IntSet): bool = + let root = db.getRow(sql"select id, fullhash from filenames where fullpath = ?", + fullpath) + if root[0].len == 0: return true + if root[1] != hashFileCached(fileIdx, fullpath): + return true + # cycle detection: assume "not changed" is correct. + if cycleCheck.containsOrIncl(fileIdx): + return false + # check dependencies (recursively): + for row in db.fastRows(sql"select fullpath from filenames where id in (select dependency from deps where module = ?)", + root[0]): + let dep = row[0] + if needsRecompile(dep.fileInfoIdx, dep, cycleCheck): + return true + return false + +proc getModuleId*(fileIdx: int32; fullpath: string): int = + if gSymbolFiles != v2Sf: return getID() + let module = db.getRow( + sql"select id, fullHash from modules where fullpath = ?", fullpath) + let currentFullhash = hashFileCached(fileIdx, fullpath) + if module[0].len == 0: + result = int db.insertID(sql"insert into modules(fullpath, interfHash, fullHash) values (?, ?, ?)", + fullpath, "", currentFullhash) + else: + result = parseInt(module[0]) + if currentFullhash == module[1]: + # not changed, so use the cached AST (even if it might be wrong + # due to its dependencies): + doAssert(result != 0) + var cycleCheck = initIntSet() + if not needsRecompile(fileIdx, fullpath, cycleCheck): + return -result + db.exec(sql"update modules set fullHash = ? where id = ?", currentFullhash, module[0]) + db.exec(sql"delete from deps where module = ?", module[0]) + db.exec(sql"delete from types where module = ?", module[0]) + db.exec(sql"delete from syms where module = ?", module[0]) + db.exec(sql"delete from toplevelstmts where module = ?", module[0]) + db.exec(sql"delete from statics where module = ?", module[0]) + +type + TRodWriter = object + module: PSym + sstack: seq[PSym] # a stack of symbols to process + tstack: seq[PType] # a stack of types to process + tmarks, smarks: IntSet + forwardedSyms: seq[PSym] + + PRodWriter = var TRodWriter + +proc initRodWriter(module: PSym): TRodWriter = + result = TRodWriter(module: module, sstack: @[], tstack: @[], + tmarks: initIntSet(), smarks: initIntSet(), forwardedSyms: @[]) + +when false: + proc getDefines(): string = + result = "" + for d in definedSymbolNames(): + if result.len != 0: add(result, " ") + add(result, d) + +const + rodNL = "\L" + +proc pushType(w: PRodWriter, t: PType) = + if not containsOrIncl(w.tmarks, t.id): + w.tstack.add(t) + +proc pushSym(w: PRodWriter, s: PSym) = + if not containsOrIncl(w.smarks, s.id): + w.sstack.add(s) + +proc toDbFileId(fileIdx: int32): int = + if fileIdx == -1: return -1 + let fullpath = fileIdx.toFullPath + let row = db.getRow(sql"select id, fullhash from filenames where fullpath = ?", + fullpath) + let id = row[0] + let fullhash = hashFileCached(fileIdx, fullpath) + if id.len == 0: + result = int db.insertID(sql"insert into filenames(fullpath, fullhash) values (?, ?)", + fullpath, fullhash) + else: + if row[1] != fullhash: + db.exec(sql"update filenames set fullhash = ? where fullpath = ?", fullhash, fullpath) + result = parseInt(id) + +proc fromDbFileId(dbId: int): int32 = + if dbId == -1: return -1 + let fullpath = db.getValue(sql"select fullpath from filenames where id = ?", dbId) + doAssert fullpath.len > 0, "cannot find file name for DB ID " & $dbId + result = fileInfoIdx(fullpath) + +proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, + result: var string) = + if n == nil: + # nil nodes have to be stored too: + result.add("()") + return + result.add('(') + encodeVInt(ord(n.kind), result) + # we do not write comments for now + # Line information takes easily 20% or more of the filesize! Therefore we + # omit line information if it is the same as the parent's line information: + if fInfo.fileIndex != n.info.fileIndex: + result.add('?') + encodeVInt(n.info.col, result) + result.add(',') + encodeVInt(n.info.line, result) + result.add(',') + encodeVInt(toDbFileId(n.info.fileIndex), result) + elif fInfo.line != n.info.line: + result.add('?') + encodeVInt(n.info.col, result) + result.add(',') + encodeVInt(n.info.line, result) + elif fInfo.col != n.info.col: + result.add('?') + encodeVInt(n.info.col, result) + # No need to output the file index, as this is the serialization of one + # file. + let f = n.flags * PersistentNodeFlags + if f != {}: + result.add('$') + encodeVInt(cast[int32](f), result) + if n.typ != nil: + result.add('^') + encodeVInt(n.typ.id, result) + pushType(w, n.typ) + case n.kind + of nkCharLit..nkUInt64Lit: + if n.intVal != 0: + result.add('!') + encodeVBiggestInt(n.intVal, result) + of nkFloatLit..nkFloat64Lit: + if n.floatVal != 0.0: + result.add('!') + encodeStr($n.floatVal, result) + of nkStrLit..nkTripleStrLit: + if n.strVal != "": + result.add('!') + encodeStr(n.strVal, result) + of nkIdent: + result.add('!') + encodeStr(n.ident.s, result) + of nkSym: + result.add('!') + encodeVInt(n.sym.id, result) + pushSym(w, n.sym) + else: + for i in countup(0, sonsLen(n) - 1): + encodeNode(w, n.info, n.sons[i], result) + add(result, ')') + +proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) = + var oldLen = result.len + result.add('<') + if loc.k != low(loc.k): encodeVInt(ord(loc.k), result) + if loc.storage != low(loc.storage): + add(result, '*') + encodeVInt(ord(loc.storage), result) + if loc.flags != {}: + add(result, '$') + encodeVInt(cast[int32](loc.flags), result) + if loc.lode != nil: + add(result, '^') + encodeNode(w, unknownLineInfo(), loc.lode, result) + #encodeVInt(cast[int32](loc.t.id), result) + #pushType(w, loc.t) + if loc.r != nil: + add(result, '!') + encodeStr($loc.r, result) + if oldLen + 1 == result.len: + # no data was necessary, so remove the '<' again: + setLen(result, oldLen) + else: + add(result, '>') + +proc encodeType(w: PRodWriter, t: PType, result: var string) = + if t == nil: + # nil nodes have to be stored too: + result.add("[]") + return + # we need no surrounding [] here because the type is in a line of its own + if t.kind == tyForward: internalError("encodeType: tyForward") + # for the new rodfile viewer we use a preceding [ so that the data section + # can easily be disambiguated: + add(result, '[') + encodeVInt(ord(t.kind), result) + add(result, '+') + encodeVInt(t.id, result) + if t.n != nil: + encodeNode(w, w.module.info, t.n, result) + if t.flags != {}: + add(result, '$') + encodeVInt(cast[int32](t.flags), result) + if t.callConv != low(t.callConv): + add(result, '?') + encodeVInt(ord(t.callConv), result) + if t.owner != nil: + add(result, '*') + encodeVInt(t.owner.id, result) + pushSym(w, t.owner) + if t.sym != nil: + add(result, '&') + encodeVInt(t.sym.id, result) + pushSym(w, t.sym) + if t.size != - 1: + add(result, '/') + encodeVBiggestInt(t.size, result) + if t.align != 2: + add(result, '=') + encodeVInt(t.align, result) + if t.lockLevel.ord != UnspecifiedLockLevel.ord: + add(result, '\14') + encodeVInt(t.lockLevel.int16, result) + if t.destructor != nil and t.destructor.id != 0: + add(result, '\15') + encodeVInt(t.destructor.id, result) + pushSym(w, t.destructor) + if t.deepCopy != nil: + add(result, '\16') + encodeVInt(t.deepcopy.id, result) + pushSym(w, t.deepcopy) + if t.assignment != nil: + add(result, '\17') + encodeVInt(t.assignment.id, result) + pushSym(w, t.assignment) + if t.sink != nil: + add(result, '\18') + encodeVInt(t.sink.id, result) + pushSym(w, t.sink) + for i, s in items(t.methods): + add(result, '\19') + encodeVInt(i, result) + add(result, '\20') + encodeVInt(s.id, result) + pushSym(w, s) + encodeLoc(w, t.loc, result) + for i in countup(0, sonsLen(t) - 1): + if t.sons[i] == nil: + add(result, "^()") + else: + add(result, '^') + encodeVInt(t.sons[i].id, result) + pushType(w, t.sons[i]) + +proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) = + add(result, '|') + encodeVInt(ord(lib.kind), result) + add(result, '|') + encodeStr($lib.name, result) + add(result, '|') + encodeNode(w, info, lib.path, result) + +proc encodeInstantiations(w: PRodWriter; s: seq[PInstantiation]; + result: var string) = + for t in s: + result.add('\15') + encodeVInt(t.sym.id, result) + pushSym(w, t.sym) + for tt in t.concreteTypes: + result.add('\17') + encodeVInt(tt.id, result) + pushType(w, tt) + result.add('\20') + encodeVInt(t.compilesId, result) + +proc encodeSym(w: PRodWriter, s: PSym, result: var string) = + if s == nil: + # nil nodes have to be stored too: + result.add("{}") + return + # we need no surrounding {} here because the symbol is in a line of its own + encodeVInt(ord(s.kind), result) + result.add('+') + encodeVInt(s.id, result) + result.add('&') + encodeStr(s.name.s, result) + if s.typ != nil: + result.add('^') + encodeVInt(s.typ.id, result) + pushType(w, s.typ) + result.add('?') + if s.info.col != -1'i16: encodeVInt(s.info.col, result) + result.add(',') + if s.info.line != -1'i16: encodeVInt(s.info.line, result) + result.add(',') + encodeVInt(toDbFileId(s.info.fileIndex), result) + if s.owner != nil: + result.add('*') + encodeVInt(s.owner.id, result) + pushSym(w, s.owner) + if s.flags != {}: + result.add('$') + encodeVInt(cast[int32](s.flags), result) + if s.magic != mNone: + result.add('@') + encodeVInt(ord(s.magic), result) + result.add('!') + encodeVInt(cast[int32](s.options), result) + if s.position != 0: + result.add('%') + encodeVInt(s.position, result) + if s.offset != - 1: + result.add('`') + encodeVInt(s.offset, result) + encodeLoc(w, s.loc, result) + if s.annex != nil: encodeLib(w, s.annex, s.info, result) + if s.constraint != nil: + add(result, '#') + encodeNode(w, unknownLineInfo(), s.constraint, result) + case s.kind + of skType, skGenericParam: + for t in s.typeInstCache: + result.add('\14') + encodeVInt(t.id, result) + pushType(w, t) + of routineKinds: + encodeInstantiations(w, s.procInstCache, result) + if s.gcUnsafetyReason != nil: + result.add('\16') + encodeVInt(s.gcUnsafetyReason.id, result) + pushSym(w, s.gcUnsafetyReason) + of skModule, skPackage: + encodeInstantiations(w, s.usedGenerics, result) + # we don't serialize: + #tab*: TStrTable # interface table for modules + of skLet, skVar, skField, skForVar: + if s.guard != nil: + result.add('\18') + encodeVInt(s.guard.id, result) + pushSym(w, s.guard) + if s.bitsize != 0: + result.add('\19') + encodeVInt(s.bitsize, result) + else: discard + # lazy loading will soon reload the ast lazily, so the ast needs to be + # the last entry of a symbol: + if s.ast != nil: + # we used to attempt to save space here by only storing a dummy AST if + # it is not necessary, but Nim's heavy compile-time evaluation features + # make that unfeasible nowadays: + encodeNode(w, s.info, s.ast, result) + +proc storeSym(w: PRodWriter; s: PSym) = + if sfForward in s.flags and s.kind != skModule: + w.forwardedSyms.add s + return + var buf = newStringOfCap(160) + encodeSym(w, s, buf) + # XXX only store the name for exported symbols in order to speed up lookup + # times once we enable the skStub logic. + db.exec(sql"insert into syms(nimid, module, name, data, exported) values (?, ?, ?, ?, ?)", + s.id, abs(w.module.id), s.name.s, buf, ord(sfExported in s.flags)) + +proc storeType(w: PRodWriter; t: PType) = + var buf = newStringOfCap(160) + encodeType(w, t, buf) + db.exec(sql"insert into types(nimid, module, data) values (?, ?, ?)", + t.id, abs(w.module.id), buf) + +var w = initRodWriter(nil) + +proc storeNode*(module: PSym; n: PNode) = + if gSymbolFiles != v2Sf: return + w.module = module + var buf = newStringOfCap(160) + encodeNode(w, module.info, n, buf) + db.exec(sql"insert into toplevelstmts(module, position, data) values (?, ?, ?)", + abs(module.id), module.offset, buf) + inc module.offset + var i = 0 + while true: + if i > 10_000: + quit "loop never ends!" + if w.sstack.len > 0: + let s = w.sstack.pop() + when false: + echo "popped ", s.name.s, " ", s.id + storeSym(w, s) + elif w.tstack.len > 0: + let t = w.tstack.pop() + storeType(w, t) + when false: + echo "popped type ", typeToString(t), " ", t.id + else: + break + inc i + +proc storeRemaining*(module: PSym) = + if gSymbolFiles != v2Sf: return + w.module = module + for s in w.forwardedSyms: + assert sfForward notin s.flags + storeSym(w, s) + w.forwardedSyms.setLen 0 + +# ---------------- decoder ----------------------------------- +type + TRodReader = object + module: PSym + #sstack: seq[(PSym, ptr PSym)] # a stack of symbols to process + #tstack: seq[(PType, ptr PType)] # a stack of types to process + + #tmarks, smarks: IntSet + syms: Table[int, PSym] ## XXX make this more efficients + types: Table[int, PType] + cache: IdentCache + + BlobReader = object + s: string + pos: int + + PRodReader = var TRodReader + +proc initRodReader(cache: IdentCache): TRodReader = + TRodReader(module: nil, + syms: initTable[int, PSym](), types: initTable[int, PType](), + cache: cache) + +var gr = initRodReader(newIdentCache()) + +using + r: PRodReader + b: var BlobReader + +proc loadSym(r; id: int, info: TLineInfo): PSym +proc loadType(r; id: int, info: TLineInfo): PType + +proc decodeLineInfo(r; b; info: var TLineInfo) = + if b.s[b.pos] == '?': + inc(b.pos) + if b.s[b.pos] == ',': info.col = -1'i16 + else: info.col = int16(decodeVInt(b.s, b.pos)) + if b.s[b.pos] == ',': + inc(b.pos) + if b.s[b.pos] == ',': info.line = -1'i16 + else: info.line = int16(decodeVInt(b.s, b.pos)) + if b.s[b.pos] == ',': + inc(b.pos) + info.fileIndex = fromDbFileId(decodeVInt(b.s, b.pos)) + +proc skipNode(b) = + assert b.s[b.pos] == '(' + var par = 0 + var pos = b.pos+1 + while true: + case b.s[pos] + of ')': + if par == 0: break + dec par + of '(': inc par + else: discard + inc pos + b.pos = pos+1 # skip ')' + +proc decodeNodeLazyBody(r; b; fInfo: TLineInfo, + belongsTo: PSym): PNode = + result = nil + if b.s[b.pos] == '(': + inc(b.pos) + if b.s[b.pos] == ')': + inc(b.pos) + return # nil node + result = newNodeI(TNodeKind(decodeVInt(b.s, b.pos)), fInfo) + decodeLineInfo(r, b, result.info) + if b.s[b.pos] == '$': + inc(b.pos) + result.flags = cast[TNodeFlags](int32(decodeVInt(b.s, b.pos))) + if b.s[b.pos] == '^': + inc(b.pos) + var id = decodeVInt(b.s, b.pos) + result.typ = loadType(r, id, result.info) + case result.kind + of nkCharLit..nkUInt64Lit: + if b.s[b.pos] == '!': + inc(b.pos) + result.intVal = decodeVBiggestInt(b.s, b.pos) + of nkFloatLit..nkFloat64Lit: + if b.s[b.pos] == '!': + inc(b.pos) + var fl = decodeStr(b.s, b.pos) + result.floatVal = parseFloat(fl) + of nkStrLit..nkTripleStrLit: + if b.s[b.pos] == '!': + inc(b.pos) + result.strVal = decodeStr(b.s, b.pos) + else: + result.strVal = "" + of nkIdent: + if b.s[b.pos] == '!': + inc(b.pos) + var fl = decodeStr(b.s, b.pos) + result.ident = r.cache.getIdent(fl) + else: + internalError(result.info, "decodeNode: nkIdent") + of nkSym: + if b.s[b.pos] == '!': + inc(b.pos) + var id = decodeVInt(b.s, b.pos) + result.sym = loadSym(r, id, result.info) + else: + internalError(result.info, "decodeNode: nkSym") + else: + var i = 0 + while b.s[b.pos] != ')': + when false: + if belongsTo != nil and i == bodyPos: + addSonNilAllowed(result, nil) + belongsTo.offset = b.pos + skipNode(b) + else: + discard + addSonNilAllowed(result, decodeNodeLazyBody(r, b, result.info, nil)) + inc i + if b.s[b.pos] == ')': inc(b.pos) + else: internalError(result.info, "decodeNode: ')' missing") + else: + internalError(fInfo, "decodeNode: '(' missing " & $b.pos) + +proc decodeNode(r; b; fInfo: TLineInfo): PNode = + result = decodeNodeLazyBody(r, b, fInfo, nil) + +proc decodeLoc(r; b; loc: var TLoc, info: TLineInfo) = + if b.s[b.pos] == '<': + inc(b.pos) + if b.s[b.pos] in {'0'..'9', 'a'..'z', 'A'..'Z'}: + loc.k = TLocKind(decodeVInt(b.s, b.pos)) + else: + loc.k = low(loc.k) + if b.s[b.pos] == '*': + inc(b.pos) + loc.storage = TStorageLoc(decodeVInt(b.s, b.pos)) + else: + loc.storage = low(loc.storage) + if b.s[b.pos] == '$': + inc(b.pos) + loc.flags = cast[TLocFlags](int32(decodeVInt(b.s, b.pos))) + else: + loc.flags = {} + if b.s[b.pos] == '^': + inc(b.pos) + loc.lode = decodeNode(r, b, info) + # rrGetType(b, decodeVInt(b.s, b.pos), info) + else: + loc.lode = nil + if b.s[b.pos] == '!': + inc(b.pos) + loc.r = rope(decodeStr(b.s, b.pos)) + else: + loc.r = nil + if b.s[b.pos] == '>': inc(b.pos) + else: internalError(info, "decodeLoc " & b.s[b.pos]) + +proc loadBlob(query: SqlQuery; id: int): BlobReader = + let blob = db.getValue(query, id) + if blob.len == 0: + internalError("symbolfiles: cannot find ID " & $ id) + result = BlobReader(pos: 0) + shallowCopy(result.s, blob) + +proc loadType(r; id: int; info: TLineInfo): PType = + result = r.types.getOrDefault(id) + if result != nil: return result + var b = loadBlob(sql"select data from types where nimid = ?", id) + + if b.s[b.pos] == '[': + inc(b.pos) + if b.s[b.pos] == ']': + inc(b.pos) + return # nil type + new(result) + result.kind = TTypeKind(decodeVInt(b.s, b.pos)) + if b.s[b.pos] == '+': + inc(b.pos) + result.id = decodeVInt(b.s, b.pos) + setId(result.id) + #if debugIds: registerID(result) + else: + internalError(info, "decodeType: no id") + # here this also avoids endless recursion for recursive type + r.types[result.id] = result + if b.s[b.pos] == '(': result.n = decodeNode(r, b, unknownLineInfo()) + if b.s[b.pos] == '$': + inc(b.pos) + result.flags = cast[TTypeFlags](int32(decodeVInt(b.s, b.pos))) + if b.s[b.pos] == '?': + inc(b.pos) + result.callConv = TCallingConvention(decodeVInt(b.s, b.pos)) + if b.s[b.pos] == '*': + inc(b.pos) + result.owner = loadSym(r, decodeVInt(b.s, b.pos), info) + if b.s[b.pos] == '&': + inc(b.pos) + result.sym = loadSym(r, decodeVInt(b.s, b.pos), info) + if b.s[b.pos] == '/': + inc(b.pos) + result.size = decodeVInt(b.s, b.pos) + else: + result.size = -1 + if b.s[b.pos] == '=': + inc(b.pos) + result.align = decodeVInt(b.s, b.pos).int16 + else: + result.align = 2 + + if b.s[b.pos] == '\14': + inc(b.pos) + result.lockLevel = decodeVInt(b.s, b.pos).TLockLevel + else: + result.lockLevel = UnspecifiedLockLevel + + if b.s[b.pos] == '\15': + inc(b.pos) + result.destructor = loadSym(r, decodeVInt(b.s, b.pos), info) + if b.s[b.pos] == '\16': + inc(b.pos) + result.deepCopy = loadSym(r, decodeVInt(b.s, b.pos), info) + if b.s[b.pos] == '\17': + inc(b.pos) + result.assignment = loadSym(r, decodeVInt(b.s, b.pos), info) + if b.s[b.pos] == '\18': + inc(b.pos) + result.sink = loadSym(r, decodeVInt(b.s, b.pos), info) + while b.s[b.pos] == '\19': + inc(b.pos) + let x = decodeVInt(b.s, b.pos) + doAssert b.s[b.pos] == '\20' + inc(b.pos) + let y = loadSym(r, decodeVInt(b.s, b.pos), info) + result.methods.safeAdd((x, y)) + decodeLoc(r, b, result.loc, info) + while b.s[b.pos] == '^': + inc(b.pos) + if b.s[b.pos] == '(': + inc(b.pos) + if b.s[b.pos] == ')': inc(b.pos) + else: internalError(info, "decodeType ^(" & b.s[b.pos]) + rawAddSon(result, nil) + else: + var d = decodeVInt(b.s, b.pos) + rawAddSon(result, loadType(r, d, info)) + +proc decodeLib(r; b; info: TLineInfo): PLib = + result = nil + if b.s[b.pos] == '|': + new(result) + inc(b.pos) + result.kind = TLibKind(decodeVInt(b.s, b.pos)) + if b.s[b.pos] != '|': internalError("decodeLib: 1") + inc(b.pos) + result.name = rope(decodeStr(b.s, b.pos)) + if b.s[b.pos] != '|': internalError("decodeLib: 2") + inc(b.pos) + result.path = decodeNode(r, b, info) + +proc decodeInstantiations(r; b; info: TLineInfo; + s: var seq[PInstantiation]) = + while b.s[b.pos] == '\15': + inc(b.pos) + var ii: PInstantiation + new ii + ii.sym = loadSym(r, decodeVInt(b.s, b.pos), info) + ii.concreteTypes = @[] + while b.s[b.pos] == '\17': + inc(b.pos) + ii.concreteTypes.add loadType(r, decodeVInt(b.s, b.pos), info) + if b.s[b.pos] == '\20': + inc(b.pos) + ii.compilesId = decodeVInt(b.s, b.pos) + s.safeAdd ii + +proc loadSymFromBlob(r; b; info: TLineInfo): PSym = + if b.s[b.pos] == '{': + inc(b.pos) + if b.s[b.pos] == '}': + inc(b.pos) + return # nil sym + var k = TSymKind(decodeVInt(b.s, b.pos)) + var id: int + if b.s[b.pos] == '+': + inc(b.pos) + id = decodeVInt(b.s, b.pos) + setId(id) + else: + internalError(info, "decodeSym: no id") + var ident: PIdent + if b.s[b.pos] == '&': + inc(b.pos) + ident = r.cache.getIdent(decodeStr(b.s, b.pos)) + else: + internalError(info, "decodeSym: no ident") + #echo "decoding: {", ident.s + new(result) + result.id = id + result.kind = k + result.name = ident # read the rest of the symbol description: + r.syms[result.id] = result + if b.s[b.pos] == '^': + inc(b.pos) + result.typ = loadType(r, decodeVInt(b.s, b.pos), info) + decodeLineInfo(r, b, result.info) + if b.s[b.pos] == '*': + inc(b.pos) + result.owner = loadSym(r, decodeVInt(b.s, b.pos), result.info) + if b.s[b.pos] == '$': + inc(b.pos) + result.flags = cast[TSymFlags](int32(decodeVInt(b.s, b.pos))) + if b.s[b.pos] == '@': + inc(b.pos) + result.magic = TMagic(decodeVInt(b.s, b.pos)) + if b.s[b.pos] == '!': + inc(b.pos) + result.options = cast[TOptions](int32(decodeVInt(b.s, b.pos))) + else: + result.options = r.module.options + if b.s[b.pos] == '%': + inc(b.pos) + result.position = decodeVInt(b.s, b.pos) + if b.s[b.pos] == '`': + inc(b.pos) + result.offset = decodeVInt(b.s, b.pos) + else: + result.offset = - 1 + decodeLoc(r, b, result.loc, result.info) + result.annex = decodeLib(r, b, info) + if b.s[b.pos] == '#': + inc(b.pos) + result.constraint = decodeNode(r, b, unknownLineInfo()) + case result.kind + of skType, skGenericParam: + while b.s[b.pos] == '\14': + inc(b.pos) + result.typeInstCache.safeAdd loadType(r, decodeVInt(b.s, b.pos), result.info) + of routineKinds: + decodeInstantiations(r, b, result.info, result.procInstCache) + if b.s[b.pos] == '\16': + inc(b.pos) + result.gcUnsafetyReason = loadSym(r, decodeVInt(b.s, b.pos), result.info) + of skModule, skPackage: + decodeInstantiations(r, b, result.info, result.usedGenerics) + of skLet, skVar, skField, skForVar: + if b.s[b.pos] == '\18': + inc(b.pos) + result.guard = loadSym(r, decodeVInt(b.s, b.pos), result.info) + if b.s[b.pos] == '\19': + inc(b.pos) + result.bitsize = decodeVInt(b.s, b.pos).int16 + else: discard + + if b.s[b.pos] == '(': + #if result.kind in routineKinds: + # result.ast = decodeNodeLazyBody(b, result.info, result) + #else: + result.ast = decodeNode(r, b, result.info) + if sfCompilerProc in result.flags: + registerCompilerProc(result) + #echo "loading ", result.name.s + +proc loadSym(r; id: int; info: TLineInfo): PSym = + result = r.syms.getOrDefault(id) + if result != nil: return result + var b = loadBlob(sql"select data from syms where nimid = ?", id) + result = loadSymFromBlob(r, b, info) + doAssert id == result.id, "symbol ID is not consistent!" + +proc loadModuleSymTab(r; module: PSym) = + ## goal: fill module.tab + gr.syms[module.id] = module + for row in db.fastRows(sql"select nimid, data from syms where module = ? and exported = 1", abs(module.id)): + let id = parseInt(row[0]) + var s = r.syms.getOrDefault(id) + if s == nil: + var b = BlobReader(pos: 0) + shallowCopy(b.s, row[1]) + s = loadSymFromBlob(r, b, module.info) + assert s != nil + strTableAdd(module.tab, s) + if sfSystemModule in module.flags: + magicsys.systemModule = module + +proc loadNode*(module: PSym; index: int): PNode = + assert gSymbolFiles == v2Sf + if index == 0: + loadModuleSymTab(gr, module) + #index = parseInt db.getValue( + # sql"select min(id) from toplevelstmts where module = ?", abs module.id) + var b = BlobReader(pos: 0) + b.s = db.getValue(sql"select data from toplevelstmts where position = ? and module = ?", + index, abs module.id) + if b.s.len == 0: + db.exec(sql"insert into controlblock(idgen) values (?)", gFrontEndId) + return nil # end marker + gr.module = module + result = decodeNode(gr, b, module.info) + +proc addModuleDep*(module, fileIdx: int32; isIncludeFile: bool) = + if gSymbolFiles != v2Sf: return + + let a = toDbFileId(module) + let b = toDbFileId(fileIdx) + + db.exec(sql"insert into deps(module, dependency, isIncludeFile) values (?, ?, ?)", + a, b, ord(isIncludeFile)) + +# --------------- Database model --------------------------------------------- + +proc createDb() = + db.exec(sql""" + create table if not exists controlblock( + idgen integer not null + ); + """) + + db.exec(sql""" + create table if not exists filenames( + id integer primary key, + fullpath varchar(8000) not null, + fullHash varchar(256) not null + ); + """) + db.exec sql"create index if not exists FilenameIx on filenames(fullpath);" + + db.exec(sql""" + create table if not exists modules( + id integer primary key, + fullpath varchar(8000) not null, + interfHash varchar(256) not null, + fullHash varchar(256) not null, + + created timestamp not null default (DATETIME('now')) + );""") + db.exec(sql"""create unique index if not exists SymNameIx on modules(fullpath);""") + + db.exec(sql""" + create table if not exists deps( + id integer primary key, + module integer not null, + dependency integer not null, + isIncludeFile integer not null, + foreign key (module) references filenames(id), + foreign key (dependency) references filenames(id) + );""") + db.exec(sql"""create index if not exists DepsIx on deps(module);""") + + db.exec(sql""" + create table if not exists types( + id integer primary key, + nimid integer not null, + module integer not null, + data blob not null, + foreign key (module) references module(id) + ); + """) + db.exec sql"create index TypeByModuleIdx on types(module);" + db.exec sql"create index TypeByNimIdIdx on types(nimid);" + + db.exec(sql""" + create table if not exists syms( + id integer primary key, + nimid integer not null, + module integer not null, + name varchar(256) not null, + data blob not null, + exported int not null, + foreign key (module) references module(id) + ); + """) + db.exec sql"create index if not exists SymNameIx on syms(name);" + db.exec sql"create index SymByNameAndModuleIdx on syms(name, module);" + db.exec sql"create index SymByModuleIdx on syms(module);" + db.exec sql"create index SymByNimIdIdx on syms(nimid);" + + + db.exec(sql""" + create table if not exists toplevelstmts( + id integer primary key, + position integer not null, + module integer not null, + data blob not null, + foreign key (module) references module(id) + ); + """) + db.exec sql"create index TopLevelStmtByModuleIdx on toplevelstmts(module);" + db.exec sql"create index TopLevelStmtByPositionIdx on toplevelstmts(position);" + + db.exec(sql""" + create table if not exists statics( + id integer primary key, + module integer not null, + data blob not null, + foreign key (module) references module(id) + ); + """) + db.exec sql"create index StaticsByModuleIdx on toplevelstmts(module);" + db.exec sql"insert into controlblock(idgen) values (0)" + +proc setupModuleCache* = + if gSymbolFiles != v2Sf: return + let dbfile = getNimcacheDir() / "rodfiles.db" + if not fileExists(dbfile): + db = open(connection=dbfile, user="nim", password="", + database="nim") + createDb() + else: + db = open(connection=dbfile, user="nim", password="", + database="nim") + db.exec(sql"pragma journal_mode=off") + db.exec(sql"pragma SYNCHRONOUS=off") + db.exec(sql"pragma LOCKING_MODE=exclusive") + let lastId = db.getValue(sql"select max(idgen) from controlblock") + if lastId.len > 0: + idgen.setId(parseInt lastId) diff --git a/compiler/rodread.nim b/compiler/rodread.nim index dfa8fc52b..52e7a924c 100644 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -90,7 +90,8 @@ import os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms, - ropes, idents, securehash, idgen, types, rodutils, memfiles, tables + ropes, idents, std / sha1, idgen, types, rodutils, memfiles, tables, + configuration type TReasonForRecompile* = enum ## all the reasons that can trigger recompilation @@ -126,8 +127,8 @@ type s: cstring # mmap'ed file contents options: TOptions reason: TReasonForRecompile - modDeps: seq[int32] - files: seq[int32] + modDeps: seq[FileIndex] + files: seq[FileIndex] dataIdx: int # offset of start of data section convertersIdx: int # offset of start of converters section initIdx, interfIdx, compilerProcsIdx, methodsIdx: int @@ -143,6 +144,7 @@ type origFile: string inViewMode: bool cache*: IdentCache + config: ConfigRef PRodReader* = ref TRodReader @@ -163,11 +165,11 @@ proc decodeLineInfo(r: PRodReader, info: var TLineInfo) = else: info.col = int16(decodeVInt(r.s, r.pos)) if r.s[r.pos] == ',': inc(r.pos) - if r.s[r.pos] == ',': info.line = -1'i16 - else: info.line = int16(decodeVInt(r.s, r.pos)) + if r.s[r.pos] == ',': info.line = 0'u16 + else: info.line = uint16(decodeVInt(r.s, r.pos)) if r.s[r.pos] == ',': inc(r.pos) - info = newLineInfo(r.files[decodeVInt(r.s, r.pos)], info.line, info.col) + info = newLineInfo(r.files[decodeVInt(r.s, r.pos)], int info.line, info.col) proc skipNode(r: PRodReader) = assert r.s[r.pos] == '(' @@ -222,14 +224,14 @@ proc decodeNodeLazyBody(r: PRodReader, fInfo: TLineInfo, var fl = decodeStr(r.s, r.pos) result.ident = r.cache.getIdent(fl) else: - internalError(result.info, "decodeNode: nkIdent") + internalError(r.config, result.info, "decodeNode: nkIdent") of nkSym: if r.s[r.pos] == '!': inc(r.pos) var id = decodeVInt(r.s, r.pos) result.sym = rrGetSym(r, id, result.info) else: - internalError(result.info, "decodeNode: nkSym") + internalError(r.config, result.info, "decodeNode: nkSym") else: var i = 0 while r.s[r.pos] != ')': @@ -241,9 +243,9 @@ proc decodeNodeLazyBody(r: PRodReader, fInfo: TLineInfo, addSonNilAllowed(result, decodeNodeLazyBody(r, result.info, nil)) inc i if r.s[r.pos] == ')': inc(r.pos) - else: internalError(result.info, "decodeNode: ')' missing") + else: internalError(r.config, result.info, "decodeNode: ')' missing") else: - internalError(fInfo, "decodeNode: '(' missing " & $r.pos) + internalError(r.config, fInfo, "decodeNode: '(' missing " & $r.pos) proc decodeNode(r: PRodReader, fInfo: TLineInfo): PNode = result = decodeNodeLazyBody(r, fInfo, nil) @@ -277,7 +279,7 @@ proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) = else: loc.r = nil if r.s[r.pos] == '>': inc(r.pos) - else: internalError(info, "decodeLoc " & r.s[r.pos]) + else: internalError(r.config, info, "decodeLoc " & r.s[r.pos]) proc decodeType(r: PRodReader, info: TLineInfo): PType = result = nil @@ -294,7 +296,7 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType = setId(result.id) if debugIds: registerID(result) else: - internalError(info, "decodeType: no id") + internalError(r.config, info, "decodeType: no id") # here this also avoids endless recursion for recursive type idTablePut(gTypeTable, result, result) if r.s[r.pos] == '(': result.n = decodeNode(r, unknownLineInfo()) @@ -345,14 +347,14 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType = doAssert r.s[r.pos] == '\20' inc(r.pos) let y = rrGetSym(r, decodeVInt(r.s, r.pos), info) - result.methods.safeAdd((x, y)) + result.methods.add((x, y)) decodeLoc(r, result.loc, info) while r.s[r.pos] == '^': inc(r.pos) if r.s[r.pos] == '(': inc(r.pos) if r.s[r.pos] == ')': inc(r.pos) - else: internalError(info, "decodeType ^(" & r.s[r.pos]) + else: internalError(r.config, info, "decodeType ^(" & r.s[r.pos]) rawAddSon(result, nil) else: var d = decodeVInt(r.s, r.pos) @@ -364,10 +366,10 @@ proc decodeLib(r: PRodReader, info: TLineInfo): PLib = new(result) inc(r.pos) result.kind = TLibKind(decodeVInt(r.s, r.pos)) - if r.s[r.pos] != '|': internalError("decodeLib: 1") + if r.s[r.pos] != '|': internalError(r.config, "decodeLib: 1") inc(r.pos) result.name = rope(decodeStr(r.s, r.pos)) - if r.s[r.pos] != '|': internalError("decodeLib: 2") + if r.s[r.pos] != '|': internalError(r.config, "decodeLib: 2") inc(r.pos) result.path = decodeNode(r, info) @@ -385,7 +387,7 @@ proc decodeInstantiations(r: PRodReader; info: TLineInfo; if r.s[r.pos] == '\20': inc(r.pos) ii.compilesId = decodeVInt(r.s, r.pos) - s.safeAdd ii + s.add ii proc decodeSym(r: PRodReader, info: TLineInfo): PSym = var @@ -403,12 +405,12 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym = id = decodeVInt(r.s, r.pos) setId(id) else: - internalError(info, "decodeSym: no id") + internalError(r.config, info, "decodeSym: no id") if r.s[r.pos] == '&': inc(r.pos) ident = r.cache.getIdent(decodeStr(r.s, r.pos)) else: - internalError(info, "decodeSym: no ident") + internalError(r.config, info, "decodeSym: no ident") #echo "decoding: {", ident.s result = r.syms.getOrDefault(id) if result == nil: @@ -417,7 +419,7 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym = r.syms[result.id] = result if debugIds: registerID(result) elif result.id != id: - internalError(info, "decodeSym: wrong id") + internalError(r.config, info, "decodeSym: wrong id") elif result.kind != skStub and not r.inViewMode: # we already loaded the symbol return @@ -465,7 +467,7 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym = of skType, skGenericParam: while r.s[r.pos] == '\14': inc(r.pos) - result.typeInstCache.safeAdd rrGetType(r, decodeVInt(r.s, r.pos), result.info) + result.typeInstCache.add rrGetType(r, decodeVInt(r.s, r.pos), result.info) of routineKinds: decodeInstantiations(r, result.info, result.procInstCache) if r.s[r.pos] == '\16': @@ -512,7 +514,7 @@ proc skipSection(r: PRodReader) = else: discard inc(r.pos) else: - internalError("skipSection " & $r.line) + internalError(r.config, "skipSection " & $r.line) proc rdWord(r: PRodReader): string = result = "" @@ -530,7 +532,7 @@ proc newStub(r: PRodReader, name: string, id: int): PSym = if debugIds: registerID(result) proc processInterf(r: PRodReader, module: PSym) = - if r.interfIdx == 0: internalError("processInterf") + if r.interfIdx == 0: internalError(r.config, "processInterf") r.pos = r.interfIdx while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'): var w = decodeStr(r.s, r.pos) @@ -543,7 +545,7 @@ proc processInterf(r: PRodReader, module: PSym) = r.syms[s.id] = s proc processCompilerProcs(r: PRodReader, module: PSym) = - if r.compilerProcsIdx == 0: internalError("processCompilerProcs") + if r.compilerProcsIdx == 0: internalError(r.config, "processCompilerProcs") r.pos = r.compilerProcsIdx while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'): var w = decodeStr(r.s, r.pos) @@ -586,7 +588,7 @@ proc cmdChangeTriggersRecompilation(old, new: TCommands): bool = # new command forces us to consider it here :-) case old of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, - cmdCompileToJS, cmdCompileToPHP, cmdCompileToLLVM: + cmdCompileToJS, cmdCompileToLLVM: if new in {cmdDoc, cmdCheck, cmdIdeTools, cmdPretty, cmdDef, cmdInteractive}: return false @@ -621,26 +623,26 @@ proc processRodFile(r: PRodReader, hash: SecureHash) = of "OPTIONS": inc(r.pos) # skip ':' r.options = cast[TOptions](int32(decodeVInt(r.s, r.pos))) - if options.gOptions != r.options: r.reason = rrOptions + if r.config.options != r.options: r.reason = rrOptions of "GOPTIONS": inc(r.pos) # skip ':' var dep = cast[TGlobalOptions](int32(decodeVInt(r.s, r.pos))) - if gGlobalOptions-harmlessOptions != dep-harmlessOptions: + if r.config.globalOptions-harmlessOptions != dep-harmlessOptions: r.reason = rrOptions of "CMD": inc(r.pos) # skip ':' var dep = cast[TCommands](int32(decodeVInt(r.s, r.pos))) - if cmdChangeTriggersRecompilation(dep, gCmd): r.reason = rrOptions + if cmdChangeTriggersRecompilation(dep, r.config.cmd): r.reason = rrOptions of "DEFINES": inc(r.pos) # skip ':' d = 0 while r.s[r.pos] > '\x0A': w = decodeStr(r.s, r.pos) inc(d) - if not condsyms.isDefined(r.cache.getIdent(w)): + if not isDefined(r.config, w): r.reason = rrDefines #MessageOut('not defined, but should: ' + w); if r.s[r.pos] == ' ': inc(r.pos) - if d != countDefinedSymbols(): r.reason = rrDefines + if d != countDefinedSymbols(r.config.symbols): r.reason = rrDefines of "FILES": inc(r.pos, 2) # skip "(\10" inc(r.line) @@ -648,7 +650,7 @@ proc processRodFile(r: PRodReader, hash: SecureHash) = let finalPath = decodeStr(r.s, r.pos) #let resolvedPath = relativePath.findModule(r.origFile) #let finalPath = if resolvedPath.len > 0: resolvedPath else: relativePath - r.files.add(finalPath.fileInfoIdx) + r.files.add(fileInfoIdx(r.config, finalPath)) inc(r.pos) # skip #10 inc(r.line) if r.s[r.pos] == ')': inc(r.pos) @@ -696,7 +698,7 @@ proc processRodFile(r: PRodReader, hash: SecureHash) = r.initIdx = r.pos + 2 # "(\10" skipSection(r) else: - internalError("invalid section: '" & section & + internalError(r.config, "invalid section: '" & section & "' at " & $r.line & " in " & r.filename) #MsgWriteln("skipping section: " & section & # " at " & $r.line & " in " & r.filename) @@ -712,9 +714,11 @@ proc startsWith(buf: cstring, token: string, pos = 0): bool = result = s == token.len proc newRodReader(modfilename: string, hash: SecureHash, - readerIndex: int; cache: IdentCache): PRodReader = + readerIndex: int; cache: IdentCache; + config: ConfigRef): PRodReader = new(result) result.cache = cache + result.config = config try: result.memfile = memfiles.open(modfilename) except OSError: @@ -757,13 +761,13 @@ proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType = # load the type: var oldPos = r.pos var d = iiTableGet(r.index.tab, id) - if d == InvalidKey: internalError(info, "rrGetType") + if d == InvalidKey: internalError(r.config, info, "rrGetType") r.pos = d + r.dataIdx result = decodeType(r, info) r.pos = oldPos type - TFileModuleRec{.final.} = object + TFileModuleRec = object filename*: string reason*: TReasonForRecompile rd*: PRodReader @@ -776,7 +780,7 @@ var gMods*: TFileModuleMap = @[] proc decodeSymSafePos(rd: PRodReader, offset: int, info: TLineInfo): PSym = # all compiled modules - if rd.dataIdx == 0: internalError(info, "dataIdx == 0") + if rd.dataIdx == 0: internalError(rd.config, info, "dataIdx == 0") var oldPos = rd.pos rd.pos = offset + rd.dataIdx result = decodeSym(rd, info) @@ -811,8 +815,9 @@ proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym = if moduleID < 0: var x = "" encodeVInt(id, x) - internalError(info, "missing from both indexes: +" & x) + internalError(r.config, info, "missing from both indexes: +" & x) var rd = getReader(moduleID) + doAssert rd != nil d = iiTableGet(rd.index.tab, id) if d != InvalidKey: result = decodeSymSafePos(rd, d, info) @@ -820,14 +825,14 @@ proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym = var x = "" encodeVInt(id, x) when false: findSomeWhere(id) - internalError(info, "rrGetSym: no reader found: +" & x) + internalError(r.config, info, "rrGetSym: no reader found: +" & x) else: # own symbol: result = decodeSymSafePos(r, d, info) if result != nil and result.kind == skStub: rawLoadStub(result) proc loadInitSection*(r: PRodReader): PNode = - if r.initIdx == 0 or r.dataIdx == 0: internalError("loadInitSection") + if r.initIdx == 0 or r.dataIdx == 0: internalError(r.config, "loadInitSection") var oldPos = r.pos r.pos = r.initIdx result = newNode(nkStmtList) @@ -844,7 +849,7 @@ proc loadConverters(r: PRodReader) = # We have to ensure that no exported converter is a stub anymore, and the # import mechanism takes care of the rest. if r.convertersIdx == 0 or r.dataIdx == 0: - internalError("importConverters") + internalError(r.config, "importConverters") r.pos = r.convertersIdx while r.s[r.pos] > '\x0A': var d = decodeVInt(r.s, r.pos) @@ -853,36 +858,36 @@ proc loadConverters(r: PRodReader) = proc loadMethods(r: PRodReader) = if r.methodsIdx == 0 or r.dataIdx == 0: - internalError("loadMethods") + internalError(r.config, "loadMethods") r.pos = r.methodsIdx while r.s[r.pos] > '\x0A': var d = decodeVInt(r.s, r.pos) r.methods.add(rrGetSym(r, d, unknownLineInfo())) if r.s[r.pos] == ' ': inc(r.pos) -proc getHash*(fileIdx: int32): SecureHash = - if fileIdx <% gMods.len and gMods[fileIdx].hashDone: - return gMods[fileIdx].hash +proc getHash*(fileIdx: FileIndex): SecureHash = + if fileIdx.int32 <% gMods.len and gMods[fileIdx.int32].hashDone: + return gMods[fileIdx.int32].hash result = secureHashFile(fileIdx.toFullPath) - if fileIdx >= gMods.len: setLen(gMods, fileIdx+1) - gMods[fileIdx].hash = result + if fileIdx.int32 >= gMods.len: setLen(gMods, fileIdx.int32+1) + gMods[fileIdx.int32].hash = result template growCache*(cache, pos) = if cache.len <= pos: cache.setLen(pos+1) -proc checkDep(fileIdx: int32; cache: IdentCache): TReasonForRecompile = +proc checkDep(fileIdx: FileIndex; cache: IdentCache; conf: ConfigRef): TReasonForRecompile = assert fileIdx != InvalidFileIDX - growCache gMods, fileIdx - if gMods[fileIdx].reason != rrEmpty: + growCache gMods, fileIdx.int32 + if gMods[fileIdx.int32].reason != rrEmpty: # reason has already been computed for this module: - return gMods[fileIdx].reason + return gMods[fileIdx.int32].reason let filename = fileIdx.toFilename var hash = getHash(fileIdx) - gMods[fileIdx].reason = rrNone # we need to set it here to avoid cycles + gMods[fileIdx.int32].reason = rrNone # we need to set it here to avoid cycles result = rrNone - var rodfile = toGeneratedFile(filename.withPackageName, RodExt) - var r = newRodReader(rodfile, hash, fileIdx, cache) + var rodfile = toGeneratedFile(conf, conf.withPackageName(filename), RodExt) + var r = newRodReader(rodfile, hash, fileIdx.int32, cache, conf) if r == nil: result = (if existsFile(rodfile): rrRodInvalid else: rrRodDoesNotExist) else: @@ -893,32 +898,31 @@ proc checkDep(fileIdx: int32; cache: IdentCache): TReasonForRecompile = # NOTE: we need to process the entire module graph so that no ID will # be used twice! However, compilation speed does not suffer much from # this, since results are cached. - var res = checkDep(systemFileIdx, cache) + var res = checkDep(systemFileIdx, cache, conf) if res != rrNone: result = rrModDeps for i in countup(0, high(r.modDeps)): - res = checkDep(r.modDeps[i], cache) + res = checkDep(r.modDeps[i], cache, conf) if res != rrNone: result = rrModDeps # we cannot break here, because of side-effects of `checkDep` if result != rrNone: - rawMessage(hintProcessing, reasonToFrmt[result] % filename) - if result != rrNone or optForceFullMake in gGlobalOptions: + rawMessage(conf, hintProcessing, reasonToFrmt[result] % filename) + if result != rrNone or optForceFullMake in conf.globalOptions: # recompilation is necessary: if r != nil: memfiles.close(r.memfile) r = nil - gMods[fileIdx].rd = r - gMods[fileIdx].reason = result # now we know better + gMods[fileIdx.int32].rd = r + gMods[fileIdx.int32].reason = result # now we know better -proc handleSymbolFile*(module: PSym; cache: IdentCache): PRodReader = - let fileIdx = module.fileIdx - if gSymbolFiles in {disabledSf, writeOnlySf}: +proc handleSymbolFile*(module: PSym; cache: IdentCache; conf: ConfigRef): PRodReader = + if conf.symbolFiles in {disabledSf, writeOnlySf, v2Sf}: module.id = getID() return nil - idgen.loadMaxIds(options.gProjectPath / options.gProjectName) - - discard checkDep(fileIdx, cache) - if gMods[fileIdx].reason == rrEmpty: internalError("handleSymbolFile") - result = gMods[fileIdx].rd + idgen.loadMaxIds(conf, conf.projectPath / conf.projectName) + let fileIdx = module.fileIdx + discard checkDep(fileIdx, cache, conf) + #if gMods[fileIdx.int32].reason == rrEmpty: internalError("handleSymbolFile") + result = gMods[fileIdx.int32].rd if result != nil: module.id = result.moduleID result.syms[module.id] = module @@ -930,19 +934,20 @@ proc handleSymbolFile*(module: PSym; cache: IdentCache): PRodReader = module.id = getID() proc rawLoadStub(s: PSym) = - if s.kind != skStub: internalError("loadStub") + assert s.kind == skStub + #if s.kind != skStub: internalError("loadStub") var rd = gMods[s.position].rd var theId = s.id # used for later check var d = iiTableGet(rd.index.tab, s.id) - if d == InvalidKey: internalError("loadStub: invalid key") + #if d == InvalidKey: internalError("loadStub: invalid key") var rs = decodeSymSafePos(rd, d, unknownLineInfo()) - if rs != s: - #echo "rs: ", toHex(cast[int](rs.position), int.sizeof * 2), - # "\ns: ", toHex(cast[int](s.position), int.sizeof * 2) - internalError(rs.info, "loadStub: wrong symbol") - elif rs.id != theId: - internalError(rs.info, "loadStub: wrong ID") - #MessageOut('loaded stub: ' + s.name.s); + when false: + if rs != s: + #echo "rs: ", toHex(cast[int](rs.position), int.sizeof * 2), + # "\ns: ", toHex(cast[int](s.position), int.sizeof * 2) + internalError(rs.info, "loadStub: wrong symbol") + elif rs.id != theId: + internalError(rs.info, "loadStub: wrong ID") proc loadStub*(s: PSym) = ## loads the stub symbol `s`. @@ -1030,9 +1035,8 @@ proc writeSym(f: File; s: PSym) = if s.magic != mNone: f.write('@') f.write($s.magic) - if s.options != gOptions: - f.write('!') - f.write($s.options) + f.write('!') + f.write($s.options) if s.position != 0: f.write('%') f.write($s.position) @@ -1083,9 +1087,10 @@ proc writeType(f: File; t: PType) = f.write("]\n") proc viewFile(rodfile: string) = - var r = newRodReader(rodfile, secureHash(""), 0, newIdentCache()) + let conf = newConfigRef() + var r = newRodReader(rodfile, secureHash(""), 0, newIdentCache(), conf) if r == nil: - rawMessage(errGenerated, "cannot open file (or maybe wrong version):" & + rawMessage(conf, errGenerated, "cannot open file (or maybe wrong version):" & rodfile) return r.inViewMode = true @@ -1133,9 +1138,9 @@ proc viewFile(rodfile: string) = outf.write("FILES(\n") while r.s[r.pos] != ')': let relativePath = decodeStr(r.s, r.pos) - let resolvedPath = relativePath.findModule(r.origFile) + let resolvedPath = findModule(conf, relativePath, r.origFile) let finalPath = if resolvedPath.len > 0: resolvedPath else: relativePath - r.files.add(finalPath.fileInfoIdx) + r.files.add(fileInfoIdx(conf, finalPath)) inc(r.pos) # skip #10 inc(r.line) outf.writeLine finalPath @@ -1152,7 +1157,7 @@ proc viewFile(rodfile: string) = if r.s[r.pos] == '\x0A': inc(r.pos) inc(r.line) - outf.write(w, " ", inclHash, "\n") + outf.write(w.int32, " ", inclHash, "\n") if r.s[r.pos] == ')': inc(r.pos) outf.write(")\n") of "DEPS": @@ -1162,7 +1167,7 @@ proc viewFile(rodfile: string) = let v = int32(decodeVInt(r.s, r.pos)) r.modDeps.add(r.files[v]) if r.s[r.pos] == ' ': inc(r.pos) - outf.write(" ", r.files[v]) + outf.write(" ", r.files[v].int32) outf.write("\n") of "INTERF", "COMPILERPROCS": inc r.pos, 2 @@ -1227,7 +1232,7 @@ proc viewFile(rodfile: string) = if r.s[r.pos] == ')': inc r.pos outf.write("<not supported by viewer>)\n") else: - internalError("invalid section: '" & section & + internalError(r.config, "invalid section: '" & section & "' at " & $r.line & " in " & r.filename) skipSection(r) if r.s[r.pos] == '\x0A': @@ -1236,4 +1241,4 @@ proc viewFile(rodfile: string) = outf.close when isMainModule: - viewFile(paramStr(1).addFileExt(rodExt)) + viewFile(paramStr(1).addFileExt(RodExt)) diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim index 0456e9349..66d7f63c2 100644 --- a/compiler/rodutils.nim +++ b/compiler/rodutils.nim @@ -8,18 +8,22 @@ # ## Serialization utilities for the compiler. -import strutils +import strutils, math proc c_snprintf(s: cstring; n:uint; frmt: cstring): cint {.importc: "snprintf", header: "<stdio.h>", nodecl, varargs.} proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string = - if f != f: + case classify(f) + of fcNaN: result = "NAN" - elif f == 0.0: + of fcNegZero: + result = "-0.0" & literalPostfix + of fcZero: result = "0.0" & literalPostfix - elif f == 0.5 * f: - if f > 0.0: result = "INF" - else: result = "-INF" + of fcInf: + result = "INF" + of fcNegInf: + result = "-INF" else: when defined(nimNoArrayToCstringConversion): result = newString(81) @@ -63,6 +67,8 @@ proc decodeStr*(s: cstring, pos: var int): string = const chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +{.push overflowChecks: off.} + # since negative numbers require a leading '-' they use up 1 byte. Thus we # subtract/add `vintDelta` here to save space for small negative numbers # which are common in ROD files: @@ -127,6 +133,8 @@ proc decodeVInt*(s: cstring, pos: var int): int = proc decodeVBiggestInt*(s: cstring, pos: var int): BiggestInt = decodeIntImpl() +{.pop.} + iterator decodeVIntArray*(s: cstring): int = var i = 0 while s[i] != '\0': diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index 9aed33ec9..4686baf2b 100644 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -13,7 +13,7 @@ import intsets, os, options, strutils, nversion, ast, astalgo, msgs, platform, - condsyms, ropes, idents, securehash, rodread, passes, idgen, + condsyms, ropes, idents, std / sha1, rodread, passes, idgen, rodutils, modulepaths from modulegraphs import ModuleGraph @@ -37,12 +37,13 @@ type files: TStringSeq origFile: string cache: IdentCache + config: ConfigRef PRodWriter = ref TRodWriter -proc getDefines(): string = +proc getDefines(conf: ConfigRef): string = result = "" - for d in definedSymbolNames(): + for d in definedSymbolNames(conf.symbols): if result.len != 0: add(result, " ") add(result, d) @@ -55,10 +56,12 @@ proc fileIdx(w: PRodWriter, filename: string): int = w.files[result] = filename template filename*(w: PRodWriter): string = - w.module.filename + toFilename(FileIndex w.module.position) -proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache): PRodWriter = +proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache; + config: ConfigRef): PRodWriter = new(result) + result.config = config result.sstack = @[] result.tstack = @[] initIiTable(result.index.tab) @@ -67,8 +70,8 @@ proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache): PRodWriter result.imports.r = "" result.hash = hash result.module = module - result.defines = getDefines() - result.options = options.gOptions + result.defines = getDefines(config) + result.options = config.options result.files = @[] result.inclDeps = "" result.modDeps = "" @@ -83,14 +86,14 @@ proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache): PRodWriter proc addModDep(w: PRodWriter, dep: string; info: TLineInfo) = if w.modDeps.len != 0: add(w.modDeps, ' ') - let resolved = dep.findModule(info.toFullPath) + let resolved = findModule(w.config, dep, info.toFullPath) encodeVInt(fileIdx(w, resolved), w.modDeps) const rodNL = "\x0A" proc addInclDep(w: PRodWriter, dep: string; info: TLineInfo) = - let resolved = dep.findModule(info.toFullPath) + let resolved = findModule(w.config, dep, info.toFullPath) encodeVInt(fileIdx(w, resolved), w.inclDeps) add(w.inclDeps, " ") encodeStr($secureHashFile(resolved), w.inclDeps) @@ -125,14 +128,14 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, result.add('?') encodeVInt(n.info.col, result) result.add(',') - encodeVInt(n.info.line, result) + encodeVInt(int n.info.line, result) result.add(',') encodeVInt(fileIdx(w, toFullPath(n.info)), result) elif fInfo.line != n.info.line: result.add('?') encodeVInt(n.info.col, result) result.add(',') - encodeVInt(n.info.line, result) + encodeVInt(int n.info.line, result) elif fInfo.col != n.info.col: result.add('?') encodeVInt(n.info.col, result) @@ -201,7 +204,7 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) = result.add("[]") return # 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(w.config, "encodeType: tyForward") # for the new rodfile viewer we use a preceding [ so that the data section # can easily be disambiguated: add(result, '[') @@ -303,7 +306,7 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) = result.add('?') if s.info.col != -1'i16: encodeVInt(s.info.col, result) result.add(',') - if s.info.line != -1'i16: encodeVInt(s.info.line, result) + if s.info.line != 0'u16: encodeVInt(int s.info.line, result) result.add(',') encodeVInt(fileIdx(w, toFullPath(s.info)), result) if s.owner != nil: @@ -390,9 +393,9 @@ proc symStack(w: PRodWriter): int = inc result elif iiTableGet(w.index.tab, s.id) == InvalidKey: var m = getModule(s) - if m == nil and s.kind != skPackage and sfGenSym notin s.flags: - internalError("symStack: module nil: " & s.name.s) - if s.kind == skPackage or {sfFromGeneric, sfGenSym} * s.flags != {} or m.id == w.module.id: + #if m == nil and s.kind != skPackage and sfGenSym notin s.flags: + # internalError("symStack: module nil: " & s.name.s & " " & $s.kind & " ID " & $s.id) + if m == nil or s.kind == skPackage or {sfFromGeneric, sfGenSym} * s.flags != {} or m.id == w.module.id: # put definition in here var L = w.data.len addToIndex(w.index, s.id, L) @@ -411,7 +414,7 @@ proc symStack(w: PRodWriter): int = add(w.compilerProcs, ' ') encodeVInt(s.id, w.compilerProcs) add(w.compilerProcs, rodNL) - if s.kind == skConverter or hasPattern(s): + if s.kind == skConverter or (s.ast != nil and hasPattern(s)): if w.converters.len != 0: add(w.converters, ' ') encodeVInt(s.id, w.converters) if s.kind == skMethod and sfDispatcher notin s.flags: @@ -454,7 +457,7 @@ proc processStacks(w: PRodWriter, finalPass: bool) = oldS = slen oldT = tlen if finalPass and (oldS != 0 or oldT != 0): - internalError("could not serialize some forwarded symbols/types") + internalError(w.config, "could not serialize some forwarded symbols/types") proc rawAddInterfaceSym(w: PRodWriter, s: PSym) = pushSym(w, s) @@ -476,8 +479,8 @@ proc addStmt(w: PRodWriter, n: PNode) = proc writeRod(w: PRodWriter) = processStacks(w, true) var f: File - if not open(f, completeGeneratedFilePath(changeFileExt( - w.filename.withPackageName, RodExt)), + if not open(f, completeGeneratedFilePath(w.config, changeFileExt( + withPackageName(w.config, w.filename), RodExt)), fmWrite): #echo "couldn't write rod file for: ", w.filename return @@ -506,12 +509,12 @@ proc writeRod(w: PRodWriter) = f.write(rodNL) var goptions = "GOPTIONS:" - encodeVInt(cast[int32](gGlobalOptions), goptions) + encodeVInt(cast[int32](w.config.globalOptions), goptions) f.write(goptions) f.write(rodNL) var cmd = "CMD:" - encodeVInt(cast[int32](gCmd), cmd) + encodeVInt(cast[int32](w.config.cmd), cmd) f.write(cmd) f.write(rodNL) @@ -587,17 +590,17 @@ proc process(c: PPassContext, n: PNode): PNode = of nkProcDef, nkFuncDef, nkIteratorDef, nkConverterDef, nkTemplateDef, nkMacroDef: let s = n.sons[namePos].sym - if s == nil: internalError(n.info, "rodwrite.process") + if s == nil: internalError(w.config, n.info, "rodwrite.process") if n.sons[bodyPos] == nil: - internalError(n.info, "rodwrite.process: body is nil") + internalError(w.config, n.info, "rodwrite.process: body is nil") if n.sons[bodyPos].kind != nkEmpty or s.magic != mNone or sfForward notin s.flags: addInterfaceSym(w, s) of nkMethodDef: let s = n.sons[namePos].sym - if s == nil: internalError(n.info, "rodwrite.process") + if s == nil: internalError(w.config, n.info, "rodwrite.process") if n.sons[bodyPos] == nil: - internalError(n.info, "rodwrite.process: body is nil") + internalError(w.config, n.info, "rodwrite.process: body is nil") if n.sons[bodyPos].kind != nkEmpty or s.magic != mNone or sfForward notin s.flags: pushSym(w, s) @@ -612,7 +615,7 @@ proc process(c: PPassContext, n: PNode): PNode = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue - if a.sons[0].kind != nkSym: internalError(a.info, "rodwrite.process") + if a.sons[0].kind != nkSym: internalError(w.config, a.info, "rodwrite.process") var s = a.sons[0].sym addInterfaceSym(w, s) # this takes care of enum fields too @@ -627,22 +630,22 @@ proc process(c: PPassContext, n: PNode): PNode = # end of nkImportStmt: for i in countup(0, sonsLen(n) - 1): - addModDep(w, getModuleName(n.sons[i]), n.info) + addModDep(w, getModuleName(w.config, n.sons[i]), n.info) addStmt(w, n) of nkFromStmt, nkImportExceptStmt: - addModDep(w, getModuleName(n.sons[0]), n.info) + addModDep(w, getModuleName(w.config, n.sons[0]), n.info) addStmt(w, n) of nkIncludeStmt: for i in countup(0, sonsLen(n) - 1): - addInclDep(w, getModuleName(n.sons[i]), n.info) + addInclDep(w, getModuleName(w.config, n.sons[i]), n.info) of nkPragma: addStmt(w, n) else: discard proc myOpen(g: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = - if module.id < 0: internalError("rodwrite: module ID not set") - var w = newRodWriter(module.fileIdx.getHash, module, cache) + if module.id < 0: internalError(g.config, "rodwrite: module ID not set") + var w = newRodWriter(rodread.getHash FileIndex module.position, module, cache, g.config) rawAddInterfaceSym(w, module) result = w @@ -650,7 +653,7 @@ proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode = result = process(c, n) var w = PRodWriter(c) writeRod(w) - idgen.saveMaxIds(options.gProjectPath / options.gProjectName) + idgen.saveMaxIds(graph.config, graph.config.projectPath / graph.config.projectName) const rodwritePass* = makePass(open = myOpen, close = myClose, process = process) diff --git a/compiler/ropes.nim b/compiler/ropes.nim index 358ce8a53..05d5e840c 100644 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -188,11 +188,11 @@ iterator leaves*(r: Rope): string = var stack = @[r] while stack.len > 0: var it = stack.pop - while isNil(it.data): + while it.left != nil: + assert it.right != nil stack.add(it.right) it = it.left assert(it != nil) - assert(it.data != nil) yield it.data iterator items*(r: Rope): char = @@ -251,7 +251,7 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope = while true: j = j * 10 + ord(frmt[i]) - ord('0') inc(i) - if frmt[i] notin {'0'..'9'}: break + if i >= frmt.len or frmt[i] notin {'0'..'9'}: break num = j if j > high(args) + 1: errorHandler(rInvalidFormatStr, $(j)) diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index 8eb76457c..30e5e4803 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -13,7 +13,7 @@ import ast, modules, idents, passes, passaux, condsyms, options, nimconf, sem, semdata, llstream, vm, vmdef, commands, msgs, - os, times, osproc, wordrecg, strtabs, modulegraphs + os, times, osproc, wordrecg, strtabs, modulegraphs, configuration # we support 'cmpIgnoreStyle' natively for efficiency: from strutils import cmpIgnoreStyle, contains @@ -26,11 +26,12 @@ proc listDirs(a: VmArgs, filter: set[PathComponent]) = setResult(a, result) proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; - config: ConfigRef = nil): PEvalContext = + graph: ModuleGraph): PEvalContext = # For Nimble we need to export 'setupVM'. - result = newCtx(module, cache) + result = newCtx(module, cache, graph) result.mode = emRepl registerAdditionalOps(result) + let conf = graph.config # captured vars: var errorMsg: string @@ -70,11 +71,16 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; setResult(a, os.getCurrentDir()) cbos moveFile: os.moveFile(getString(a, 0), getString(a, 1)) + cbos moveDir: + os.moveDir(getString(a, 0), getString(a, 1)) cbos copyFile: os.copyFile(getString(a, 0), getString(a, 1)) + cbos copyDir: + os.copyDir(getString(a, 0), getString(a, 1)) cbos getLastModificationTime: - # depends on Time's implementation! - setResult(a, int64(getLastModificationTime(getString(a, 0)))) + setResult(a, getLastModificationTime(getString(a, 0)).toUnix) + cbos findExe: + setResult(a, os.findExe(getString(a, 0))) cbos rawExec: setResult(a, osproc.execCmd getString(a, 0)) @@ -83,6 +89,8 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; setResult(a, os.getEnv(a.getString 0, a.getString 1)) cbconf existsEnv: setResult(a, os.existsEnv(a.getString 0)) + cbconf putEnv: + os.putEnv(a.getString 0, a.getString 1) cbconf dirExists: setResult(a, os.dirExists(a.getString 0)) cbconf fileExists: @@ -91,13 +99,13 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; cbconf thisDir: setResult(a, vthisDir) cbconf put: - options.setConfigVar(getString(a, 0), getString(a, 1)) + options.setConfigVar(conf, getString(a, 0), getString(a, 1)) cbconf get: - setResult(a, options.getConfigVar(a.getString 0)) + setResult(a, options.getConfigVar(conf, a.getString 0)) cbconf exists: - setResult(a, options.existsConfigVar(a.getString 0)) + setResult(a, options.existsConfigVar(conf, a.getString 0)) cbconf nimcacheDir: - setResult(a, options.getNimcacheDir()) + setResult(a, options.getNimcacheDir(conf)) cbconf paramStr: setResult(a, os.paramStr(int a.getInt 0)) cbconf paramCount: @@ -107,67 +115,66 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; cbconf cmpIgnoreCase: setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1)) cbconf setCommand: - options.command = a.getString 0 + conf.command = a.getString 0 let arg = a.getString 1 if arg.len > 0: - gProjectName = arg + conf.projectName = arg let path = - if gProjectName.isAbsolute: gProjectName - else: gProjectPath / gProjectName + if conf.projectName.isAbsolute: conf.projectName + else: conf.projectPath / conf.projectName try: - gProjectFull = canonicalizePath(path) + conf.projectFull = canonicalizePath(conf, path) except OSError: - gProjectFull = path + conf.projectFull = path cbconf getCommand: - setResult(a, options.command) + setResult(a, conf.command) cbconf switch: - processSwitch(a.getString 0, a.getString 1, passPP, module.info) + processSwitch(a.getString 0, a.getString 1, passPP, module.info, conf) cbconf hintImpl: processSpecificNote(a.getString 0, wHint, passPP, module.info, - a.getString 1) + a.getString 1, conf) cbconf warningImpl: processSpecificNote(a.getString 0, wWarning, passPP, module.info, - a.getString 1) + a.getString 1, conf) cbconf patchFile: let key = a.getString(0) & "_" & a.getString(1) var val = a.getString(2).addFileExt(NimExt) if {'$', '~'} in val: - val = pathSubs(val, vthisDir) + val = pathSubs(conf, val, vthisDir) elif not isAbsolute(val): val = vthisDir / val - gModuleOverrides[key] = val + conf.moduleOverrides[key] = val cbconf selfExe: setResult(a, os.getAppFilename()) cbconf cppDefine: - if config != nil: - options.cppDefine(config, a.getString(0)) + options.cppDefine(conf, a.getString(0)) proc runNimScript*(cache: IdentCache; scriptName: string; - freshDefines=true; config: ConfigRef=nil) = - rawMessage(hintConf, scriptName) + freshDefines=true; conf: ConfigRef) = + rawMessage(conf, hintConf, scriptName) passes.gIncludeFile = includeModule passes.gImportModule = importModule - let graph = newModuleGraph(config) - if freshDefines: initDefines() + let graph = newModuleGraph(conf) + if freshDefines: initDefines(conf.symbols) - defineSymbol("nimscript") - defineSymbol("nimconfig") + defineSymbol(conf.symbols, "nimscript") + defineSymbol(conf.symbols, "nimconfig") registerPass(semPass) registerPass(evalPass) - searchPaths.add(options.libpath) + conf.searchPaths.add(conf.libpath) var m = graph.makeModule(scriptName) incl(m.flags, sfMainModule) - vm.globalCtx = setupVM(m, cache, scriptName, config) + vm.globalCtx = setupVM(m, cache, scriptName, graph) graph.compileSystemModule(cache) discard graph.processModule(m, llStreamOpen(scriptName, fmRead), nil, cache) # ensure we load 'system.nim' again for the real non-config stuff! - resetSystemArtifacts() + resetSystemArtifacts(graph) vm.globalCtx = nil # do not remove the defined symbols #initDefines() - undefSymbol("nimscript") - undefSymbol("nimconfig") + undefSymbol(conf.symbols, "nimscript") + undefSymbol(conf.symbols, "nimconfig") diff --git a/compiler/sem.nim b/compiler/sem.nim index 1098e9961..d56355f14 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -16,7 +16,7 @@ import procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch, intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting, evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity, - semparallel, lowerings, pluginsupport, plugins.active + semparallel, lowerings, pluginsupport, plugins.active, rod, configuration from modulegraphs import ModuleGraph @@ -33,11 +33,12 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode proc semProcBody(c: PContext, n: PNode): PNode proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode -proc changeType(n: PNode, newType: PType, check: bool) +proc changeType(c: PContext; n: PNode, newType: PType, check: bool) proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode proc semTypeNode(c: PContext, n: PNode, prev: PType): PType proc semStmt(c: PContext, n: PNode): PNode +proc semOpAux(c: PContext, n: PNode) proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) proc addParams(c: PContext, n: PNode, kind: TSymKind) proc maybeAddResult(c: PContext, s: PSym, n: PNode) @@ -64,14 +65,14 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode; # templates perform some quick check whether the cursor is actually in # the generic or template. when defined(nimsuggest): - if gCmd == cmdIdeTools and requiresCheck: + if c.config.cmd == cmdIdeTools and requiresCheck: #if optIdeDebug in gGlobalOptions: # echo "passing to safeSemExpr: ", renderTree(n) discard safeSemExpr(c, n) proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = if arg.typ.isNil: - localError(arg.info, errExprXHasNoType, + localError(c.config, arg.info, "expression has no type: " & renderTree(arg, {renderNoComments})) # error correction: result = copyTree(arg) @@ -79,14 +80,14 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = else: result = indexTypesMatch(c, formal, arg.typ, arg) if result == nil: - typeMismatch(info, formal, arg.typ) + typeMismatch(c.config, info, formal, arg.typ) # error correction: result = copyTree(arg) result.typ = formal else: let x = result.skipConv - if x.kind == nkPar and formal.kind != tyExpr: - changeType(x, formal, check=true) + if x.kind in {nkPar, nkTupleConstr} and formal.kind != tyExpr: + changeType(c, x, formal, check=true) else: result = skipHiddenSubConv(result) #result.typ = takeType(formal, arg.typ) @@ -102,8 +103,8 @@ proc commonType*(x, y: PType): PType = # if expressions, etc.: if x == nil: return x if y == nil: return y - var a = skipTypes(x, {tyGenericInst, tyAlias}) - var b = skipTypes(y, {tyGenericInst, tyAlias}) + var a = skipTypes(x, {tyGenericInst, tyAlias, tySink}) + var b = skipTypes(y, {tyGenericInst, tyAlias, tySink}) result = x if a.kind in {tyExpr, tyNil}: result = y elif b.kind in {tyExpr, tyNil}: result = x @@ -153,14 +154,17 @@ proc commonType*(x, y: PType): PType = if a.kind in {tyRef, tyPtr}: k = a.kind if b.kind != a.kind: return x - a = a.lastSon - b = b.lastSon + # bug #7601, array construction of ptr generic + a = a.lastSon.skipTypes({tyGenericInst}) + b = b.lastSon.skipTypes({tyGenericInst}) if a.kind == tyObject and b.kind == tyObject: result = commonSuperclass(a, b) # this will trigger an error later: if result.isNil or result == a: return x if result == b: return y - if k != tyNone: + # bug #7906, tyRef/tyPtr + tyGenericInst of ref/ptr object -> + # ill-formed AST, no need for additional tyRef/tyPtr + if k != tyNone and x.kind != tyGenericInst: let r = result result = newType(k, r.owner) result.addSonSkipIntLit(r) @@ -179,7 +183,7 @@ proc commonType*(x: PType, y: PNode): PType = commonType(x, y.typ) proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym = - result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info) + result = newSym(kind, considerQuotedIdent(c.config, n), getCurrOwner(c), n.info) when defined(nimsuggest): suggestDecl(c, n, result) @@ -191,7 +195,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = # and sfGenSym in n.sym.flags: result = n.sym if result.kind != kind: - localError(n.info, "cannot use symbol of kind '" & + localError(c.config, n.info, "cannot use symbol of kind '" & $result.kind & "' as a '" & $kind & "'") if sfGenSym in result.flags and result.kind notin {skTemplate, skMacro, skParam}: # declarative context, so produce a fresh gensym: @@ -203,7 +207,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = # template; we must fix it here: see #909 result.owner = getCurrOwner(c) else: - result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info) + result = newSym(kind, considerQuotedIdent(c.config, n), getCurrOwner(c), n.info) #if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule: # incl(result.flags, sfGlobal) when defined(nimsuggest): @@ -215,15 +219,20 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, allowed: TSymFlags): PSym -proc typeAllowedCheck(info: TLineInfo; typ: PType; kind: TSymKind) = - let t = typeAllowed(typ, kind) +proc typeAllowedCheck(conf: ConfigRef; info: TLineInfo; typ: PType; kind: TSymKind; + flags: TTypeAllowedFlags = {}) = + let t = typeAllowed(typ, kind, flags) if t != nil: - if t == typ: localError(info, "invalid type: '" & typeToString(typ) & "'") - else: localError(info, "invalid type: '" & typeToString(t) & - "' in this context: '" & typeToString(typ) & "'") + if t == typ: + localError(conf, info, "invalid type: '" & typeToString(typ) & + "' for " & substr($kind, 2).toLowerAscii) + else: + localError(conf, info, "invalid type: '" & typeToString(t) & + "' in this context: '" & typeToString(typ) & + "' for " & substr($kind, 2).toLowerAscii) proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} = - typeAllowedCheck(typ.n.info, typ, skProc) + typeAllowedCheck(c.config, typ.n.info, typ, skProc) proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode @@ -276,10 +285,10 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode = result = evaluated let expectedType = eOrig.typ.skipTypes({tyStatic}) if hasCycle(result): - globalError(eOrig.info, "the resulting AST is cyclic and cannot be processed further") + globalError(c.config, eOrig.info, "the resulting AST is cyclic and cannot be processed further") result = errorNode(c, eOrig) else: - semmacrosanity.annotateType(result, expectedType) + semmacrosanity.annotateType(result, expectedType, c.config) else: result = semExprWithType(c, evaluated) #result = fitNode(c, e.typ, result) inlined with special case: @@ -296,18 +305,18 @@ proc tryConstExpr(c: PContext, n: PNode): PNode = var e = semExprWithType(c, n) if e == nil: return - result = getConstExpr(c.module, e) + result = getConstExpr(c.module, e, c.graph) if result != nil: return - let oldErrorCount = msgs.gErrorCounter - let oldErrorMax = msgs.gErrorMax + let oldErrorCount = c.config.errorCounter + let oldErrorMax = c.config.errorMax let oldErrorOutputs = errorOutputs errorOutputs = {} - msgs.gErrorMax = high(int) + c.config.errorMax = high(int) try: - result = evalConstExpr(c.module, c.cache, e) + result = evalConstExpr(c.module, c.cache, c.graph, e) if result == nil or result.kind == nkEmpty: result = nil else: @@ -316,26 +325,29 @@ proc tryConstExpr(c: PContext, n: PNode): PNode = except ERecoverableError: result = nil - msgs.gErrorCounter = oldErrorCount - msgs.gErrorMax = oldErrorMax + c.config.errorCounter = oldErrorCount + c.config.errorMax = oldErrorMax errorOutputs = oldErrorOutputs +const + errConstExprExpected = "constant expression expected" + proc semConstExpr(c: PContext, n: PNode): PNode = var e = semExprWithType(c, n) if e == nil: - localError(n.info, errConstExprExpected) + localError(c.config, n.info, errConstExprExpected) return n - result = getConstExpr(c.module, e) + result = getConstExpr(c.module, e, c.graph) if result == nil: #if e.kind == nkEmpty: globalError(n.info, errConstExprExpected) - result = evalConstExpr(c.module, c.cache, e) + result = evalConstExpr(c.module, c.cache, c.graph, e) if result == nil or result.kind == nkEmpty: if e.info != n.info: pushInfoContext(n.info) - localError(e.info, errConstExprExpected) + localError(c.config, e.info, errConstExprExpected) popInfoContext() else: - localError(e.info, errConstExprExpected) + localError(c.config, e.info, errConstExprExpected) # error correction: result = e else: @@ -350,7 +362,7 @@ proc semExprFlagDispatched(c: PContext, n: PNode, flags: TExprFlags): PNode = else: result = semExprWithType(c, n, flags) if efPreferStatic in flags: - var evaluated = getConstExpr(c.module, result) + var evaluated = getConstExpr(c.module, result, c.graph) if evaluated != nil: return evaluated evaluated = evalAtCompileTime(c, result) if evaluated != nil: return evaluated @@ -372,8 +384,8 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode, ## reassigned, and binding the unbound identifiers that the macro output ## contains. inc(evalTemplateCounter) - if evalTemplateCounter > 100: - globalError(s.info, errTemplateInstantiationTooNested) + if evalTemplateCounter > evalTemplateLimit: + globalError(c.config, s.info, "template instantiation too nested") c.friendModules.add(s.owner.getModule) result = macroResult @@ -415,43 +427,46 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode, dec(evalTemplateCounter) discard c.friendModules.pop() +const + errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters" + proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, flags: TExprFlags = {}): PNode = pushInfoContext(nOrig.info) - markUsed(n.info, sym, c.graph.usageSym) + markUsed(c.config, n.info, sym, c.graph.usageSym) styleCheckUse(n.info, sym) if sym == c.p.owner: - globalError(n.info, errRecursiveDependencyX, sym.name.s) + globalError(c.config, n.info, "recursive dependency: '$1'" % sym.name.s) let genericParams = if sfImmediate in sym.flags: 0 else: sym.ast[genericParamsPos].len let suppliedParams = max(n.safeLen - 1, 0) if suppliedParams < genericParams: - globalError(n.info, errMissingGenericParamsForTemplate, n.renderTree) + globalError(c.config, n.info, errMissingGenericParamsForTemplate % n.renderTree) #if c.evalContext == nil: # c.evalContext = c.createEvalContext(emStatic) - result = evalMacroCall(c.module, c.cache, n, nOrig, sym) + result = evalMacroCall(c.module, c.cache, c.graph, n, nOrig, sym) if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, sym, flags) result = wrapInComesFrom(nOrig.info, sym, result) popInfoContext() proc forceBool(c: PContext, n: PNode): PNode = - result = fitNode(c, getSysType(tyBool), n, n.info) + result = fitNode(c, getSysType(c.graph, n.info, tyBool), n, n.info) if result == nil: result = n proc semConstBoolExpr(c: PContext, n: PNode): PNode = let nn = semExprWithType(c, n) - result = fitNode(c, getSysType(tyBool), nn, nn.info) + result = fitNode(c, getSysType(c.graph, n.info, tyBool), nn, nn.info) if result == nil: - localError(n.info, errConstExprExpected) + localError(c.config, n.info, errConstExprExpected) return nn - result = getConstExpr(c.module, result) + result = getConstExpr(c.module, result, c.graph) if result == nil: - localError(n.info, errConstExprExpected) + localError(c.config, n.info, errConstExprExpected) result = nn proc semGenericStmt(c: PContext, n: PNode): PNode @@ -464,14 +479,14 @@ proc addCodeForGenerics(c: PContext, n: PNode) = var prc = c.generics[i].inst.sym if prc.kind in {skProc, skFunc, skMethod, skConverter} and prc.magic == mNone: if prc.ast == nil or prc.ast.sons[bodyPos] == nil: - internalError(prc.info, "no code for " & prc.name.s) + internalError(c.config, prc.info, "no code for " & prc.name.s) else: addSon(n, prc.ast) c.lastGenericIdx = c.generics.len proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = var c = newContext(graph, module, cache) - if c.p != nil: internalError(module.info, "sem.myOpen") + if c.p != nil: internalError(graph.config, module.info, "sem.myOpen") c.semConstExpr = semConstExpr c.semExpr = semExpr c.semTryExpr = tryExpr @@ -489,14 +504,14 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = c.importTable = openScope(c) c.importTable.addSym(module) # a module knows itself if sfSystemModule in module.flags: - magicsys.systemModule = module # set global variable! + graph.systemModule = module c.topLevelScope = openScope(c) # don't be verbose unless the module belongs to the main package: if module.owner.id == gMainPackageId: - gNotes = gMainPackageNotes + graph.config.notes = graph.config.mainPackageNotes else: - if gMainPackageNotes == {}: gMainPackageNotes = gNotes - gNotes = ForeignPackageNotes + if graph.config.mainPackageNotes == {}: graph.config.mainPackageNotes = graph.config.notes + graph.config.notes = graph.config.foreignPackageNotes result = c proc myOpenCached(graph: ModuleGraph; module: PSym; rd: PRodReader): PPassContext = @@ -505,30 +520,30 @@ proc myOpenCached(graph: ModuleGraph; module: PSym; rd: PRodReader): PPassContex proc replayMethodDefs(graph: ModuleGraph; rd: PRodReader) = for m in items(rd.methods): methodDef(graph, m, true) -proc isImportSystemStmt(n: PNode): bool = - if magicsys.systemModule == nil: return false +proc isImportSystemStmt(g: ModuleGraph; n: PNode): bool = + if g.systemModule == nil: return false case n.kind of nkImportStmt: for x in n: if x.kind == nkIdent: - let f = checkModuleName(x, false) - if f == magicsys.systemModule.info.fileIndex: + let f = checkModuleName(g.config, x, false) + if f == g.systemModule.info.fileIndex: return true of nkImportExceptStmt, nkFromStmt: if n[0].kind == nkIdent: - let f = checkModuleName(n[0], false) - if f == magicsys.systemModule.info.fileIndex: + let f = checkModuleName(g.config, n[0], false) + if f == g.systemModule.info.fileIndex: return true else: discard proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = if n.kind == nkDefer: - localError(n.info, "defer statement not supported at top level") - if c.topStmts == 0 and not isImportSystemStmt(n): + localError(c.config, n.info, "defer statement not supported at top level") + if c.topStmts == 0 and not isImportSystemStmt(c.graph, n): if sfSystemModule notin c.module.flags and n.kind notin {nkEmpty, nkCommentStmt}: - c.importTable.addSym magicsys.systemModule # import the "System" identifier - importAllSymbols(c, magicsys.systemModule) + c.importTable.addSym c.graph.systemModule # import the "System" identifier + importAllSymbols(c, c.graph.systemModule) inc c.topStmts else: inc c.topStmts @@ -550,11 +565,11 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = if result.kind != nkEmpty: addSon(a, result) result = a result = hloStmt(c, result) - if gCmd == cmdInteractive and not isEmptyType(result.typ): + if c.config.cmd == cmdInteractive and not isEmptyType(result.typ): result = buildEchoStmt(c, result) - if gCmd == cmdIdeTools: + if c.config.cmd == cmdIdeTools: appendToModule(c.module, result) - result = transformStmt(c.module, result) + result = transformStmt(c.graph, c.module, result) proc recoverContext(c: PContext) = # clean up in case of a semantic error: We clean up the stacks, etc. This is @@ -567,7 +582,7 @@ proc recoverContext(c: PContext) = proc myProcess(context: PPassContext, n: PNode): PNode = var c = PContext(context) # no need for an expensive 'try' if we stop after the first error anyway: - if msgs.gErrorMax <= 1: + if c.config.errorMax <= 1: result = semStmtAndGenerateGenerics(c, n) else: let oldContextLen = msgs.getInfoContextLen() @@ -583,15 +598,16 @@ proc myProcess(context: PPassContext, n: PNode): PNode = result = nil else: result = ast.emptyNode - #if gCmd == cmdIdeTools: findSuggest(c, n) + #if c.config.cmd == cmdIdeTools: findSuggest(c, n) + rod.storeNode(c.module, result) proc testExamples(c: PContext) = let inp = toFullPath(c.module.info) let outp = inp.changeFileExt"" & "_examples.nim" renderModule(c.runnableExamples, inp, outp) - let backend = if isDefined("js"): "js" - elif isDefined("cpp"): "cpp" - elif isDefined("objc"): "objc" + let backend = if isDefined(c.config, "js"): "js" + elif isDefined(c.config, "cpp"): "cpp" + elif isDefined(c.config, "objc"): "objc" else: "c" if os.execShellCmd("nim " & backend & " -r " & outp) != 0: quit "[Examples] failed" @@ -599,13 +615,13 @@ proc testExamples(c: PContext) = proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = var c = PContext(context) - if gCmd == cmdIdeTools and not c.suggestionsMade: + if c.config.cmd == cmdIdeTools and not c.suggestionsMade: suggestSentinel(c) closeScope(c) # close module's scope rawCloseScope(c) # imported symbols; don't check for unused ones! result = newNode(nkStmtList) if n != nil: - internalError(n.info, "n is not nil") #result := n; + internalError(c.config, n.info, "n is not nil") #result := n; addCodeForGenerics(c, result) if c.module.ast != nil: result.add(c.module.ast) @@ -613,6 +629,8 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = replayMethodDefs(graph, c.rd) popOwner(c) popProcCon(c) + storeRemaining(c.module) if c.runnableExamples != nil: testExamples(c) -const semPass* = makePass(myOpen, myOpenCached, myProcess, myClose) +const semPass* = makePass(myOpen, myOpenCached, myProcess, myClose, + isFrontend = true) diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index 67af6ade7..f0fde195c 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -33,7 +33,7 @@ proc at(a, i: PNode, elemType: PType): PNode = proc liftBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) = for i in 0 ..< t.len: - let lit = lowerings.newIntLit(i) + let lit = lowerings.newIntLit(c.c.graph, x.info, 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 = @@ -66,15 +66,15 @@ proc liftBodyObj(c: var TLiftCtx; n, body, x, y: 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") + localError(c.c.config, 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) + illFormedAstLocal(n, c.c.config) proc genAddr(c: PContext; x: PNode): PNode = if x.kind == nkHiddenDeref: - checkSonsLen(x, 1) + checkSonsLen(x, 1, c.config) result = x.sons[0] else: result = newNodeIT(nkHiddenAddr, x.info, makeVarType(c, x.typ)) @@ -82,7 +82,7 @@ proc genAddr(c: PContext; x: PNode): PNode = proc newAsgnCall(c: PContext; op: PSym; x, y: PNode): PNode = if sfError in op.flags: - localError(x.info, errWrongSymbolX, op.name.s) + localError(c.config, x.info, "usage of '$1' is a user-defined error" % op.name.s) result = newNodeI(nkCall, x.info) result.add newSymNode(op) result.add genAddr(c, x) @@ -101,7 +101,7 @@ proc newOpCall(op: PSym; x: PNode): PNode = proc destructorCall(c: PContext; op: PSym; x: PNode): PNode = result = newNodeIT(nkCall, x.info, op.typ.sons[0]) result.add(newSymNode(op)) - if newDestructors: + if destructor in c.features: result.add genAddr(c, x) else: result.add x @@ -124,7 +124,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode; op = field if op == nil: op = liftBody(c.c, t, c.kind, c.info) - markUsed(c.info, op, c.c.graph.usageSym) + markUsed(c.c.config, c.info, op, c.c.graph.usageSym) styleCheckUse(c.info, op) body.add newAsgnCall(c.c, op, x, y) result = true @@ -134,7 +134,7 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = of attachedDestructor: let op = t.destructor if op != nil: - markUsed(c.info, op, c.c.graph.usageSym) + markUsed(c.c.config, c.info, op, c.c.graph.usageSym) styleCheckUse(c.info, op) body.add destructorCall(c.c, op, x) result = true @@ -145,7 +145,7 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = of attachedDeepCopy: let op = t.deepCopy if op != nil: - markUsed(c.info, op, c.c.graph.usageSym) + markUsed(c.c.config, c.info, op, c.c.graph.usageSym) styleCheckUse(c.info, op) body.add newDeepCopyCall(op, x, y) result = true @@ -163,37 +163,37 @@ proc addVar(father, v, value: PNode) = 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) + temp.typ = getSysType(c.c.graph, body.info, tyInt) incl(temp.flags, sfFromGeneric) var v = newNodeI(nkVarSection, c.info) result = newSymNode(temp) - v.addVar(result, lowerings.newIntLit(first)) + v.addVar(result, lowerings.newIntLit(c.c.graph, body.info, first)) body.add v -proc genBuiltin(magic: TMagic; name: string; i: PNode): PNode = +proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode = result = newNodeI(nkCall, i.info) - result.add createMagic(name, magic).newSymNode + result.add createMagic(g, 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) + let cmp = genBuiltin(c.c.graph, mLeI, "<=", i) + cmp.add genHigh(c.c.graph, dest) + cmp.typ = getSysType(c.c.graph, c.info, 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) +proc addIncStmt(c: var TLiftCtx; body, i: PNode) = + let incCall = genBuiltin(c.c.graph, mInc, "inc", i) + incCall.add lowerings.newIntLit(c.c.graph, c.info, 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 = genBuiltin(c.graph, mNewSeq, "newSeq", x) + let lenCall = genBuiltin(c.graph, mLengthSeq, "len", y) + lenCall.typ = getSysType(c.graph, x.info, tyInt) result.add lenCall proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = @@ -212,7 +212,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = let elemType = t.lastSon liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType), y.at(i, elemType)) - addIncStmt(whileLoop.sons[1], i) + addIncStmt(c, whileLoop.sons[1], i) body.add whileLoop else: defaultOp(c, t, body, x, y) @@ -231,20 +231,20 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = # have to go through some indirection; we delegate this to the codegen: let call = newNodeI(nkCall, c.info, 2) call.typ = t - call.sons[0] = newSymNode(createMagic("deepCopy", mDeepCopy)) + call.sons[0] = newSymNode(createMagic(c.c.graph, "deepCopy", mDeepCopy)) call.sons[1] = y body.add newAsgnStmt(x, call) of tyVarargs, tyOpenArray: - localError(c.info, errGenerated, "cannot copy openArray") + localError(c.c.config, c.info, "cannot copy openArray") of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything, tyGenericParam, tyGenericBody, tyNil, tyExpr, tyStmt, tyTypeDesc, tyGenericInvocation, tyForward: - internalError(c.info, "assignment requested for type: " & typeToString(t)) + internalError(c.c.config, c.info, "assignment requested for type: " & typeToString(t)) of tyOrdinal, tyRange, tyInferred, - tyGenericInst, tyStatic, tyVar, tyAlias: + tyGenericInst, tyStatic, tyVar, tyLent, tyAlias, tySink: liftBodyAux(c, lastSon(t), body, x, y) - of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("liftBodyAux") + of tyUnused, tyOptAsRef: internalError(c.c.config, "liftBodyAux") proc newProcType(info: TLineInfo; owner: PSym): PType = result = newType(tyProc, owner) @@ -306,7 +306,7 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp; proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym = - let t = typ.skipTypes({tyGenericInst, tyVar, tyAlias}) + let t = typ.skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink}) result = t.assignment if result.isNil: result = liftBody(c, t, attachedAsgn, info) @@ -319,7 +319,7 @@ proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) = ## In the semantic pass this is called in strategic places ## to ensure we lift assignment, destructors and moves properly. ## The later 'destroyer' pass depends on it. - if not newDestructors or not hasDestructor(typ): return + if destructor notin c.features or not hasDestructor(typ): return when false: # do not produce wrong liftings while we're still instantiating generics: # now disabled; breaks topttree.nim! diff --git a/compiler/semcall.nim b/compiler/semcall.nim index a51b9afe3..df99d6c24 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -59,7 +59,8 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, filter: TSymKinds, best, alt: var TCandidate, errors: var CandidateErrors, - diagnosticsFlag = false) = + diagnosticsFlag: bool, + errorsEnabled: bool) = var o: TOverloadIter var sym = initOverloadIter(o, c, headSymbol) var scope = o.lastOverloadScope @@ -68,6 +69,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, # This can occur in cases like 'init(a, 1, (var b = new(Type2); b))' let counterInitial = c.currentScope.symbols.counter var syms: seq[tuple[s: PSym, scope: int]] + var noSyms = true var nextSymIndex = 0 while sym != nil: if sym.kind in filter: @@ -102,17 +104,19 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, var cmp = cmpCandidates(best, z) 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 - elif errors != nil or z.diagnostics != nil: + elif errorsEnabled or z.diagnosticsEnabled: errors.safeAdd(CandidateError( sym: sym, unmatchedVarParam: int z.mutabilityProblem, + firstMismatch: z.firstMismatch, diagnostics: z.diagnostics)) else: # Symbol table has been modified. Restart and pre-calculate all syms # before any further candidate init and compare. SLOW, but rare case. syms = initCandidateSymbols(c, headSymbol, initialBinding, filter, best, alt, o, diagnosticsFlag) - if syms == nil: + noSyms = false + if noSyms: sym = nextOverloadIter(o, c, headSymbol) scope = o.lastOverloadScope elif nextSymIndex < syms.len: @@ -123,6 +127,20 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, else: break +proc effectProblem(f, a: PType; result: var string) = + if f.kind == tyProc and a.kind == tyProc: + if tfThread in f.flags and tfThread notin a.flags: + result.add "\n This expression is not GC-safe. Annotate the " & + "proc with {.gcsafe.} to get extended error information." + elif tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags: + result.add "\n This expression can have side effects. Annotate the " & + "proc with {.noSideEffect.} to get extended error information." + +proc renderNotLValue(n: PNode): string = + result = $n + if n.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv} and n.len == 2: + result = typeToString(n.typ.skipTypes(abstractVar)) & "(" & result & ")" + proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): (TPreferedDesc, string) = var prefer = preferName @@ -154,32 +172,61 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): else: add(candidates, err.sym.getProcHeader(prefer)) add(candidates, "\n") + if err.firstMismatch != 0 and n.len > 1: + let cond = n.len > 2 + if cond: + candidates.add(" first type mismatch at position: " & $err.firstMismatch & + "\n required type: ") + var wanted, got: PType = nil + if err.firstMismatch < err.sym.typ.len: + wanted = err.sym.typ.sons[err.firstMismatch] + if cond: candidates.add typeToString(wanted) + else: + if cond: candidates.add "none" + if err.firstMismatch < n.len: + if cond: + candidates.add "\n but expression '" + candidates.add renderTree(n[err.firstMismatch]) + candidates.add "' is of type: " + got = n[err.firstMismatch].typ + if cond: candidates.add typeToString(got) + if wanted != nil and got != nil: + effectProblem(wanted, got, candidates) + if cond: candidates.add "\n" if err.unmatchedVarParam != 0 and err.unmatchedVarParam < n.len: - add(candidates, "for a 'var' type a variable needs to be passed, but '" & - renderTree(n[err.unmatchedVarParam]) & "' is immutable\n") + candidates.add(" for a 'var' type a variable needs to be passed, but '" & + renderNotLValue(n[err.unmatchedVarParam]) & + "' is immutable\n") for diag in err.diagnostics: - add(candidates, diag & "\n") + candidates.add(diag & "\n") result = (prefer, candidates) +const + errTypeMismatch = "type mismatch: got <" + errButExpected = "but expected one of: " + errUndeclaredField = "undeclared field: '$1'" + errUndeclaredRoutine = "attempting to call undeclared routine: '$1'" + errAmbiguousCallXYZ = "ambiguous call; both $1 and $2 match for: $3" + proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = # Gives a detailed error message; this is separated from semOverloadedCall, # as semOverlodedCall is already pretty slow (and we need this information # only in case of an error). if errorOutputs == {}: # fail fast: - globalError(n.info, errTypeMismatch, "") - if errors.isNil or errors.len == 0: - localError(n.info, errExprXCannotBeCalled, n[0].renderTree) + globalError(c.config, n.info, "type mismatch") + if errors.len == 0: + localError(c.config, n.info, "expression '$1' cannot be called" % n[0].renderTree) return let (prefer, candidates) = presentFailedCandidates(c, n, errors) - var result = msgKindToString(errTypeMismatch) + var result = errTypeMismatch add(result, describeArgs(c, n, 1, prefer)) - add(result, ')') + add(result, '>') if candidates != "": - add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates) - localError(n.info, errGenerated, result & "\nexpression: " & $n) + add(result, "\n" & errButExpected & "\n" & candidates) + localError(c.config, n.info, result & "\nexpression: " & $n) proc bracketNotFoundError(c: PContext; n: PNode) = var errors: CandidateErrors = @[] @@ -189,22 +236,25 @@ proc bracketNotFoundError(c: PContext; n: PNode) = while symx != nil: if symx.kind in routineKinds: errors.add(CandidateError(sym: symx, - unmatchedVarParam: 0, - diagnostics: nil)) + unmatchedVarParam: 0, firstMismatch: 0, + diagnostics: nil, + enabled: false)) symx = nextOverloadIter(o, c, headSymbol) if errors.len == 0: - localError(n.info, "could not resolve: " & $n) + localError(c.config, n.info, "could not resolve: " & $n) else: notFoundError(c, n, errors) proc resolveOverloads(c: PContext, n, orig: PNode, filter: TSymKinds, flags: TExprFlags, - errors: var CandidateErrors): TCandidate = + errors: var CandidateErrors, + errorsEnabled: bool): TCandidate = var initialBinding: PNode var alt: TCandidate var f = n.sons[0] if f.kind == nkBracketExpr: # fill in the bindings: + semOpAux(c, f) initialBinding = f f = f.sons[0] else: @@ -212,7 +262,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode, template pickBest(headSymbol) = pickBestCandidate(c, headSymbol, n, orig, initialBinding, - filter, result, alt, errors, efExplain in flags) + filter, result, alt, errors, efExplain in flags, + errorsEnabled) pickBest(f) let overloadsState = result.state @@ -234,7 +285,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, else: return if nfDotField in n.flags: - internalAssert f.kind == nkIdent and n.sonsLen >= 2 + internalAssert c.config, f.kind == nkIdent and n.len >= 2 # leave the op head symbol empty, # we are going to try multiple variants @@ -263,13 +314,13 @@ proc resolveOverloads(c: PContext, n, orig: PNode, if overloadsState == csEmpty and result.state == csEmpty: if nfDotField in n.flags and nfExplicitCall notin n.flags: - localError(n.info, errUndeclaredField, considerQuotedIdent(f).s) + localError(c.config, n.info, errUndeclaredField % considerQuotedIdent(c.config, f, n).s) else: - localError(n.info, errUndeclaredRoutine, considerQuotedIdent(f).s) + localError(c.config, n.info, errUndeclaredRoutine % considerQuotedIdent(c.config, f, n).s) return elif result.state != csMatch: if nfExprCall in n.flags: - localError(n.info, errExprXCannotBeCalled, + localError(c.config, n.info, "expression '$1' cannot be called" % renderTree(n, {renderNoComments})) else: if {nfDotField, nfDotSetter} * n.flags != {}: @@ -279,13 +330,13 @@ proc resolveOverloads(c: PContext, n, orig: PNode, return if alt.state == csMatch and cmpCandidates(result, alt) == 0 and not sameMethodDispatcher(result.calleeSym, alt.calleeSym): - internalAssert result.state == csMatch + internalAssert c.config, result.state == csMatch #writeMatches(result) #writeMatches(alt) if errorOutputs == {}: # quick error message for performance of 'compiles' built-in: - globalError(n.info, errGenerated, "ambiguous call") - elif gErrorCounter == 0: + globalError(c.config, n.info, errGenerated, "ambiguous call") + elif c.config.errorCounter == 0: # don't cascade errors var args = "(" for i in countup(1, sonsLen(n) - 1): @@ -293,7 +344,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, add(args, typeToString(n.sons[i].typ)) add(args, ")") - localError(n.info, errGenerated, msgKindToString(errAmbiguousCallXYZ) % [ + localError(c.config, n.info, errAmbiguousCallXYZ % [ getProcHeader(result.calleeSym), getProcHeader(alt.calleeSym), args]) @@ -334,7 +385,7 @@ proc inferWithMetatype(c: PContext, formal: PType, result.typ = generateTypeInstance(c, m.bindings, arg.info, formal.skipTypes({tyCompositeTypeClass})) else: - typeMismatch(arg.info, formal, arg.typ) + typeMismatch(c.config, arg.info, formal, arg.typ) # error correction: result = copyTree(arg) result.typ = formal @@ -342,7 +393,7 @@ proc inferWithMetatype(c: PContext, formal: PType, proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = assert x.state == csMatch var finalCallee = x.calleeSym - markUsed(n.sons[0].info, finalCallee, c.graph.usageSym) + markUsed(c.config, n.sons[0].info, finalCallee, c.graph.usageSym) styleCheckUse(n.sons[0].info, finalCallee) assert finalCallee.ast != nil if x.hasFauxMatch: @@ -368,7 +419,7 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = of skType: x.call.add newSymNode(s, n.info) else: - internalAssert false + internalAssert c.config, false result = x.call instGenericConvertersSons(c, result, x) @@ -377,7 +428,7 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = proc canDeref(n: PNode): bool {.inline.} = result = n.len >= 2 and (let t = n[1].typ; - t != nil and t.skipTypes({tyGenericInst, tyAlias}).kind in {tyPtr, tyRef}) + t != nil and t.skipTypes({tyGenericInst, tyAlias, tySink}).kind in {tyPtr, tyRef}) proc tryDeref(n: PNode): PNode = result = newNodeI(nkHiddenDeref, n.info) @@ -386,18 +437,17 @@ proc tryDeref(n: PNode): PNode = proc semOverloadedCall(c: PContext, n, nOrig: PNode, filter: TSymKinds, flags: TExprFlags): PNode = - var errors: CandidateErrors = if efExplain in flags: @[] - else: nil - var r = resolveOverloads(c, n, nOrig, filter, flags, errors) + var errors: CandidateErrors = if efExplain in flags: @[] else: nil + var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags) if r.state == csMatch: # this may be triggered, when the explain pragma is used if errors.len > 0: let (_, candidates) = presentFailedCandidates(c, n, errors) - message(n.info, hintUserRaw, + message(c.config, n.info, hintUserRaw, "Non-matching candidates for " & renderTree(n) & "\n" & candidates) result = semResolvedCall(c, n, r) - elif experimentalMode(c) and canDeref(n): + elif implicitDeref in c.features and canDeref(n): # try to deref the first argument and then try overloading resolution again: # # XXX: why is this here? @@ -406,7 +456,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, # into sigmatch with hidden conversion produced there # n.sons[1] = n.sons[1].tryDeref - var r = resolveOverloads(c, n, nOrig, filter, flags, errors) + var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags) if r.state == csMatch: result = semResolvedCall(c, n, r) else: # get rid of the deref again for a better error message: @@ -426,8 +476,8 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, else: notFoundError(c, n, errors) -proc explicitGenericInstError(n: PNode): PNode = - localError(n.info, errCannotInstantiateX, renderTree(n)) +proc explicitGenericInstError(c: PContext; n: PNode): PNode = + localError(c.config, n.info, errCannotInstantiateX % renderTree(n)) result = n proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = @@ -442,7 +492,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = if tm in {isNone, isConvertible}: return nil var newInst = generateInstance(c, s, m.bindings, n.info) newInst.typ.flags.excl tfUnresolved - markUsed(n.info, s, c.graph.usageSym) + markUsed(c.config, n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) result = newSymNode(newInst, n.info) @@ -458,11 +508,11 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = # number of generic type parameters: if safeLen(s.ast.sons[genericParamsPos]) != n.len-1: let expected = safeLen(s.ast.sons[genericParamsPos]) - localError(n.info, errGenerated, "cannot instantiate: " & renderTree(n) & - "; got " & $(n.len-1) & " type(s) but expected " & $expected) + localError(c.config, n.info, errGenerated, "cannot instantiate: '" & renderTree(n) & + "'; got " & $(n.len-1) & " type(s) but expected " & $expected) return n result = explicitGenericSym(c, n, s) - if result == nil: result = explicitGenericInstError(n) + if result == nil: result = explicitGenericInstError(c, n) elif a.kind in {nkClosedSymChoice, nkOpenSymChoice}: # choose the generic proc with the proper number of type parameters. # XXX I think this could be improved by reusing sigmatch.paramTypesMatch. @@ -480,10 +530,10 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = # get rid of nkClosedSymChoice if not ambiguous: if result.len == 1 and a.kind == nkClosedSymChoice: result = result[0] - elif result.len == 0: result = explicitGenericInstError(n) - # candidateCount != 1: return explicitGenericInstError(n) + elif result.len == 0: result = explicitGenericInstError(c, n) + # candidateCount != 1: return explicitGenericInstError(c, n) else: - result = explicitGenericInstError(n) + result = explicitGenericInstError(c, n) proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym = # Searchs for the fn in the symbol table. If the parameter lists are suitable diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 8affee649..12ac19ca3 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -14,7 +14,7 @@ import wordrecg, ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math, magicsys, nversion, nimsets, parser, times, passes, rodread, vmdef, - modulegraphs + modulegraphs, configuration type TOptionEntry* = object # entries to put on a stack for pragma parsing @@ -63,7 +63,7 @@ type # to the user. efWantStmt, efAllowStmt, efDetermineType, efExplain, efAllowDestructor, efWantValue, efOperand, efNoSemCheck, - efNoProcvarCheck, efNoEvaluateGeneric, efInCall, efFromHlo, + efNoEvaluateGeneric, efInCall, efFromHlo TExprFlags* = set[TExprFlag] @@ -89,6 +89,7 @@ type ambiguousSymbols*: IntSet # ids of all ambiguous symbols (cannot # store this info in the syms themselves!) inGenericContext*: int # > 0 if we are in a generic type + inStaticContext*: int # > 0 if we are inside a static: block inUnrolledContext*: int # > 0 if we are unrolling a loop compilesContextId*: int # > 0 if we are in a ``compiles`` magic compilesContextIdGenerator*: int @@ -130,6 +131,7 @@ type signatures*: TStrTable recursiveDep*: string suggestionsMade*: bool + features*: set[Feature] inTypeContext*: int typesWithOps*: seq[(PType, PType)] #\ # We need to instantiate the type bound ops lazily after @@ -138,13 +140,15 @@ type # would otherwise fail. runnableExamples*: PNode +template config*(c: PContext): ConfigRef = c.graph.config + proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = result.genericSym = s result.inst = inst proc filename*(c: PContext): string = # the module's filename - return c.module.filename + return toFilename(FileIndex c.module.position) proc scopeDepth*(c: PContext): int {.inline.} = result = if c.currentScope != nil: c.currentScope.depthLevel @@ -163,7 +167,7 @@ proc pushOwner*(c: PContext; owner: PSym) = proc popOwner*(c: PContext) = var length = len(c.graph.owners) if length > 0: setLen(c.graph.owners, length - 1) - else: internalError("popOwner") + else: internalError(c.config, "popOwner") proc lastOptionEntry*(c: PContext): POptionEntry = result = c.optionStack[^1] @@ -199,19 +203,19 @@ proc considerGenSyms*(c: PContext; n: PNode) = for i in 0..<n.safeLen: considerGenSyms(c, n.sons[i]) -proc newOptionEntry*(): POptionEntry = +proc newOptionEntry*(conf: ConfigRef): POptionEntry = new(result) - result.options = gOptions + result.options = conf.options result.defaultCC = ccDefault result.dynlib = nil - result.notes = gNotes + result.notes = conf.notes proc newContext*(graph: ModuleGraph; module: PSym; cache: IdentCache): PContext = new(result) result.ambiguousSymbols = initIntSet() result.optionStack = @[] result.libs = @[] - result.optionStack.add(newOptionEntry()) + result.optionStack.add(newOptionEntry(graph.config)) result.module = module result.friendModules = @[module] result.converters = @[] @@ -225,7 +229,7 @@ proc newContext*(graph: ModuleGraph; module: PSym; cache: IdentCache): PContext result.graph = graph initStrTable(result.signatures) result.typesWithOps = @[] - + result.features = graph.config.features proc inclSym(sq: var TSymSeq, s: PSym) = var L = len(sq) @@ -254,36 +258,37 @@ proc newTypeS*(kind: TTypeKind, c: PContext): PType = proc makePtrType*(c: PContext, baseType: PType): PType = result = newTypeS(tyPtr, c) - addSonSkipIntLit(result, baseType.assertNotNil) + addSonSkipIntLit(result, baseType) proc makeTypeWithModifier*(c: PContext, modifier: TTypeKind, baseType: PType): PType = - assert modifier in {tyVar, tyPtr, tyRef, tyStatic, tyTypeDesc} + assert modifier in {tyVar, tyLent, tyPtr, tyRef, tyStatic, tyTypeDesc} - if modifier in {tyVar, tyTypeDesc} and baseType.kind == modifier: + if modifier in {tyVar, tyLent, tyTypeDesc} and baseType.kind == modifier: result = baseType else: result = newTypeS(modifier, c) - addSonSkipIntLit(result, baseType.assertNotNil) + addSonSkipIntLit(result, baseType) -proc makeVarType*(c: PContext, baseType: PType): PType = - if baseType.kind == tyVar: +proc makeVarType*(c: PContext, baseType: PType; kind = tyVar): PType = + if baseType.kind == kind: result = baseType else: - result = newTypeS(tyVar, c) - addSonSkipIntLit(result, baseType.assertNotNil) + result = newTypeS(kind, c) + addSonSkipIntLit(result, baseType) proc makeTypeDesc*(c: PContext, typ: PType): PType = if typ.kind == tyTypeDesc: result = typ else: result = newTypeS(tyTypeDesc, c) - result.addSonSkipIntLit(typ.assertNotNil) + result.addSonSkipIntLit(typ) proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode = let typedesc = makeTypeDesc(c, typ) - let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info).linkTo(typedesc) + let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info, + c.config.options).linkTo(typedesc) return newSymNode(sym, info) proc makeTypeFromExpr*(c: PContext, n: PNode): PType = @@ -338,21 +343,20 @@ proc makeNotType*(c: PContext, t1: PType): PType = result.flags.incl(t1.flags * {tfHasStatic}) result.flags.incl tfHasMeta -proc nMinusOne*(n: PNode): PNode = +proc nMinusOne(c: PContext; n: PNode): PNode = result = newNode(nkCall, n.info, @[ - newSymNode(getSysMagic("pred", mPred)), - n]) + newSymNode(getSysMagic(c.graph, n.info, "pred", mPred)), n]) # Remember to fix the procs below this one when you make changes! proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType = - let intType = getSysType(tyInt) + let intType = getSysType(c.graph, n.info, tyInt) result = newTypeS(tyRange, c) result.sons = @[intType] if n.typ != nil and n.typ.n == nil: result.flags.incl tfUnresolved result.n = newNode(nkRange, n.info, @[ newIntTypeNode(nkIntLit, 0, intType), - makeStaticExpr(c, n.nMinusOne)]) + makeStaticExpr(c, nMinusOne(c, n))]) template rangeHasUnresolvedStatic*(t: PType): bool = tfUnresolved in t.flags @@ -371,7 +375,8 @@ proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext) = dest.size = - 1 proc makeRangeType*(c: PContext; first, last: BiggestInt; - info: TLineInfo; intType = getSysType(tyInt)): PType = + info: TLineInfo; intType: PType = nil): PType = + let intType = if intType != nil: intType else: getSysType(c.graph, info, tyInt) var n = newNodeI(nkRange, info) addSon(n, newIntTypeNode(nkIntLit, first, intType)) addSon(n, newIntTypeNode(nkIntLit, last, intType)) @@ -384,20 +389,17 @@ proc markIndirect*(c: PContext, s: PSym) {.inline.} = incl(s.flags, sfAddrTaken) # XXX add to 'c' for global analysis -proc illFormedAst*(n: PNode) = - globalError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments})) +proc illFormedAst*(n: PNode; conf: ConfigRef) = + globalError(conf, n.info, errIllFormedAstX, renderTree(n, {renderNoComments})) -proc illFormedAstLocal*(n: PNode) = - localError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments})) +proc illFormedAstLocal*(n: PNode; conf: ConfigRef) = + localError(conf, n.info, errIllFormedAstX, renderTree(n, {renderNoComments})) -proc checkSonsLen*(n: PNode, length: int) = - if sonsLen(n) != length: illFormedAst(n) +proc checkSonsLen*(n: PNode, length: int; conf: ConfigRef) = + if sonsLen(n) != length: illFormedAst(n, conf) -proc checkMinSonsLen*(n: PNode, length: int) = - if sonsLen(n) < length: illFormedAst(n) +proc checkMinSonsLen*(n: PNode, length: int; conf: ConfigRef) = + if sonsLen(n) < length: illFormedAst(n, conf) proc isTopLevel*(c: PContext): bool {.inline.} = result = c.currentScope.depthLevel <= 2 - -proc experimentalMode*(c: PContext): bool {.inline.} = - result = gExperimentalMode or sfExperimental in c.module.flags diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 51e75e91f..92b9c365a 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -10,12 +10,24 @@ # this module does the semantic checking for expressions # included from sem.nim +const + errExprXHasNoType = "expression '$1' has no type (or is ambiguous)" + errXExpectsTypeOrValue = "'$1' expects a type or value" + errVarForOutParamNeededX = "for a 'var' type a variable needs to be passed; but '$1' is immutable" + errXStackEscape = "address of '$1' may not escape its stack frame" + errExprHasNoAddress = "expression has no address; maybe use 'unsafeAddr'" + errCannotInterpretNodeX = "cannot evaluate '$1'" + errNamedExprExpected = "named expression expected" + errNamedExprNotAllowed = "named expression not allowed here" + errFieldInitTwice = "field initialized twice: '$1'" + errUndeclaredFieldX = "undeclared field: '$1'" + proc semTemplateExpr(c: PContext, n: PNode, s: PSym, flags: TExprFlags = {}): PNode = - markUsed(n.info, s, c.graph.usageSym) + markUsed(c.config, n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) pushInfoContext(n.info) - result = evalTemplate(n, s, getCurrOwner(c), efFromHlo in flags) + result = evalTemplate(n, s, getCurrOwner(c), c.config, efFromHlo in flags) if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags) popInfoContext() @@ -31,12 +43,12 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = if result.typ != nil: # XXX tyGenericInst here? if result.typ.kind == tyProc and tfUnresolved in result.typ.flags: - localError(n.info, errProcHasNoConcreteType, n.renderTree) - if result.typ.kind == tyVar: result = newDeref(result) + localError(c.config, n.info, errProcHasNoConcreteType % n.renderTree) + if result.typ.kind in {tyVar, tyLent}: result = newDeref(result) elif {efWantStmt, efAllowStmt} * flags != {}: result.typ = newTypeS(tyVoid, c) else: - localError(n.info, errExprXHasNoType, + localError(c.config, n.info, errExprXHasNoType % renderTree(result, {renderNoComments})) result.typ = errorType(c) @@ -47,12 +59,11 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = #raiseRecoverableError("") result = errorNode(c, n) if result.typ == nil or result.typ == enforceVoidContext: - localError(n.info, errExprXHasNoType, + localError(c.config, n.info, errExprXHasNoType % renderTree(result, {renderNoComments})) result.typ = errorType(c) else: - if efNoProcvarCheck notin flags: semProcvarCheck(c, result) - if result.typ.kind == tyVar: result = newDeref(result) + if result.typ.kind in {tyVar, tyLent}: result = newDeref(result) proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = semExpr(c, n, flags) @@ -60,19 +71,17 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # do not produce another redundant error message: result = errorNode(c, n) if result.typ == nil: - localError(n.info, errExprXHasNoType, + localError(c.config, n.info, errExprXHasNoType % renderTree(result, {renderNoComments})) result.typ = errorType(c) - else: - semProcvarCheck(c, result) proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = result = symChoice(c, n, s, scClosed) -proc inlineConst(n: PNode, s: PSym): PNode {.inline.} = +proc inlineConst(c: PContext, n: PNode, s: PSym): PNode {.inline.} = result = copyTree(s.ast) if result.isNil: - localError(n.info, "constant of type '" & typeToString(s.typ) & "' has no value") + localError(c.config, n.info, "constant of type '" & typeToString(s.typ) & "' has no value") result = newSymNode(s) else: result.typ = s.typ @@ -175,15 +184,28 @@ proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) = proc semConv(c: PContext, n: PNode): PNode = if sonsLen(n) != 2: - localError(n.info, errConvNeedsOneArg) + localError(c.config, n.info, "a type conversion takes exactly one argument") return n result = newNodeI(nkConv, n.info) var targetType = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc}) maybeLiftType(targetType, c, n[0].info) + + if targetType.kind in {tySink, tyLent}: + let baseType = semTypeNode(c, n.sons[1], nil).skipTypes({tyTypeDesc}) + let t = newTypeS(targetType.kind, c) + t.rawAddSonNoPropagationOfTypeFlags baseType + result = newNodeI(nkType, n.info) + result.typ = makeTypeDesc(c, t) + return + result.addSon copyTree(n.sons[0]) - var op = semExprWithType(c, n.sons[1]) + # special case to make MyObject(x = 3) produce a nicer error message: + if n[1].kind == nkExprEqExpr and + targetType.skipTypes(abstractPtrs).kind == tyObject: + localError(c.config, n.info, "object contruction uses ':', not '='") + var op = semExprWithType(c, n.sons[1]) if targetType.isMetaType: let final = inferWithMetatype(c, targetType, op, true) result.addSon final @@ -191,6 +213,8 @@ proc semConv(c: PContext, n: PNode): PNode = return result.typ = targetType + # XXX op is overwritten later on, this is likely added too early + # here or needs to be overwritten too then. addSon(result, op) if not isSymChoice(op): @@ -200,21 +224,21 @@ proc semConv(c: PContext, n: PNode): PNode = # handle SomeProcType(SomeGenericProc) if op.kind == nkSym and op.sym.isGenericRoutine: result.sons[1] = fitNode(c, result.typ, result.sons[1], result.info) - elif op.kind == nkPar and targetType.kind == tyTuple: + elif op.kind in {nkPar, nkTupleConstr} and targetType.kind == tyTuple: op = fitNode(c, targetType, op, result.info) of convNotNeedeed: - message(n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString) + message(c.config, n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString) of convNotLegal: result = fitNode(c, result.typ, result.sons[1], result.info) if result == nil: - localError(n.info, errGenerated, msgKindToString(errIllegalConvFromXtoY)% + localError(c.config, n.info, "illegal conversion from '$1' to '$2'" % [op.typ.typeToString, result.typ.typeToString]) else: for i in countup(0, sonsLen(op) - 1): let it = op.sons[i] let status = checkConvertible(c, result.typ, it.typ) if status in {convOK, convNotNeedeed}: - markUsed(n.info, it.sym, c.graph.usageSym) + markUsed(c.config, n.info, it.sym, c.graph.usageSym) styleCheckUse(n.info, it.sym) markIndirect(c, it.sym) return it @@ -222,16 +246,16 @@ proc semConv(c: PContext, n: PNode): PNode = proc semCast(c: PContext, n: PNode): PNode = ## Semantically analyze a casting ("cast[type](param)") - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.config) let targetType = semTypeNode(c, n.sons[0], nil) let castedExpr = semExprWithType(c, n.sons[1]) if tfHasMeta in targetType.flags: - localError(n.sons[0].info, errCastToANonConcreteType, $targetType) + localError(c.config, n.sons[0].info, "cannot cast to a non concrete type: '$1'" % $targetType) if not isCastable(targetType, castedExpr.typ): let tar = $targetType let alt = typeToString(targetType, preferDesc) let msg = if tar != alt: tar & "=" & alt else: tar - localError(n.info, errExprCannotBeCastToX, msg) + localError(c.config, n.info, "expression cannot be cast to " & msg) result = newNodeI(nkCast, n.info) result.typ = targetType addSon(result, copyTree(n.sons[0])) @@ -241,13 +265,13 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = const opToStr: array[mLow..mHigh, string] = ["low", "high"] if sonsLen(n) != 2: - localError(n.info, errXExpectsTypeOrValue, opToStr[m]) + localError(c.config, n.info, errXExpectsTypeOrValue % opToStr[m]) else: n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType}) var typ = skipTypes(n.sons[1].typ, abstractVarRange + {tyTypeDesc}) case typ.kind of tySequence, tyString, tyCString, tyOpenArray, tyVarargs: - n.typ = getSysType(tyInt) + n.typ = getSysType(c.graph, n.info, tyInt) of tyArray: n.typ = typ.sons[0] # indextype of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt8, tyUInt16, tyUInt32: @@ -259,20 +283,20 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = # that could easily turn into an infinite recursion in semtypinst n.typ = makeTypeFromExpr(c, n.copyTree) else: - localError(n.info, errInvalidArgForX, opToStr[m]) + localError(c.config, n.info, "invalid argument for: " & opToStr[m]) result = n proc semSizeof(c: PContext, n: PNode): PNode = if sonsLen(n) != 2: - localError(n.info, errXExpectsTypeOrValue, "sizeof") + localError(c.config, n.info, errXExpectsTypeOrValue % "sizeof") else: n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType}) #restoreOldStyleType(n.sons[1]) - n.typ = getSysType(tyInt) + n.typ = getSysType(c.graph, n.info, tyInt) result = n proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode = - internalAssert n.sonsLen == 3 and + internalAssert c.config, n.sonsLen == 3 and n[1].typ != nil and n[1].typ.kind == tyTypeDesc and n[2].kind in {nkStrLit..nkTripleStrLit, nkType} @@ -293,7 +317,9 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode = maybeLiftType(t2, c, n.info) var m: TCandidate initCandidate(c, m, t2) - if efExplain in flags: m.diagnostics = @[] + if efExplain in flags: + m.diagnostics = @[] + m.diagnosticsEnabled = true let match = typeRel(m, t2, t1) >= isSubtype # isNone result = newIntNode(nkIntLit, ord(match)) @@ -301,10 +327,10 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode = if sonsLen(n) != 3: - localError(n.info, errXExpectsTwoArguments, "is") + localError(c.config, n.info, "'is' operator takes 2 arguments") result = n - n.typ = getSysType(tyBool) + n.typ = getSysType(c.graph, n.info, tyBool) n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator}) if n[2].kind notin {nkStrLit..nkTripleStrLit}: @@ -326,8 +352,8 @@ proc semOpAux(c: PContext, n: PNode) = for i in countup(1, n.sonsLen-1): var a = n.sons[i] if a.kind == nkExprEqExpr and sonsLen(a) == 2: - var info = a.sons[0].info - a.sons[0] = newIdentNode(considerQuotedIdent(a.sons[0], a), info) + let info = a.sons[0].info + a.sons[0] = newIdentNode(considerQuotedIdent(c.config, a.sons[0], a), info) a.sons[1] = semExprWithType(c, a.sons[1], flags) a.typ = a.sons[1].typ else: @@ -344,34 +370,34 @@ proc overloadedCallOpr(c: PContext, n: PNode): PNode = for i in countup(0, sonsLen(n) - 1): addSon(result, n.sons[i]) result = semExpr(c, result) -proc changeType(n: PNode, newType: PType, check: bool) = +proc changeType(c: PContext; n: PNode, newType: PType, check: bool) = case n.kind of nkCurly, nkBracket: for i in countup(0, sonsLen(n) - 1): - changeType(n.sons[i], elemType(newType), check) - of nkPar: - let tup = newType.skipTypes({tyGenericInst, tyAlias}) + changeType(c, n.sons[i], elemType(newType), check) + of nkPar, nkTupleConstr: + let tup = newType.skipTypes({tyGenericInst, tyAlias, tySink}) if tup.kind != tyTuple: if tup.kind == tyObject: return - globalError(n.info, "no tuple type for constructor") + globalError(c.config, n.info, "no tuple type for constructor") elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr: # named tuple? for i in countup(0, sonsLen(n) - 1): var m = n.sons[i].sons[0] if m.kind != nkSym: - globalError(m.info, "invalid tuple constructor") + globalError(c.config, m.info, "invalid tuple constructor") return if tup.n != nil: var f = getSymFromList(tup.n, m.sym.name) if f == nil: - globalError(m.info, "unknown identifier: " & m.sym.name.s) + globalError(c.config, m.info, "unknown identifier: " & m.sym.name.s) return - changeType(n.sons[i].sons[1], f.typ, check) + changeType(c, n.sons[i].sons[1], f.typ, check) else: - changeType(n.sons[i].sons[1], tup.sons[i], check) + changeType(c, n.sons[i].sons[1], tup.sons[i], check) else: for i in countup(0, sonsLen(n) - 1): - changeType(n.sons[i], tup.sons[i], check) + changeType(c, n.sons[i], tup.sons[i], check) when false: var m = n.sons[i] var a = newNodeIT(nkExprColonExpr, m.info, newType.sons[i]) @@ -382,7 +408,7 @@ proc changeType(n: PNode, newType: PType, check: bool) = if check and n.kind != nkUInt64Lit: let value = n.intVal if value < firstOrd(newType) or value > lastOrd(newType): - localError(n.info, errGenerated, "cannot convert " & $value & + localError(c.config, n.info, "cannot convert " & $value & " to " & typeToString(newType)) else: discard n.typ = newType @@ -393,7 +419,7 @@ proc arrayConstrType(c: PContext, n: PNode): PType = if sonsLen(n) == 0: rawAddSon(typ, newTypeS(tyEmpty, c)) # needs an empty basetype! else: - var t = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyOrdinal, tyAlias}) + var t = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink}) addSonSkipIntLit(typ, t) typ.sons[0] = makeRangeType(c, 0, sonsLen(n) - 1, n.info) result = typ @@ -407,7 +433,7 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = else: var x = n.sons[0] var lastIndex: BiggestInt = 0 - var indexType = getSysType(tyInt) + var indexType = getSysType(c.graph, n.info, tyInt) if x.kind == nkExprColonExpr and sonsLen(x) == 2: var idx = semConstExpr(c, x.sons[0]) lastIndex = getOrdValue(idx) @@ -417,14 +443,14 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = let yy = semExprWithType(c, x) var typ = yy.typ addSon(result, yy) - #var typ = skipTypes(result.sons[0].typ, {tyGenericInst, tyVar, tyOrdinal}) + #var typ = skipTypes(result.sons[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal}) for i in countup(1, sonsLen(n) - 1): x = n.sons[i] if x.kind == nkExprColonExpr and sonsLen(x) == 2: var idx = semConstExpr(c, x.sons[0]) idx = fitNode(c, indexType, idx, x.info) if lastIndex+1 != getOrdValue(idx): - localError(x.info, errInvalidOrderInArrayConstructor) + localError(c.config, x.info, "invalid order in array constructor") x = x.sons[1] let xx = semExprWithType(c, x, flags*{efAllowDestructor}) @@ -448,22 +474,22 @@ proc fixAbstractType(c: PContext, n: PNode) = {tyNil, tyTuple, tySet} or it[1].isArrayConstr: var s = skipTypes(it.typ, abstractVar) if s.kind != tyExpr: - changeType(it.sons[1], s, check=true) + changeType(c, it.sons[1], s, check=true) n.sons[i] = it.sons[1] proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult = result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr) proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = - if n.kind == nkHiddenDeref and not (gCmd == cmdCompileToCpp or + if n.kind == nkHiddenDeref and not (c.config.cmd == cmdCompileToCpp or sfCompileToCpp in c.module.flags): - checkSonsLen(n, 1) + checkSonsLen(n, 1, c.config) result = n.sons[0] else: result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ)) addSon(result, n) if isAssignable(c, n) notin {arLValue, arLocalLValue}: - localError(n.info, errVarForOutParamNeededX, $n) + localError(c.config, n.info, errVarForOutParamNeededX % renderNotLValue(n)) proc analyseIfAddressTaken(c: PContext, n: PNode): PNode = result = n @@ -471,27 +497,27 @@ proc analyseIfAddressTaken(c: PContext, n: PNode): PNode = of nkSym: # n.sym.typ can be nil in 'check' mode ... if n.sym.typ != nil and - skipTypes(n.sym.typ, abstractInst-{tyTypeDesc}).kind != tyVar: + skipTypes(n.sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}: incl(n.sym.flags, sfAddrTaken) result = newHiddenAddrTaken(c, n) of nkDotExpr: - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.config) if n.sons[1].kind != nkSym: - internalError(n.info, "analyseIfAddressTaken") + internalError(c.config, n.info, "analyseIfAddressTaken") return - if skipTypes(n.sons[1].sym.typ, abstractInst-{tyTypeDesc}).kind != tyVar: + if skipTypes(n.sons[1].sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}: incl(n.sons[1].sym.flags, sfAddrTaken) result = newHiddenAddrTaken(c, n) of nkBracketExpr: - checkMinSonsLen(n, 1) - if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind != tyVar: + checkMinSonsLen(n, 1, c.config) + if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}: if n.sons[0].kind == nkSym: incl(n.sons[0].sym.flags, sfAddrTaken) result = newHiddenAddrTaken(c, n) else: result = newHiddenAddrTaken(c, n) proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = - checkMinSonsLen(n, 1) + checkMinSonsLen(n, 1, c.config) const FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl, mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap, @@ -499,7 +525,7 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = # get the real type of the callee # it may be a proc var with a generic alias type, so we skip over them - var t = n.sons[0].typ.skipTypes({tyGenericInst, tyAlias}) + var t = n.sons[0].typ.skipTypes({tyGenericInst, tyAlias, tySink}) if n.sons[0].kind == nkSym and n.sons[0].sym.magic in FakeVarParams: # BUGFIX: check for L-Value still needs to be done for the arguments! @@ -510,14 +536,21 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = let it = n[i] if isAssignable(c, it) notin {arLValue, arLocalLValue}: if it.kind != nkHiddenAddr: - localError(it.info, errVarForOutParamNeededX, $it) + localError(c.config, it.info, errVarForOutParamNeededX % $it) + # bug #5113: disallow newSeq(result) where result is a 'var T': + if n[0].sym.magic in {mNew, mNewFinalize, mNewSeq}: + var arg = n[1] #.skipAddr + if arg.kind == nkHiddenDeref: arg = arg[0] + if arg.kind == nkSym and arg.sym.kind == skResult and + arg.typ.skipTypes(abstractInst).kind in {tyVar, tyLent}: + localError(c.config, n.info, errXStackEscape % renderTree(n[1], {renderNoComments})) + return for i in countup(1, sonsLen(n) - 1): if n.sons[i].kind == nkHiddenCallConv: # we need to recurse explicitly here as converters can create nested # calls and then they wouldn't be analysed otherwise analyseIfAddressTakenInCall(c, n.sons[i]) - semProcvarCheck(c, n.sons[i]) if i < sonsLen(t) and skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar: if n.sons[i].kind != nkHiddenAddr: @@ -539,14 +572,14 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = call.add(n.sons[0]) var allConst = true for i in 1 ..< n.len: - var a = getConstExpr(c.module, n.sons[i]) + var a = getConstExpr(c.module, n.sons[i], c.graph) if a == nil: allConst = false a = n.sons[i] if a.kind == nkHiddenStdConv: a = a.sons[1] call.add(a) if allConst: - result = semfold.getConstExpr(c.module, call) + result = semfold.getConstExpr(c.module, call, c.graph) if result.isNil: result = n else: return result @@ -568,7 +601,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = if {sfNoSideEffect, sfCompileTime} * callee.flags != {} and {sfForward, sfImportc} * callee.flags == {} and n.typ != nil: if sfCompileTime notin callee.flags and - optImplicitStatic notin gOptions: return + optImplicitStatic notin c.config.options: return if callee.magic notin ctfeWhitelist: return if callee.kind notin {skProc, skFunc, skConverter} or callee.isGenericRoutine: @@ -579,17 +612,17 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = var call = newNodeIT(nkCall, n.info, n.typ) call.add(n.sons[0]) for i in 1 ..< n.len: - let a = getConstExpr(c.module, n.sons[i]) + let a = getConstExpr(c.module, n.sons[i], c.graph) if a == nil: return n call.add(a) #echo "NOW evaluating at compile time: ", call.renderTree if sfCompileTime in callee.flags: - result = evalStaticExpr(c.module, c.cache, call, c.p.owner) + result = evalStaticExpr(c.module, c.cache, c.graph, call, c.p.owner) if result.isNil: - localError(n.info, errCannotInterpretNodeX, renderTree(call)) + localError(c.config, n.info, errCannotInterpretNodeX % renderTree(call)) else: result = fixupTypeAfterEval(c, result, n) else: - result = evalConstExpr(c.module, c.cache, call) + result = evalConstExpr(c.module, c.cache, c.graph, call) if result.isNil: result = n else: result = fixupTypeAfterEval(c, result, n) #if result != n: @@ -598,9 +631,9 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = proc semStaticExpr(c: PContext, n: PNode): PNode = let a = semExpr(c, n.sons[0]) if a.findUnresolvedStatic != nil: return a - result = evalStaticExpr(c.module, c.cache, a, c.p.owner) + result = evalStaticExpr(c.module, c.cache, c.graph, a, c.p.owner) if result.isNil: - localError(n.info, errCannotInterpretNodeX, renderTree(n)) + localError(c.config, n.info, errCannotInterpretNodeX % renderTree(n)) result = emptyNode else: result = fixupTypeAfterEval(c, result, a) @@ -620,14 +653,14 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, if result != nil: if result.sons[0].kind != nkSym: - internalError("semOverloadedCallAnalyseEffects") + internalError(c.config, "semOverloadedCallAnalyseEffects") return let callee = result.sons[0].sym case callee.kind of skMacro, skTemplate: discard else: if callee.kind == skIterator and callee.id == c.p.owner.id: - localError(n.info, errRecursiveDependencyX, callee.name.s) + localError(c.config, 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)) @@ -640,7 +673,7 @@ proc resolveIndirectCall(c: PContext; n, nOrig: PNode; matches(c, n, nOrig, result) if result.state != csMatch: # try to deref the first argument: - if experimentalMode(c) and canDeref(n): + if implicitDeref in c.features and canDeref(n): n.sons[1] = n.sons[1].tryDeref initCandidate(c, result, t) matches(c, n, nOrig, result) @@ -675,10 +708,10 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode = proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = result = nil - checkMinSonsLen(n, 1) + checkMinSonsLen(n, 1, c.config) var prc = n.sons[0] if n.sons[0].kind == nkDotExpr: - checkSonsLen(n.sons[0], 2) + checkSonsLen(n.sons[0], 2, c.config) let n0 = semFieldAccess(c, n.sons[0]) if n0.kind == nkDotCall: # it is a static call! @@ -692,7 +725,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = else: n.sons[0] = semExpr(c, n.sons[0], {efInCall}) let t = n.sons[0].typ - if t != nil and t.kind == tyVar: + if t != nil and t.kind in {tyVar, tyLent}: n.sons[0] = newDeref(n.sons[0]) elif n.sons[0].kind == nkBracketExpr: let s = bracketedMacro(n.sons[0]) @@ -711,11 +744,11 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = if m.state != csMatch: if errorOutputs == {}: # speed up error generation: - globalError(n.info, errTypeMismatch, "") + globalError(c.config, n.info, "type mismatch") return emptyNode else: var hasErrorType = false - var msg = msgKindToString(errTypeMismatch) + var msg = "type mismatch: got <" for i in countup(1, sonsLen(n) - 1): if i > 1: add(msg, ", ") let nt = n.sons[i].typ @@ -724,9 +757,9 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = hasErrorType = true break if not hasErrorType: - add(msg, ")\n" & msgKindToString(errButExpected) & "\n" & + add(msg, ">\nbut expected one of: \n" & typeToString(n.sons[0].typ)) - localError(n.info, errGenerated, msg) + localError(c.config, n.info, msg) return errorNode(c, n) result = nil else: @@ -769,11 +802,11 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = proc buildEchoStmt(c: PContext, n: PNode): PNode = # we MUST not check 'n' for semantics again here! But for now we give up: result = newNodeI(nkCall, n.info) - var e = strTableGet(magicsys.systemModule.tab, getIdent"echo") + var e = strTableGet(c.graph.systemModule.tab, getIdent"echo") if e != nil: add(result, newSymNode(e)) else: - localError(n.info, errSystemNeeds, "echo") + localError(c.config, n.info, "system needs: echo") add(result, errorNode(c, n)) add(result, n) result = semExpr(c, result) @@ -817,8 +850,8 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent, result = lookupInRecordAndBuildCheck(c, n, r.sons[i], field, check) if result != nil: return of nkRecCase: - checkMinSonsLen(r, 2) - if (r.sons[0].kind != nkSym): illFormedAst(r) + checkMinSonsLen(r, 2, c.config) + if (r.sons[0].kind != nkSym): illFormedAst(r, c.config) result = lookupInRecordAndBuildCheck(c, n, r.sons[0], field, check) if result != nil: return let setType = createSetType(c, r.sons[0].typ) @@ -836,8 +869,8 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent, addSon(check, ast.emptyNode) # make space for access node s = newNodeIT(nkCurly, n.info, setType) for j in countup(0, sonsLen(it) - 2): addSon(s, copyTree(it.sons[j])) - var inExpr = newNodeIT(nkCall, n.info, getSysType(tyBool)) - addSon(inExpr, newSymNode(opContains, n.info)) + var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool)) + addSon(inExpr, newSymNode(c.graph.opContains, n.info)) addSon(inExpr, s) addSon(inExpr, copyTree(r.sons[0])) addSon(check, inExpr) @@ -849,26 +882,29 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent, if check == nil: check = newNodeI(nkCheckedFieldExpr, n.info) addSon(check, ast.emptyNode) # make space for access node - var inExpr = newNodeIT(nkCall, n.info, getSysType(tyBool)) - addSon(inExpr, newSymNode(opContains, n.info)) + var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool)) + addSon(inExpr, newSymNode(c.graph.opContains, n.info)) addSon(inExpr, s) addSon(inExpr, copyTree(r.sons[0])) - var notExpr = newNodeIT(nkCall, n.info, getSysType(tyBool)) - addSon(notExpr, newSymNode(opNot, n.info)) + var notExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool)) + addSon(notExpr, newSymNode(c.graph.opNot, n.info)) addSon(notExpr, inExpr) addSon(check, notExpr) return - else: illFormedAst(it) + else: illFormedAst(it, c.config) of nkSym: if r.sym.name.id == field.id: result = r.sym - else: illFormedAst(n) + else: illFormedAst(n, c.config) const tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass} - tyDotOpTransparent = {tyVar, tyPtr, tyRef, tyAlias} + tyDotOpTransparent = {tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink} proc readTypeParameter(c: PContext, typ: PType, paramName: PIdent, info: TLineInfo): PNode = + # Note: This function will return emptyNode when attempting to read + # a static type parameter that is not yet resolved (e.g. this may + # happen in proc signatures such as `proc(x: T): array[T.sizeParam, U]` if typ.kind in {tyUserTypeClass, tyUserTypeClassInst}: for statement in typ.n: case statement.kind @@ -899,7 +935,10 @@ proc readTypeParameter(c: PContext, typ: PType, if tParam.sym.name.id == paramName.id: let rawTyp = ty.sons[s + 1] if rawTyp.kind == tyStatic: - return rawTyp.n + if rawTyp.n != nil: + return rawTyp.n + else: + return emptyNode else: let foundTyp = makeTypeDesc(c, rawTyp) return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info) @@ -910,12 +949,12 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = let s = getGenSym(c, sym) case s.kind of skConst: - markUsed(n.info, s, c.graph.usageSym) + markUsed(c.config, n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, tyTuple, tySet, tyUInt..tyUInt64: - if s.magic == mNone: result = inlineConst(n, s) + if s.magic == mNone: result = inlineConst(c, n, s) else: result = newSymNode(s, n.info) of tyArray, tySequence: # Consider:: @@ -928,26 +967,29 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = # It is clear that ``[]`` means two totally different things. Thus, we # copy `x`'s AST into each context, so that the type fixup phase can # deal with two different ``[]``. - if s.ast.len == 0: result = inlineConst(n, s) + if s.ast.len == 0: result = inlineConst(c, n, s) else: result = newSymNode(s, n.info) else: result = newSymNode(s, n.info) of skMacro: - if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0: - markUsed(n.info, s, c.graph.usageSym) + if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or + (n.kind notin nkCallKinds and s.requiredParams > 0): + markUsed(c.config, n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) - result = newSymNode(s, n.info) + result = symChoice(c, n, s, scClosed) else: result = semMacroExpr(c, n, n, s, flags) of skTemplate: - if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0: - markUsed(n.info, s, c.graph.usageSym) + if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or + (n.kind notin nkCallKinds and s.requiredParams > 0) or + sfCustomPragma in sym.flags: + markUsed(c.config, n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) - result = newSymNode(s, n.info) + result = symChoice(c, n, s, scClosed) else: result = semTemplateExpr(c, n, s, flags) of skParam: - markUsed(n.info, s, c.graph.usageSym) + markUsed(c.config, n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) if s.typ != nil and s.typ.kind == tyStatic and s.typ.n != nil: # XXX see the hack in sigmatch.nim ... @@ -957,19 +999,19 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = # 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 + internalAssert c.config, 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 + internalAssert c.config, 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: if s.magic == mNimvm: - localError(n.info, "illegal context for 'nimvm' magic") + localError(c.config, n.info, "illegal context for 'nimvm' magic") - markUsed(n.info, s, c.graph.usageSym) + markUsed(c.config, n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) result = newSymNode(s, n.info) # We cannot check for access to outer vars for example because it's still @@ -987,7 +1029,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = n.typ = s.typ return n of skType: - markUsed(n.info, s, c.graph.usageSym) + markUsed(c.config, n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) if s.typ.kind == tyStatic and s.typ.n != nil: return s.typ.n @@ -998,8 +1040,8 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = while p != nil and p.selfSym == nil: p = p.next if p != nil and p.selfSym != nil: - var ty = skipTypes(p.selfSym.typ, {tyGenericInst, tyVar, tyPtr, tyRef, - tyAlias}) + var ty = skipTypes(p.selfSym.typ, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, + tyAlias, tySink}) while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct}) var check: PNode = nil if ty.kind == tyObject: @@ -1009,7 +1051,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = if f != nil and fieldVisible(c, f): # is the access to a public field or in the same module or in a friend? doAssert f == s - markUsed(n.info, f, c.graph.usageSym) + markUsed(c.config, n.info, f, c.graph.usageSym) styleCheckUse(n.info, f) result = newNodeIT(nkDotExpr, n.info, f.typ) result.add makeDeref(newSymNode(p.selfSym)) @@ -1022,23 +1064,23 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = if ty.sons[0] == nil: break ty = skipTypes(ty.sons[0], skipPtrs) # old code, not sure if it's live code: - markUsed(n.info, s, c.graph.usageSym) + markUsed(c.config, n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) result = newSymNode(s, n.info) else: - markUsed(n.info, s, c.graph.usageSym) + markUsed(c.config, n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) result = newSymNode(s, n.info) proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = ## returns nil if it's not a built-in field access - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.config) # tests/bind/tbindoverload.nim wants an early exit here, but seems to # work without now. template/tsymchoicefield doesn't like an early exit # here at all! #if isSymChoice(n.sons[1]): return when defined(nimsuggest): - if gCmd == cmdIdeTools: + if c.config.cmd == cmdIdeTools: suggestExpr(c, n) if exactEquals(gTrackPos, n[1].info): suggestExprNoCheck(c, n) @@ -1048,14 +1090,14 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result = symChoice(c, n, s, scClosed) if result.kind == nkSym: result = semSym(c, n, s, flags) else: - markUsed(n.sons[1].info, s, c.graph.usageSym) + markUsed(c.config, n.sons[1].info, s, c.graph.usageSym) result = semSym(c, n, s, flags) styleCheckUse(n.sons[1].info, s) return n.sons[0] = semExprWithType(c, n.sons[0], flags+{efDetermineType}) #restoreOldStyleType(n.sons[0]) - var i = considerQuotedIdent(n.sons[1], n) + var i = considerQuotedIdent(c.config, n.sons[1], n) var ty = n.sons[0].typ var f: PSym = nil result = nil @@ -1063,21 +1105,43 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = template tryReadingGenericParam(t: PType) = case t.kind of tyTypeParamsHolders: - return readTypeParameter(c, t, i, n.info) + result = readTypeParameter(c, t, i, n.info) + if result == emptyNode: + result = n + n.typ = makeTypeFromExpr(c, n.copyTree) + return of tyUserTypeClasses: if t.isResolvedUserTypeClass: return readTypeParameter(c, t, i, n.info) else: n.typ = makeTypeFromExpr(c, copyTree(n)) return n - of tyGenericParam: + of tyGenericParam, tyAnything: n.typ = makeTypeFromExpr(c, copyTree(n)) return n else: discard - if isTypeExpr(n.sons[0]) or (ty.kind == tyTypeDesc and ty.base.kind != tyNone): - if ty.kind == tyTypeDesc: ty = ty.base + var argIsType = false + + if ty.kind == tyTypeDesc: + if ty.base.kind == tyNone: + # This is a still unresolved typedesc parameter. + # If this is a regular proc, then all bets are off and we must return + # tyFromExpr, but when this happen in a macro this is not a built-in + # field access and we leave the compiler to compile a normal call: + if getCurrOwner(c).kind != skMacro: + n.typ = makeTypeFromExpr(c, n.copyTree) + return n + else: + return nil + else: + ty = ty.base + argIsType = true + else: + argIsType = isTypeExpr(n.sons[0]) + + if argIsType: ty = ty.skipTypes(tyDotOpTransparent) case ty.kind of tyEnum: @@ -1090,7 +1154,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result = newSymNode(f) result.info = n.info result.typ = ty - markUsed(n.info, f, c.graph.usageSym) + markUsed(c.config, n.info, f, c.graph.usageSym) styleCheckUse(n.info, f) return of tyObject, tyTuple: @@ -1108,7 +1172,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = return nil if ty.kind in tyUserTypeClasses and ty.isResolvedUserTypeClass: ty = ty.lastSon - ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef, tyAlias}) + ty = skipTypes(ty, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink}) while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct}) var check: PNode = nil if ty.kind == tyObject: @@ -1121,7 +1185,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = if f != nil: if fieldVisible(c, f): # is the access to a public field or in the same module or in a friend? - markUsed(n.sons[1].info, f, c.graph.usageSym) + markUsed(c.config, n.sons[1].info, f, c.graph.usageSym) styleCheckUse(n.sons[1].info, f) n.sons[0] = makeDeref(n.sons[0]) n.sons[1] = newSymNode(f) # we now have the correct field @@ -1135,7 +1199,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = elif ty.kind == tyTuple and ty.n != nil: f = getSymFromList(ty.n, i) if f != nil: - markUsed(n.sons[1].info, f, c.graph.usageSym) + markUsed(c.config, n.sons[1].info, f, c.graph.usageSym) styleCheckUse(n.sons[1].info, f) n.sons[0] = makeDeref(n.sons[0]) n.sons[1] = newSymNode(f) @@ -1153,7 +1217,7 @@ proc dotTransformation(c: PContext, n: PNode): PNode = addSon(result, n.sons[1]) addSon(result, copyTree(n[0])) else: - var i = considerQuotedIdent(n.sons[1], n) + var i = considerQuotedIdent(c.config, n.sons[1], n) result = newNodeI(nkDotCall, n.info) result.flags.incl nfDotField addSon(result, newIdentNode(i, n[1].info)) @@ -1172,10 +1236,10 @@ proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode = for i in 0 .. n.len-1: result.add(n[i]) proc semDeref(c: PContext, n: PNode): PNode = - checkSonsLen(n, 1) + checkSonsLen(n, 1, c.config) n.sons[0] = semExprWithType(c, n.sons[0]) result = n - var t = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyAlias}) + var t = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyLent, tyAlias, tySink}) case t.kind of tyRef, tyPtr: n.typ = t.lastSon else: result = nil @@ -1190,12 +1254,12 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = result = newNodeIT(nkDerefExpr, x.info, x.typ) result.add(x[0]) return - checkMinSonsLen(n, 2) + checkMinSonsLen(n, 2, c.config) # make sure we don't evaluate generic macros/templates n.sons[0] = semExprWithType(c, n.sons[0], - {efNoProcvarCheck, efNoEvaluateGeneric}) + {efNoEvaluateGeneric}) let arr = skipTypes(n.sons[0].typ, {tyGenericInst, - tyVar, tyPtr, tyRef, tyAlias}) + tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink}) case arr.kind of tyArray, tyOpenArray, tyVarargs, tySequence, tyString, tyCString: @@ -1204,7 +1268,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = for i in countup(1, sonsLen(n) - 1): n.sons[i] = semExprWithType(c, n.sons[i], flags*{efInTypeof, efDetermineType}) - var indexType = if arr.kind == tyArray: arr.sons[0] else: getSysType(tyInt) + var indexType = if arr.kind == tyArray: arr.sons[0] else: getSysType(c.graph, n.info, tyInt) var arg = indexTypesMatch(c, indexType, n.sons[1].typ, n.sons[1]) if arg != nil: n.sons[1] = arg @@ -1223,11 +1287,11 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = n.sons[0] = makeDeref(n.sons[0]) # [] operator for tuples requires constant expression: n.sons[1] = semConstExpr(c, n.sons[1]) - if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal, tyAlias}).kind in + if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal, tyAlias, tySink}).kind in {tyInt..tyInt64}: var idx = getOrdValue(n.sons[1]) if idx >= 0 and idx < sonsLen(arr): n.typ = arr.sons[int(idx)] - else: localError(n.info, errInvalidIndexValueForTuple) + else: localError(c.config, n.info, "invalid index value for tuple subscript") result = n else: result = nil @@ -1267,7 +1331,7 @@ proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]")) proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = - var id = considerQuotedIdent(a[1], a) + var id = considerQuotedIdent(c.config, a[1], a) var setterId = newIdentNode(getIdent(id.s & '='), n.info) # a[0] is already checked for semantics, that does ``builtinFieldAccess`` # this is ugly. XXX Semantic checking should use the ``nfSem`` flag for @@ -1284,28 +1348,38 @@ proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = #fixAbstractType(c, result) #analyseIfAddressTakenInCall(c, result) -proc takeImplicitAddr(c: PContext, n: PNode): PNode = +proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode = + # See RFC #7373, calls returning 'var T' are assumed to + # return a view into the first argument (if there is one): + let root = exprRoot(n) + if root != nil and root.owner == c.p.owner: + if root.kind in {skLet, skVar, skTemp} and sfGlobal notin root.flags: + localError(c.config, n.info, "'$1' escapes its stack frame; context: '$2'; see $3/var_t_return.html" % [ + root.name.s, renderTree(n, {renderNoComments}), explanationsBaseUrl]) + elif root.kind == skParam and root.position != 0: + localError(c.config, n.info, "'$1' is not the first parameter; context: '$2'; see $3/var_t_return.html" % [ + root.name.s, renderTree(n, {renderNoComments}), explanationsBaseUrl]) case n.kind of nkHiddenAddr, nkAddr: return n of nkHiddenDeref, nkDerefExpr: return n.sons[0] of nkBracketExpr: if len(n) == 1: return n.sons[0] else: discard - var valid = isAssignable(c, n) + let valid = isAssignable(c, n) if valid != arLValue: if valid == arLocalLValue: - localError(n.info, errXStackEscape, renderTree(n, {renderNoComments})) - else: - localError(n.info, errExprHasNoAddress) + localError(c.config, n.info, errXStackEscape % renderTree(n, {renderNoComments})) + elif not isLent: + localError(c.config, n.info, errExprHasNoAddress) result = newNodeIT(nkHiddenAddr, n.info, makePtrType(c, n.typ)) result.add(n) proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = if le.kind == nkHiddenDeref: var x = le.sons[0] - if x.typ.kind == tyVar and x.kind == nkSym and x.sym.kind == skResult: + if x.typ.kind in {tyVar, tyLent} and x.kind == nkSym and x.sym.kind == skResult: n.sons[0] = x # 'result[]' --> 'result' - n.sons[1] = takeImplicitAddr(c, ri) + n.sons[1] = takeImplicitAddr(c, ri, x.typ.kind == tyLent) x.typ.flags.incl tfVarIsPtr #echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info @@ -1313,7 +1387,7 @@ template resultTypeIsInferrable(typ: PType): untyped = typ.isMetaType and typ.kind != tyTypeDesc proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.config) var a = n.sons[0] case a.kind of nkDotExpr: @@ -1348,7 +1422,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=") add(result, n[1]) return semExprNoType(c, result) - of nkPar: + of nkPar, nkTupleConstr: if a.len >= 2: # unfortunately we need to rewrite ``(x, y) = foo()`` already here so # that overloading of the assignment operator still works. Usually we @@ -1362,11 +1436,11 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = # a = b # both are vars, means: a[] = b[] # a = b # b no 'var T' means: a = addr(b) var le = a.typ - if (skipTypes(le, {tyGenericInst, tyAlias}).kind != tyVar and + if (skipTypes(le, {tyGenericInst, tyAlias, tySink}).kind != tyVar and isAssignable(c, a) == arNone) or skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs}: # Direct assignment to a discriminant is allowed! - localError(a.info, errXCannotBeAssignedTo, + localError(c.config, a.info, errXCannotBeAssignedTo % renderTree(a, {renderNoComments})) else: let @@ -1382,15 +1456,15 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = if rhsTyp.kind in tyUserTypeClasses and rhsTyp.isResolvedUserTypeClass: rhsTyp = rhsTyp.lastSon if cmpTypes(c, lhs.typ, rhsTyp) in {isGeneric, isEqual}: - internalAssert c.p.resultSym != nil + internalAssert c.config, c.p.resultSym != nil lhs.typ = rhsTyp c.p.resultSym.typ = rhsTyp c.p.owner.typ.sons[0] = rhsTyp else: - typeMismatch(n.info, lhs.typ, rhsTyp) + typeMismatch(c.config, n.info, lhs.typ, rhsTyp) n.sons[1] = fitNode(c, le, rhs, n.info) - if not newDestructors: + if destructor notin c.features: if tfHasAsgn in lhs.typ.flags and not lhsIsResult and mode != noOverloadedAsgn: return overloadedAsgn(c, lhs, n.sons[1]) @@ -1403,7 +1477,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = proc semReturn(c: PContext, n: PNode): PNode = result = n - checkSonsLen(n, 1) + checkSonsLen(n, 1, c.config) if c.p.owner.kind in {skConverter, skMethod, skProc, skFunc, skMacro} or ( c.p.owner.kind == skIterator and c.p.owner.typ.callConv == ccClosure): if n.sons[0].kind != nkEmpty: @@ -1417,9 +1491,9 @@ proc semReturn(c: PContext, n: PNode): PNode = if n[0][1].kind == nkSym and n[0][1].sym == c.p.resultSym: n.sons[0] = ast.emptyNode else: - localError(n.info, errNoReturnTypeDeclared) + localError(c.config, n.info, errNoReturnTypeDeclared) else: - localError(n.info, errXNotAllowedHere, "\'return\'") + localError(c.config, n.info, "'return' not allowed here") proc semProcBody(c: PContext, n: PNode): PNode = openScope(c) @@ -1434,7 +1508,7 @@ proc semProcBody(c: PContext, n: PNode): PNode = # nil # # comment # are not expressions: - fixNilType(result) + fixNilType(c, result) else: var a = newNodeI(nkAsgn, n.info, 2) a.sons[0] = newSymNode(c.p.resultSym) @@ -1450,40 +1524,40 @@ proc semProcBody(c: PContext, n: PNode): PNode = c.p.resultSym.typ = errorType(c) c.p.owner.typ.sons[0] = nil else: - localError(c.p.resultSym.info, errCannotInferReturnType) + localError(c.config, c.p.resultSym.info, errCannotInferReturnType) closeScope(c) proc semYieldVarResult(c: PContext, n: PNode, restype: PType) = - var t = skipTypes(restype, {tyGenericInst, tyAlias}) + var t = skipTypes(restype, {tyGenericInst, tyAlias, tySink}) case t.kind - of tyVar: - t.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892 + of tyVar, tyLent: + if t.kind == tyVar: t.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892 if n.sons[0].kind in {nkHiddenStdConv, nkHiddenSubConv}: n.sons[0] = n.sons[0].sons[1] - n.sons[0] = takeImplicitAddr(c, n.sons[0]) + n.sons[0] = takeImplicitAddr(c, n.sons[0], t.kind == tyLent) of tyTuple: for i in 0..<t.sonsLen: - var e = skipTypes(t.sons[i], {tyGenericInst, tyAlias}) - if e.kind == tyVar: - e.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892 - if n.sons[0].kind == nkPar: - n.sons[0].sons[i] = takeImplicitAddr(c, n.sons[0].sons[i]) + var e = skipTypes(t.sons[i], {tyGenericInst, tyAlias, tySink}) + if e.kind in {tyVar, tyLent}: + if e.kind == tyVar: e.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892 + if n.sons[0].kind in {nkPar, nkTupleConstr}: + n.sons[0].sons[i] = takeImplicitAddr(c, n.sons[0].sons[i], e.kind == tyLent) elif n.sons[0].kind in {nkHiddenStdConv, nkHiddenSubConv} and - n.sons[0].sons[1].kind == nkPar: + n.sons[0].sons[1].kind in {nkPar, nkTupleConstr}: var a = n.sons[0].sons[1] - a.sons[i] = takeImplicitAddr(c, a.sons[i]) + a.sons[i] = takeImplicitAddr(c, a.sons[i], false) else: - localError(n.sons[0].info, errXExpected, "tuple constructor") + localError(c.config, n.sons[0].info, errXExpected, "tuple constructor") else: discard proc semYield(c: PContext, n: PNode): PNode = result = n - checkSonsLen(n, 1) + checkSonsLen(n, 1, c.config) if c.p.owner == nil or c.p.owner.kind != skIterator: - localError(n.info, errYieldNotAllowedHere) - elif c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline: - localError(n.info, errYieldNotAllowedInTryStmt) + localError(c.config, n.info, errYieldNotAllowedHere) + elif oldIterTransf in c.features and c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline: + localError(c.config, n.info, errYieldNotAllowedInTryStmt) elif n.sons[0].kind != nkEmpty: n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility: var iterType = c.p.owner.typ @@ -1491,7 +1565,7 @@ proc semYield(c: PContext, n: PNode): PNode = if restype != nil: if restype.kind != tyExpr: n.sons[0] = fitNode(c, restype, n.sons[0], n.info) - if n.sons[0].typ == nil: internalError(n.info, "semYield") + if n.sons[0].typ == nil: internalError(c.config, n.info, "semYield") if resultTypeIsInferrable(restype): let inferred = n.sons[0].typ @@ -1499,9 +1573,9 @@ proc semYield(c: PContext, n: PNode): PNode = semYieldVarResult(c, n, restype) else: - localError(n.info, errCannotReturnExpr) + localError(c.config, n.info, errCannotReturnExpr) elif c.p.owner.typ.sons[0] != nil: - localError(n.info, errGenerated, "yield statement must yield a value") + localError(c.config, n.info, errGenerated, "yield statement must yield a value") proc lookUpForDefined(c: PContext, i: PIdent, onlyCurrentScope: bool): PSym = if onlyCurrentScope: @@ -1516,37 +1590,37 @@ proc lookUpForDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PSym = of nkDotExpr: result = nil if onlyCurrentScope: return - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.config) var m = lookUpForDefined(c, n.sons[0], onlyCurrentScope) if m != nil and m.kind == skModule: - let ident = considerQuotedIdent(n[1], n) + let ident = considerQuotedIdent(c.config, n[1], n) if m == c.module: result = strTableGet(c.topLevelScope.symbols, ident) else: result = strTableGet(m.tab, ident) of nkAccQuoted: - result = lookUpForDefined(c, considerQuotedIdent(n), onlyCurrentScope) + result = lookUpForDefined(c, considerQuotedIdent(c.config, n), onlyCurrentScope) of nkSym: result = n.sym of nkOpenSymChoice, nkClosedSymChoice: result = n.sons[0].sym else: - localError(n.info, errIdentifierExpected, renderTree(n)) + localError(c.config, n.info, "identifier expected, but got: " & renderTree(n)) result = nil proc semDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PNode = - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.config) # we replace this node by a 'true' or 'false' node: result = newIntNode(nkIntLit, 0) - if not onlyCurrentScope and considerQuotedIdent(n[0], n).s == "defined": + if not onlyCurrentScope and considerQuotedIdent(c.config, n[0], n).s == "defined": if n.sons[1].kind != nkIdent: - localError(n.info, "obsolete usage of 'defined', use 'declared' instead") - elif condsyms.isDefined(n.sons[1].ident): + localError(c.config, n.info, "obsolete usage of 'defined', use 'declared' instead") + elif isDefined(c.config, n.sons[1].ident.s): result.intVal = 1 elif lookUpForDefined(c, n.sons[1], onlyCurrentScope) != nil: result.intVal = 1 result.info = n.info - result.typ = getSysType(tyBool) + result.typ = getSysType(c.graph, n.info, tyBool) proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym = ## The argument to the proc should be nkCall(...) or similar @@ -1558,12 +1632,12 @@ proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym = return errorSym(c, n[0]) if expandedSym.kind notin {skMacro, skTemplate}: - localError(n.info, errXisNoMacroOrTemplate, expandedSym.name.s) + localError(c.config, n.info, "'$1' is not a macro or template" % expandedSym.name.s) return errorSym(c, n[0]) result = expandedSym else: - localError(n.info, errXisNoMacroOrTemplate, n.renderTree) + localError(c.config, n.info, "'$1' is not a macro or template" % n.renderTree) result = errorSym(c, n) proc expectString(c: PContext, n: PNode): string = @@ -1571,11 +1645,7 @@ proc expectString(c: PContext, n: PNode): string = if n.kind in nkStrKinds: return n.strVal else: - localError(n.info, errStringLiteralExpected) - -proc getMagicSym(magic: TMagic): PSym = - result = newSym(skProc, getIdent($magic), systemModule, gCodegenLineInfo) - result.magic = magic + localError(c.config, n.info, errStringLiteralExpected) proc newAnonSym(c: PContext; kind: TSymKind, info: TLineInfo): PSym = result = newSym(kind, c.cache.idAnon, getCurrOwner(c), info) @@ -1589,7 +1659,7 @@ proc semExpandToAst(c: PContext, n: PNode): PNode = if expandedSym.kind == skError: return n macroCall.sons[0] = newSymNode(expandedSym, macroCall.info) - markUsed(n.info, expandedSym, c.graph.usageSym) + markUsed(c.config, n.info, expandedSym, c.graph.usageSym) styleCheckUse(n.info, expandedSym) if isCallExpr(macroCall): @@ -1608,26 +1678,25 @@ proc semExpandToAst(c: PContext, n: PNode): PNode = inc cands symx = nextOverloadIter(o, c, headSymbol) if cands == 0: - localError(n.info, "expected a template that takes " & $(macroCall.len-1) & " arguments") + localError(c.config, n.info, "expected a template that takes " & $(macroCall.len-1) & " arguments") elif cands >= 2: - localError(n.info, "ambiguous symbol in 'getAst' context: " & $macroCall) + localError(c.config, n.info, "ambiguous symbol in 'getAst' context: " & $macroCall) else: let info = macroCall.sons[0].info macroCall.sons[0] = newSymNode(cand, info) - markUsed(info, cand, c.graph.usageSym) + markUsed(c.config, info, cand, c.graph.usageSym) styleCheckUse(info, cand) # we just perform overloading resolution here: #n.sons[1] = semOverloadedCall(c, macroCall, macroCall, {skTemplate, skMacro}) else: - localError(n.info, "getAst takes a call, but got " & n.renderTree) + localError(c.config, n.info, "getAst takes a call, but got " & n.renderTree) # Preserve the magic symbol in order to be handled in evals.nim - internalAssert n.sons[0].sym.magic == mExpandToAst + internalAssert c.config, n.sons[0].sym.magic == mExpandToAst #n.typ = getSysSym("NimNode").typ # expandedSym.getReturnType if n.kind == nkStmtList and n.len == 1: result = n[0] else: result = n - result.typ = if getCompilerProc("NimNode") != nil: sysTypeFromName"NimNode" - else: sysTypeFromName"PNimrodNode" + result.typ = sysTypeFromName(c.graph, n.info, "NimNode") proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym, flags: TExprFlags = {}): PNode = @@ -1637,7 +1706,7 @@ proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym, else: result = semDirectOp(c, n, flags) -proc processQuotations(n: var PNode, op: string, +proc processQuotations(c: PContext; n: var PNode, op: string, quotes: var seq[PNode], ids: var seq[PNode]) = template returnQuote(q) = @@ -1647,7 +1716,7 @@ proc processQuotations(n: var PNode, op: string, return if n.kind == nkPrefix: - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.config) if n[0].kind == nkIdent: var examinedOp = n[0].ident.s if examinedOp == op: @@ -1658,10 +1727,10 @@ proc processQuotations(n: var PNode, op: string, returnQuote n[0] for i in 0 ..< n.safeLen: - processQuotations(n.sons[i], op, quotes, ids) + processQuotations(c, n.sons[i], op, quotes, ids) proc semQuoteAst(c: PContext, n: PNode): PNode = - internalAssert n.len == 2 or n.len == 3 + internalAssert c.config, n.len == 2 or n.len == 3 # We transform the do block into a template with a param for # each interpolation. We'll pass this template to getAst. var @@ -1674,9 +1743,9 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = # this will store the generated param names if quotedBlock.kind != nkStmtList: - localError(n.info, errXExpected, "block") + localError(c.config, n.info, errXExpected, "block") - processQuotations(quotedBlock, op, quotes, ids) + processQuotations(c, quotedBlock, op, quotes, ids) var dummyTemplate = newProcNode( nkTemplateDef, quotedBlock.info, quotedBlock, @@ -1684,27 +1753,27 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = if ids.len > 0: dummyTemplate.sons[paramsPos] = newNodeI(nkFormalParams, n.info) - dummyTemplate[paramsPos].add getSysSym("typed").newSymNode # return type - ids.add getSysSym("untyped").newSymNode # params type + dummyTemplate[paramsPos].add getSysSym(c.graph, n.info, "typed").newSymNode # return type + ids.add getSysSym(c.graph, n.info, "untyped").newSymNode # params type ids.add emptyNode # no default value dummyTemplate[paramsPos].add newNode(nkIdentDefs, n.info, ids) var tmpl = semTemplateDef(c, dummyTemplate) quotes[0] = tmpl[namePos] result = newNode(nkCall, n.info, @[ - getMagicSym(mExpandToAst).newSymNode, + createMagic(c.graph, "getAst", mExpandToAst).newSymNode, newNode(nkCall, n.info, quotes)]) result = semExpandToAst(c, result) proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # watch out, hacks ahead: - let oldErrorCount = msgs.gErrorCounter - let oldErrorMax = msgs.gErrorMax + let oldErrorCount = c.config.errorCounter + let oldErrorMax = c.config.errorMax let oldCompilesId = c.compilesContextId inc c.compilesContextIdGenerator c.compilesContextId = c.compilesContextIdGenerator # do not halt after first error: - msgs.gErrorMax = high(int) + c.config.errorMax = high(int) # open a scope for temporary symbol inclusions: let oldScope = c.currentScope @@ -1718,12 +1787,13 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = let oldInGenericContext = c.inGenericContext let oldInUnrolledContext = c.inUnrolledContext let oldInGenericInst = c.inGenericInst + let oldInStaticContext = c.inStaticContext let oldProcCon = c.p c.generics = @[] var err: string try: result = semExpr(c, n, flags) - if msgs.gErrorCounter != oldErrorCount: result = nil + if c.config.errorCounter != oldErrorCount: result = nil except ERecoverableError: discard # undo symbol table changes (as far as it's possible): @@ -1732,13 +1802,14 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = c.inGenericContext = oldInGenericContext c.inUnrolledContext = oldInUnrolledContext c.inGenericInst = oldInGenericInst + c.inStaticContext = oldInStaticContext c.p = oldProcCon msgs.setInfoContextLen(oldContextLen) setLen(c.graph.owners, oldOwnerLen) c.currentScope = oldScope errorOutputs = oldErrorOutputs - msgs.gErrorCounter = oldErrorCount - msgs.gErrorMax = oldErrorMax + c.config.errorCounter = oldErrorCount + c.config.errorMax = oldErrorMax proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode = # we replace this node by a 'true' or 'false' node: @@ -1746,7 +1817,7 @@ proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode = result = newIntNode(nkIntLit, ord(tryExpr(c, n[1], flags) != nil)) result.info = n.info - result.typ = getSysType(tyBool) + result.typ = getSysType(c.graph, n.info, tyBool) proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode = if sonsLen(n) == 3: @@ -1761,15 +1832,15 @@ proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode = proc createFlowVar(c: PContext; t: PType; info: TLineInfo): PType = result = newType(tyGenericInvocation, c.module) - addSonSkipIntLit(result, magicsys.getCompilerProc("FlowVar").typ) + addSonSkipIntLit(result, magicsys.getCompilerProc(c.graph, "FlowVar").typ) addSonSkipIntLit(result, t) result = instGenericContainer(c, info, result, allowMetaTypes = false) proc instantiateCreateFlowVarCall(c: PContext; t: PType; info: TLineInfo): PSym = - let sym = magicsys.getCompilerProc("nimCreateFlowVar") + let sym = magicsys.getCompilerProc(c.graph, "nimCreateFlowVar") if sym == nil: - localError(info, errSystemNeeds, "nimCreateFlowVar") + localError(c.config, info, "system needs: nimCreateFlowVar") var bindings: TIdTable initIdTable(bindings) bindings.idTablePut(sym.ast[genericParamsPos].sons[0].typ, t) @@ -1798,10 +1869,10 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = result = n case s.magic # magics that need special treatment of mAddr: - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.config) result = semAddr(c, n.sons[1], s.name.s == "unsafeAddr") of mTypeOf: - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.config) result = semTypeOf(c, n.sons[1]) #of mArrGet: result = semArrGet(c, n, flags) #of mArrPut: result = semArrPut(c, n, flags) @@ -1818,12 +1889,12 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = of mExpandToAst: result = semExpandToAst(c, n, s, flags) of mQuoteAst: result = semQuoteAst(c, n) of mAstToStr: - checkSonsLen(n, 2) - result = newStrNodeT(renderTree(n[1], {renderNoComments}), n) - result.typ = getSysType(tyString) + checkSonsLen(n, 2, c.config) + result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, c.graph) + result.typ = getSysType(c.graph, n.info, tyString) of mParallel: - if not experimentalMode(c): - localError(n.info, "use the {.experimental.} pragma to enable 'parallel'") + if parallel notin c.features: + localError(c.config, n.info, "use the {.experimental.} pragma to enable 'parallel'") result = setMs(n, s) var x = n.lastSon if x.kind == nkDo: x = x.sons[bodyPos] @@ -1864,18 +1935,17 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = if callee.magic != mNone: result = magicsAfterOverloadResolution(c, result, flags) of mRunnableExamples: - if gCmd == cmdDoc and n.len >= 2 and n.lastSon.kind == nkStmtList: - if n.sons[0].kind == nkIdent: - if sfMainModule in c.module.flags: - let inp = toFullPath(c.module.info) - if c.runnableExamples == nil: - c.runnableExamples = newTree(nkStmtList, - newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp)))) - let imports = newTree(nkStmtList) - extractImports(n.lastSon, imports) - for imp in imports: c.runnableExamples.add imp - c.runnableExamples.add newTree(nkBlockStmt, emptyNode, copyTree n.lastSon) - result = setMs(n, s) + if c.config.cmd == cmdDoc and n.len >= 2 and n.lastSon.kind == nkStmtList: + if sfMainModule in c.module.flags: + let inp = toFullPath(c.module.info) + if c.runnableExamples == nil: + c.runnableExamples = newTree(nkStmtList, + newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp)))) + let imports = newTree(nkStmtList) + extractImports(n.lastSon, imports) + for imp in imports: c.runnableExamples.add imp + c.runnableExamples.add newTree(nkBlockStmt, emptyNode, copyTree n.lastSon) + result = setMs(n, s) else: result = emptyNode else: @@ -1910,7 +1980,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = var it = n.sons[i] case it.kind of nkElifBranch, nkElifExpr: - checkSonsLen(it, 2) + checkSonsLen(it, 2, c.config) if whenNimvm: if semCheck: it.sons[1] = semExpr(c, it.sons[1]) @@ -1925,14 +1995,14 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = elif e.intVal != 0 and result == nil: setResult(it.sons[1]) of nkElse, nkElseExpr: - checkSonsLen(it, 1) + checkSonsLen(it, 1, c.config) if result == nil or whenNimvm: if semCheck: it.sons[0] = semExpr(c, it.sons[0]) typ = commonType(typ, it.sons[0].typ) if result == nil: result = it.sons[0] - else: illFormedAst(n) + else: illFormedAst(n, c.config) if result == nil: result = newNodeI(nkEmpty, n.info) if whenNimvm: result.typ = typ @@ -1951,24 +2021,24 @@ proc semSetConstr(c: PContext, n: PNode): PNode = var typ: PType = nil for i in countup(0, sonsLen(n) - 1): if isRange(n.sons[i]): - checkSonsLen(n.sons[i], 3) + checkSonsLen(n.sons[i], 3, c.config) n.sons[i].sons[1] = semExprWithType(c, n.sons[i].sons[1]) n.sons[i].sons[2] = semExprWithType(c, n.sons[i].sons[2]) if typ == nil: typ = skipTypes(n.sons[i].sons[1].typ, - {tyGenericInst, tyVar, tyOrdinal, tyAlias}) + {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink}) n.sons[i].typ = n.sons[i].sons[2].typ # range node needs type too elif n.sons[i].kind == nkRange: # already semchecked if typ == nil: typ = skipTypes(n.sons[i].sons[0].typ, - {tyGenericInst, tyVar, tyOrdinal, tyAlias}) + {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink}) else: n.sons[i] = semExprWithType(c, n.sons[i]) if typ == nil: - typ = skipTypes(n.sons[i].typ, {tyGenericInst, tyVar, tyOrdinal, tyAlias}) + typ = skipTypes(n.sons[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink}) if not isOrdinalType(typ): - localError(n.info, errOrdinalTypeExpected) + localError(c.config, n.info, errOrdinalTypeExpected) typ = makeRangeType(c, 0, MaxSetElements-1, n.info) elif lengthOrd(typ) > MaxSetElements: typ = makeRangeType(c, 0, MaxSetElements-1, n.info) @@ -1994,31 +2064,32 @@ proc semTableConstr(c: PContext, n: PNode): PNode = var x = n.sons[i] if x.kind == nkExprColonExpr and sonsLen(x) == 2: for j in countup(lastKey, i-1): - var pair = newNodeI(nkPar, x.info) + var pair = newNodeI(nkTupleConstr, x.info) pair.add(n.sons[j]) pair.add(x[1]) result.add(pair) - var pair = newNodeI(nkPar, x.info) + var pair = newNodeI(nkTupleConstr, x.info) pair.add(x[0]) pair.add(x[1]) result.add(pair) lastKey = i+1 - if lastKey != n.len: illFormedAst(n) + if lastKey != n.len: illFormedAst(n, c.config) result = semExpr(c, result) type TParKind = enum paNone, paSingle, paTupleFields, paTuplePositions -proc checkPar(n: PNode): TParKind = +proc checkPar(c: PContext; n: PNode): TParKind = var length = sonsLen(n) if length == 0: result = paTuplePositions # () elif length == 1: if n.sons[0].kind == nkExprColonExpr: result = paTupleFields + elif n.kind == nkTupleConstr: result = paTuplePositions else: result = paSingle # (expr) else: if n.sons[0].kind == nkExprColonExpr: result = paTupleFields @@ -2027,26 +2098,26 @@ proc checkPar(n: PNode): TParKind = if result == paTupleFields: if (n.sons[i].kind != nkExprColonExpr) or not (n.sons[i].sons[0].kind in {nkSym, nkIdent}): - localError(n.sons[i].info, errNamedExprExpected) + localError(c.config, n.sons[i].info, errNamedExprExpected) return paNone else: if n.sons[i].kind == nkExprColonExpr: - localError(n.sons[i].info, errNamedExprNotAllowed) + localError(c.config, n.sons[i].info, errNamedExprNotAllowed) return paNone proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = - result = newNodeI(nkPar, n.info) + result = newNodeI(nkTupleConstr, n.info) var typ = newTypeS(tyTuple, c) typ.n = newNodeI(nkRecList, n.info) # nkIdentDefs var ids = initIntSet() for i in countup(0, sonsLen(n) - 1): if n[i].kind != nkExprColonExpr or n[i][0].kind notin {nkSym, nkIdent}: - illFormedAst(n.sons[i]) + illFormedAst(n.sons[i], c.config) var id: PIdent if n.sons[i].sons[0].kind == nkIdent: id = n.sons[i].sons[0].ident else: id = n.sons[i].sons[0].sym.name if containsOrIncl(ids, id.id): - localError(n.sons[i].info, errFieldInitTwice, id.s) + localError(c.config, n.sons[i].info, errFieldInitTwice % id.s) n.sons[i].sons[1] = semExprWithType(c, n.sons[i].sons[1], flags*{efAllowDestructor}) var f = newSymS(skField, n.sons[i].sons[0], c) @@ -2060,6 +2131,7 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = result = n # we don't modify n, but compute the type: + result.kind = nkTupleConstr var typ = newTypeS(tyTuple, c) # leave typ.n nil! for i in countup(0, sonsLen(n) - 1): n.sons[i] = semExprWithType(c, n.sons[i], flags*{efAllowDestructor}) @@ -2079,14 +2151,14 @@ include semobjconstr proc semBlock(c: PContext, n: PNode): PNode = result = n inc(c.p.nestedBlockCounter) - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.config) openScope(c) # BUGFIX: label is in the scope of block! if n.sons[0].kind != nkEmpty: var labl = newSymG(skLabel, n.sons[0], c) if sfGenSym notin labl.flags: addDecl(c, labl) n.sons[0] = newSymNode(labl, n.sons[0].info) - suggestSym(n.sons[0].info, labl, c.graph.usageSym) + suggestSym(c.config, n.sons[0].info, labl, c.graph.usageSym) styleCheckDef(labl) n.sons[1] = semExpr(c, n.sons[1]) n.typ = n.sons[1].typ @@ -2095,15 +2167,31 @@ proc semBlock(c: PContext, n: PNode): PNode = closeScope(c) dec(c.p.nestedBlockCounter) +proc semExportExcept(c: PContext, n: PNode): PNode = + let moduleName = semExpr(c, n[0]) + if moduleName.kind != nkSym or moduleName.sym.kind != skModule: + localError(c.config, n.info, "The export/except syntax expects a module name") + return n + let exceptSet = readExceptSet(c, n) + let exported = moduleName.sym + strTableAdd(c.module.tab, exported) + var i: TTabIter + var s = initTabIter(i, exported.tab) + while s != nil: + if s.kind in ExportableSymKinds+{skModule} and + s.name.id notin exceptSet: + strTableAdd(c.module.tab, s) + s = nextIter(i, exported.tab) + result = n + proc semExport(c: PContext, n: PNode): PNode = var x = newNodeI(n.kind, n.info) - #let L = if n.kind == nkExportExceptStmt: L = 1 else: n.len for i in 0..<n.len: let a = n.sons[i] var o: TOverloadIter var s = initOverloadIter(o, c, a) if s == nil: - localError(a.info, errGenerated, "cannot export: " & renderTree(a)) + localError(c.config, a.info, errGenerated, "cannot export: " & renderTree(a)) elif s.kind == skModule: # forward everything from that module: strTableAdd(c.module.tab, s) @@ -2137,7 +2225,7 @@ proc shouldBeBracketExpr(n: PNode): bool = proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = n - if gCmd == cmdIdeTools: suggestExpr(c, n) + if c.config.cmd == cmdIdeTools: suggestExpr(c, n) if nfSem in n.flags: return case n.kind of nkIdent, nkAccQuoted: @@ -2156,54 +2244,54 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = if result.kind == nkSym: markIndirect(c, result.sym) # if isGenericRoutine(result.sym): - # localError(n.info, errInstantiateXExplicitly, s.name.s) + # localError(c.config, n.info, errInstantiateXExplicitly, s.name.s) of nkSym: # because of the changed symbol binding, this does not mean that we # don't have to check the symbol for semantics here again! result = semSym(c, n, n.sym, flags) - of nkEmpty, nkNone, nkCommentStmt: + of nkEmpty, nkNone, nkCommentStmt, nkType: discard of nkNilLit: - if result.typ == nil: result.typ = getSysType(tyNil) + if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyNil) of nkIntLit: - if result.typ == nil: setIntLitType(result) + if result.typ == nil: setIntLitType(c.graph, result) of nkInt8Lit: - if result.typ == nil: result.typ = getSysType(tyInt8) + if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt8) of nkInt16Lit: - if result.typ == nil: result.typ = getSysType(tyInt16) + if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt16) of nkInt32Lit: - if result.typ == nil: result.typ = getSysType(tyInt32) + if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt32) of nkInt64Lit: - if result.typ == nil: result.typ = getSysType(tyInt64) + if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt64) of nkUIntLit: - if result.typ == nil: result.typ = getSysType(tyUInt) + if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt) of nkUInt8Lit: - if result.typ == nil: result.typ = getSysType(tyUInt8) + if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt8) of nkUInt16Lit: - if result.typ == nil: result.typ = getSysType(tyUInt16) + if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt16) of nkUInt32Lit: - if result.typ == nil: result.typ = getSysType(tyUInt32) + if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt32) of nkUInt64Lit: - if result.typ == nil: result.typ = getSysType(tyUInt64) + if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt64) #of nkFloatLit: # if result.typ == nil: result.typ = getFloatLitType(result) of nkFloat32Lit: - if result.typ == nil: result.typ = getSysType(tyFloat32) + if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyFloat32) of nkFloat64Lit, nkFloatLit: - if result.typ == nil: result.typ = getSysType(tyFloat64) + if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyFloat64) of nkFloat128Lit: - if result.typ == nil: result.typ = getSysType(tyFloat128) + if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyFloat128) of nkStrLit..nkTripleStrLit: - if result.typ == nil: result.typ = getSysType(tyString) + if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyString) of nkCharLit: - if result.typ == nil: result.typ = getSysType(tyChar) + if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyChar) of nkDotExpr: result = semFieldAccess(c, n, flags) if result.kind == nkDotCall: result.kind = nkCall result = semExpr(c, result, flags) of nkBind: - message(n.info, warnDeprecated, "bind") + message(c.config, n.info, warnDeprecated, "bind") result = semExpr(c, n.sons[0], flags) of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy: if c.matchedConcept != nil and n.len == 1: @@ -2216,13 +2304,13 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result.typ = makeTypeDesc(c, typ) of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: # check if it is an expression macro: - checkMinSonsLen(n, 1) + checkMinSonsLen(n, 1, c.config) #when defined(nimsuggest): # if gIdeCmd == ideCon and gTrackPos == n.info: suggestExprNoCheck(c, n) let mode = if nfDotField in n.flags: {} else: {checkUndeclared} var s = qualifiedLookUp(c, n.sons[0], mode) if s != nil: - #if gCmd == cmdPretty and n.sons[0].kind == nkDotExpr: + #if c.config.cmd == cmdPretty and n.sons[0].kind == nkDotExpr: # pretty.checkUse(n.sons[0].sons[1].info, s) case s.kind of skMacro: @@ -2272,7 +2360,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = else: result = semExpr(c, result, flags) of nkBracketExpr: - checkMinSonsLen(n, 1) + checkMinSonsLen(n, 1, c.config) result = semArrayAccess(c, n, flags) of nkCurlyExpr: result = semExpr(c, buildOverloadedSubscripts(n, getIdent"{}"), flags) @@ -2280,7 +2368,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = var expr = n[0] pragma = n[1] - pragmaName = considerQuotedIdent(pragma[0]) + pragmaName = considerQuotedIdent(c.config, pragma[0]) flags = flags case whichKeyword(pragmaName) @@ -2288,11 +2376,11 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = flags.incl efExplain else: # what other pragmas are allowed for expressions? `likely`, `unlikely` - invalidPragma(n) + invalidPragma(c, n) result = semExpr(c, n[0], flags) - of nkPar: - case checkPar(n) + of nkPar, nkTupleConstr: + case checkPar(c, n) of paNone: result = errorNode(c, n) of paTuplePositions: var tupexp = semTuplePositionsConstr(c, n, flags) @@ -2311,24 +2399,24 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkDerefExpr: result = semDeref(c, n) of nkAddr: result = n - checkSonsLen(n, 1) + checkSonsLen(n, 1, c.config) result = semAddr(c, n.sons[0]) of nkHiddenAddr, nkHiddenDeref: - checkSonsLen(n, 1) + checkSonsLen(n, 1, c.config) n.sons[0] = semExpr(c, n.sons[0], flags) of nkCast: result = semCast(c, n) of nkIfExpr, nkIfStmt: result = semIf(c, n) of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv: - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.config) considerGenSyms(c, n) of nkStringToCString, nkCStringToString, nkObjDownConv, nkObjUpConv: - checkSonsLen(n, 1) + checkSonsLen(n, 1, c.config) considerGenSyms(c, n) of nkChckRangeF, nkChckRange64, nkChckRange: - checkSonsLen(n, 3) + checkSonsLen(n, 3, c.config) considerGenSyms(c, n) of nkCheckedFieldExpr: - checkMinSonsLen(n, 2) + checkMinSonsLen(n, 2, c.config) considerGenSyms(c, n) of nkTableConstr: result = semTableConstr(c, n) @@ -2365,20 +2453,30 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkMacroDef: result = semMacroDef(c, n) of nkTemplateDef: result = semTemplateDef(c, n) of nkImportStmt: - if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "import") + # this particular way allows 'import' in a 'compiles' context so that + # template canImport(x): bool = + # compiles: + # import x + # + # works: + if c.currentScope.depthLevel > 2 + c.compilesContextId: + localError(c.config, n.info, errXOnlyAtModuleScope % "import") result = evalImport(c, n) of nkImportExceptStmt: - if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "import") + if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "import") result = evalImportExcept(c, n) of nkFromStmt: - if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "from") + if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "from") result = evalFrom(c, n) of nkIncludeStmt: - #if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "include") + #if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "include") result = evalInclude(c, n) - of nkExportStmt, nkExportExceptStmt: - if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "export") + of nkExportStmt: + if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "export") result = semExport(c, n) + of nkExportExceptStmt: + if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "export") + result = semExportExcept(c, n) of nkPragmaBlock: result = semPragmaBlock(c, n) of nkStaticStmt: @@ -2386,14 +2484,14 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkDefer: n.sons[0] = semExpr(c, n.sons[0]) if not n.sons[0].typ.isEmptyType and not implicitlyDiscardable(n.sons[0]): - localError(n.info, errGenerated, "'defer' takes a 'void' expression") - #localError(n.info, errGenerated, "'defer' not allowed in this context") + localError(c.config, n.info, "'defer' takes a 'void' expression") + #localError(c.config, n.info, errGenerated, "'defer' not allowed in this context") of nkGotoState, nkState: - if n.len != 1 and n.len != 2: illFormedAst(n) + if n.len != 1 and n.len != 2: illFormedAst(n, c.config) for i in 0 ..< n.len: n.sons[i] = semExpr(c, n.sons[i]) of nkComesFrom: discard "ignore the comes from information for now" else: - localError(n.info, errInvalidExpressionX, + localError(c.config, n.info, "invalid expression: " & renderTree(n, {renderNoComments})) if result != nil: incl(result.flags, nfSem) diff --git a/compiler/semfields.nim b/compiler/semfields.nim index c5bc07d77..16d79c559 100644 --- a/compiler/semfields.nim +++ b/compiler/semfields.nim @@ -16,16 +16,17 @@ type tupleIndex: int field: PSym replaceByFieldName: bool + c: PContext proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode = case n.kind of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = n of nkIdent, nkSym: result = n - let ident = considerQuotedIdent(n) + let ident = considerQuotedIdent(c.c.config, n) var L = sonsLen(forLoop) if c.replaceByFieldName: - if ident.id == considerQuotedIdent(forLoop[0]).id: + if ident.id == considerQuotedIdent(c.c.config, forLoop[0]).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 @@ -33,7 +34,7 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode = return # other fields: for i in ord(c.replaceByFieldName)..L-3: - if ident.id == considerQuotedIdent(forLoop[i]).id: + if ident.id == considerQuotedIdent(c.c.config, forLoop[i]).id: var call = forLoop.sons[L-2] var tupl = call.sons[i+1-ord(c.replaceByFieldName)] if c.field.isNil: @@ -47,7 +48,7 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode = break else: if n.kind == nkContinueStmt: - localError(n.info, errGenerated, + localError(c.c.config, n.info, "'continue' not supported in a 'fields' loop") result = copyNode(n) newSons(result, sonsLen(n)) @@ -63,6 +64,7 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) = case typ.kind of nkSym: var fc: TFieldInstCtx # either 'tup[i]' or 'field' is valid + fc.c = c.c fc.field = typ.sym fc.replaceByFieldName = c.m == mFieldPairs openScope(c.c) @@ -76,7 +78,7 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) = let L = forLoop.len let call = forLoop.sons[L-2] if call.len > 2: - localError(forLoop.info, errGenerated, + localError(c.c.config, forLoop.info, "parallel 'fields' iterator does not work for 'case' objects") return # iterate over the selector: @@ -99,17 +101,17 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) = of nkRecList: for t in items(typ): semForObjectFields(c, t, forLoop, father) else: - illFormedAstLocal(typ) + illFormedAstLocal(typ, c.c.config) proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = # so that 'break' etc. work as expected, we produce # a 'while true: stmt; break' loop ... result = newNodeI(nkWhileStmt, n.info, 2) - var trueSymbol = strTableGet(magicsys.systemModule.tab, getIdent"true") + var trueSymbol = strTableGet(c.graph.systemModule.tab, getIdent"true") if trueSymbol == nil: - localError(n.info, errSystemNeeds, "true") + localError(c.config, n.info, "system needs: 'true'") trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(c), n.info) - trueSymbol.typ = getSysType(tyBool) + trueSymbol.typ = getSysType(c.graph, n.info, tyBool) result.sons[0] = newSymNode(trueSymbol, n.info) var stmts = newNodeI(nkStmtList, n.info) @@ -118,18 +120,18 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = var length = sonsLen(n) var call = n.sons[length-2] if length-2 != sonsLen(call)-1 + ord(m==mFieldPairs): - localError(n.info, errWrongNumberOfVariables) + localError(c.config, n.info, errWrongNumberOfVariables) return result const skippedTypesForFields = abstractVar - {tyTypeDesc} + tyUserTypeClasses var tupleTypeA = skipTypes(call.sons[1].typ, skippedTypesForFields) if tupleTypeA.kind notin {tyTuple, tyObject}: - localError(n.info, errGenerated, "no object or tuple type") + localError(c.config, n.info, errGenerated, "no object or tuple type") return result for i in 1..call.len-1: var tupleTypeB = skipTypes(call.sons[i].typ, skippedTypesForFields) if not sameType(tupleTypeA, tupleTypeB): - typeMismatch(call.sons[i].info, tupleTypeA, tupleTypeB) + typeMismatch(c.config, call.sons[i].info, tupleTypeA, tupleTypeB) inc(c.p.nestedLoopCounter) if tupleTypeA.kind == tyTuple: @@ -139,6 +141,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = var fc: TFieldInstCtx fc.tupleType = tupleTypeA fc.tupleIndex = i + fc.c = c fc.replaceByFieldName = m == mFieldPairs var body = instFieldLoopBody(fc, loopBody, n) inc c.inUnrolledContext diff --git a/compiler/semfold.nim b/compiler/semfold.nim index d2d36140d..daf9ce983 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -13,24 +13,18 @@ import strutils, options, ast, astalgo, trees, treetab, nimsets, times, nversion, platform, math, msgs, os, condsyms, idents, renderer, types, - commands, magicsys, saturate + commands, magicsys, modulegraphs, strtabs -proc getConstExpr*(m: PSym, n: PNode): PNode - # evaluates the constant expression or returns nil if it is no constant - # expression -proc evalOp*(m: TMagic, n, a, b, c: PNode): PNode -proc leValueConv*(a, b: PNode): bool -proc newIntNodeT*(intVal: BiggestInt, n: PNode): PNode -proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode -proc newStrNodeT*(strVal: string, n: PNode): PNode - -# implementation - -proc newIntNodeT(intVal: BiggestInt, n: PNode): PNode = +proc newIntNodeT*(intVal: BiggestInt, n: PNode; g: ModuleGraph): PNode = case skipTypes(n.typ, abstractVarRange).kind of tyInt: result = newIntNode(nkIntLit, intVal) - result.typ = getIntLitType(result) + # See bug #6989. 'pred' et al only produce an int literal type if the + # original type was 'int', not a distinct int etc. + if n.typ.kind == tyInt: + result.typ = getIntLitType(g, result) + else: + result.typ = n.typ # hrm, this is not correct: 1 + high(int) shouldn't produce tyInt64 ... #setIntLitType(result) of tyChar: @@ -41,20 +35,82 @@ proc newIntNodeT(intVal: BiggestInt, n: PNode): PNode = result.typ = n.typ result.info = n.info -proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode = +proc newFloatNodeT*(floatVal: BiggestFloat, n: PNode; g: ModuleGraph): PNode = result = newFloatNode(nkFloatLit, floatVal) if skipTypes(n.typ, abstractVarRange).kind == tyFloat: - result.typ = getFloatLitType(result) + result.typ = getFloatLitType(g, result) else: result.typ = n.typ result.info = n.info -proc newStrNodeT(strVal: string, n: PNode): PNode = +proc newStrNodeT*(strVal: string, n: PNode; g: ModuleGraph): PNode = result = newStrNode(nkStrLit, strVal) result.typ = n.typ result.info = n.info -proc ordinalValToString*(a: PNode): string = +proc getConstExpr*(m: PSym, n: PNode; g: ModuleGraph): PNode + # evaluates the constant expression or returns nil if it is no constant + # expression +proc evalOp*(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode + +proc checkInRange(n: PNode, res: BiggestInt): bool = + if res in firstOrd(n.typ)..lastOrd(n.typ): + result = true + +proc foldAdd(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode = + let res = a +% b + if ((res xor a) >= 0'i64 or (res xor b) >= 0'i64) and + checkInRange(n, res): + result = newIntNodeT(res, n, g) + +proc foldSub*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode = + let res = a -% b + if ((res xor a) >= 0'i64 or (res xor not b) >= 0'i64) and + checkInRange(n, res): + result = newIntNodeT(res, n, g) + +proc foldAbs*(a: BiggestInt, n: PNode; g: ModuleGraph): PNode = + if a != firstOrd(n.typ): + result = newIntNodeT(a, n, g) + +proc foldMod*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode = + if b != 0'i64: + result = newIntNodeT(a mod b, n, g) + +proc foldModU*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode = + if b != 0'i64: + result = newIntNodeT(a %% b, n, g) + +proc foldDiv*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode = + if b != 0'i64 and (a != firstOrd(n.typ) or b != -1'i64): + result = newIntNodeT(a div b, n, g) + +proc foldDivU*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode = + if b != 0'i64: + result = newIntNodeT(a /% b, n, g) + +proc foldMul*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode = + let res = a *% b + let floatProd = toBiggestFloat(a) * toBiggestFloat(b) + let resAsFloat = toBiggestFloat(res) + + # Fast path for normal case: small multiplicands, and no info + # is lost in either method. + if resAsFloat == floatProd and checkInRange(n, res): + return newIntNodeT(res, n, g) + + # Somebody somewhere lost info. Close enough, or way off? Note + # that a != 0 and b != 0 (else resAsFloat == floatProd == 0). + # The difference either is or isn't significant compared to the + # true value (of which floatProd is a good approximation). + + # abs(diff)/abs(prod) <= 1/32 iff + # 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough" + if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd) and + checkInRange(n, res): + return newIntNodeT(res, n, g) + +proc ordinalValToString*(a: PNode; g: ModuleGraph): string = # because $ has the param ordinal[T], `a` is not necessarily an enum, but an # ordinal var x = getInt(a) @@ -66,14 +122,14 @@ proc ordinalValToString*(a: PNode): string = of tyEnum: var n = t.n for i in countup(0, sonsLen(n) - 1): - if n.sons[i].kind != nkSym: internalError(a.info, "ordinalValToString") + if n.sons[i].kind != nkSym: internalError(g.config, a.info, "ordinalValToString") var field = n.sons[i].sym if field.position == x: if field.ast == nil: return field.name.s else: return field.ast.strVal - internalError(a.info, "no symbol for ordinal value: " & $x) + internalError(g.config, a.info, "no symbol for ordinal value: " & $x) else: result = $x @@ -92,12 +148,12 @@ proc pickIntRange(a, b: PType): PType = proc isIntRangeOrLit(t: PType): bool = result = isIntRange(t) or isIntLit(t) -proc makeRange(typ: PType, first, last: BiggestInt): PType = +proc makeRange(typ: PType, first, last: BiggestInt; g: ModuleGraph): PType = let minA = min(first, last) let maxA = max(first, last) let lowerNode = newIntNode(nkIntLit, minA) if typ.kind == tyInt and minA == maxA: - result = getIntLitType(lowerNode) + result = getIntLitType(g, lowerNode) elif typ.kind in {tyUint, tyUInt64}: # these are not ordinal types, so you get no subrange type for these: result = typ @@ -109,7 +165,7 @@ proc makeRange(typ: PType, first, last: BiggestInt): PType = result.n = n addSonSkipIntLit(result, skipTypes(typ, {tyRange})) -proc makeRangeF(typ: PType, first, last: BiggestFloat): PType = +proc makeRangeF(typ: PType, first, last: BiggestFloat; g: ModuleGraph): PType = var n = newNode(nkRange) addSon(n, newFloatNode(nkFloatLit, min(first.float, last.float))) addSon(n, newFloatNode(nkFloatLit, max(first.float, last.float))) @@ -119,9 +175,9 @@ proc makeRangeF(typ: PType, first, last: BiggestFloat): PType = proc evalIs(n, a: PNode): PNode = # XXX: This should use the standard isOpImpl - internalAssert a.kind == nkSym and a.sym.kind == skType - internalAssert n.sonsLen == 3 and - n[2].kind in {nkStrLit..nkTripleStrLit, nkType} + #internalAssert a.kind == nkSym and a.sym.kind == skType + #internalAssert n.sonsLen == 3 and + # n[2].kind in {nkStrLit..nkTripleStrLit, nkType} let t1 = a.sym.typ @@ -145,125 +201,113 @@ proc evalIs(n, a: PNode): PNode = result = newIntNode(nkIntLit, ord(match)) result.typ = n.typ -proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = +proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode = # b and c may be nil result = nil case m - of mOrd: result = newIntNodeT(getOrdValue(a), n) - of mChr: result = newIntNodeT(getInt(a), n) - of mUnaryMinusI, mUnaryMinusI64: result = newIntNodeT(- getInt(a), n) - of mUnaryMinusF64: result = newFloatNodeT(- getFloat(a), n) - of mNot: result = newIntNodeT(1 - getInt(a), n) - of mCard: result = newIntNodeT(nimsets.cardSet(a), n) - of mBitnotI: result = newIntNodeT(not getInt(a), n) - of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), n) + of mOrd: result = newIntNodeT(getOrdValue(a), n, g) + of mChr: result = newIntNodeT(getInt(a), n, g) + of mUnaryMinusI, mUnaryMinusI64: result = newIntNodeT(- getInt(a), n, g) + of mUnaryMinusF64: result = newFloatNodeT(- getFloat(a), n, g) + of mNot: result = newIntNodeT(1 - getInt(a), n, g) + of mCard: result = newIntNodeT(nimsets.cardSet(a), n, g) + of mBitnotI: result = newIntNodeT(not getInt(a), n, g) + of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), n, g) of mLengthSeq, mLengthOpenArray, mXLenSeq, mLengthStr, mXLenStr: if a.kind == nkNilLit: - result = newIntNodeT(0, n) + result = newIntNodeT(0, n, g) elif a.kind in {nkStrLit..nkTripleStrLit}: - result = newIntNodeT(len a.strVal, n) + result = newIntNodeT(len a.strVal, n, g) else: - result = newIntNodeT(sonsLen(a), n) # BUGFIX + result = newIntNodeT(sonsLen(a), n, g) of mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away of mToFloat, mToBiggestFloat: - result = newFloatNodeT(toFloat(int(getInt(a))), n) - of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n) - of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n) - of mAbsI: - if getInt(a) >= 0: result = a - else: result = newIntNodeT(- getInt(a), n) + result = newFloatNodeT(toFloat(int(getInt(a))), n, g) + # XXX: Hides overflow/underflow + of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n, g) + of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n, g) + of mAbsI: result = foldAbs(getInt(a), n, g) of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64: # byte(-128) = 1...1..1000_0000'64 --> 0...0..1000_0000'64 - result = newIntNodeT(getInt(a) and (`shl`(1, getSize(a.typ) * 8) - 1), n) - of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n) - of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n) - of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n) - of mUnaryLt: result = newIntNodeT(getOrdValue(a) |-| 1, n) - of mSucc: result = newIntNodeT(getOrdValue(a) |+| getInt(b), n) - of mPred: result = newIntNodeT(getOrdValue(a) |-| getInt(b), n) - of mAddI: result = newIntNodeT(getInt(a) |+| getInt(b), n) - of mSubI: result = newIntNodeT(getInt(a) |-| getInt(b), n) - of mMulI: result = newIntNodeT(getInt(a) |*| getInt(b), n) + result = newIntNodeT(getInt(a) and (`shl`(1, getSize(a.typ) * 8) - 1), n, g) + of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n, g) + of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n, g) + of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n, g) + of mUnaryLt: result = foldSub(getOrdValue(a), 1, n, g) + of mSucc: result = foldAdd(getOrdValue(a), getInt(b), n, g) + of mPred: result = foldSub(getOrdValue(a), getInt(b), n, g) + of mAddI: result = foldAdd(getInt(a), getInt(b), n, g) + of mSubI: result = foldSub(getInt(a), getInt(b), n, g) + of mMulI: result = foldMul(getInt(a), getInt(b), n, g) of mMinI: - if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n) - else: result = newIntNodeT(getInt(a), n) + if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n, g) + else: result = newIntNodeT(getInt(a), n, g) of mMaxI: - if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n) - else: result = newIntNodeT(getInt(b), n) + if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n, g) + else: result = newIntNodeT(getInt(b), n, g) of mShlI: case skipTypes(n.typ, abstractRange).kind - of tyInt8: result = newIntNodeT(int8(getInt(a)) shl int8(getInt(b)), n) - of tyInt16: result = newIntNodeT(int16(getInt(a)) shl int16(getInt(b)), n) - of tyInt32: result = newIntNodeT(int32(getInt(a)) shl int32(getInt(b)), n) + of tyInt8: result = newIntNodeT(int8(getInt(a)) shl int8(getInt(b)), n, g) + of tyInt16: result = newIntNodeT(int16(getInt(a)) shl int16(getInt(b)), n, g) + of tyInt32: result = newIntNodeT(int32(getInt(a)) shl int32(getInt(b)), n, g) of tyInt64, tyInt, tyUInt..tyUInt64: - result = newIntNodeT(`shl`(getInt(a), getInt(b)), n) - else: internalError(n.info, "constant folding for shl") + result = newIntNodeT(`shl`(getInt(a), getInt(b)), n, g) + else: internalError(g.config, n.info, "constant folding for shl") of mShrI: case skipTypes(n.typ, abstractRange).kind - of tyInt8: result = newIntNodeT(int8(getInt(a)) shr int8(getInt(b)), n) - of tyInt16: result = newIntNodeT(int16(getInt(a)) shr int16(getInt(b)), n) - of tyInt32: result = newIntNodeT(int32(getInt(a)) shr int32(getInt(b)), n) + of tyInt8: result = newIntNodeT(int8(getInt(a)) shr int8(getInt(b)), n, g) + of tyInt16: result = newIntNodeT(int16(getInt(a)) shr int16(getInt(b)), n, g) + of tyInt32: result = newIntNodeT(int32(getInt(a)) shr int32(getInt(b)), n, g) of tyInt64, tyInt, tyUInt..tyUInt64: - result = newIntNodeT(`shr`(getInt(a), getInt(b)), n) - else: internalError(n.info, "constant folding for shr") - of mDivI: - let y = getInt(b) - if y != 0: - result = newIntNodeT(`|div|`(getInt(a), y), n) - of mModI: - let y = getInt(b) - if y != 0: - result = newIntNodeT(`|mod|`(getInt(a), y), n) - of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n) - of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n) - of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n) + result = newIntNodeT(`shr`(getInt(a), getInt(b)), n, g) + else: internalError(g.config, n.info, "constant folding for shr") + of mDivI: result = foldDiv(getInt(a), getInt(b), n, g) + of mModI: result = foldMod(getInt(a), getInt(b), n, g) + of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n, g) + of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n, g) + of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n, g) of mDivF64: if getFloat(b) == 0.0: - if getFloat(a) == 0.0: result = newFloatNodeT(NaN, n) - else: result = newFloatNodeT(Inf, n) + if getFloat(a) == 0.0: result = newFloatNodeT(NaN, n, g) + elif getFloat(b).classify == fcNegZero: result = newFloatNodeT(-Inf, n, g) + else: result = newFloatNodeT(Inf, n, g) else: - result = newFloatNodeT(getFloat(a) / getFloat(b), n) + result = newFloatNodeT(getFloat(a) / getFloat(b), n, g) of mMaxF64: - if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(a), n) - else: result = newFloatNodeT(getFloat(b), n) + if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(a), n, g) + else: result = newFloatNodeT(getFloat(b), n, g) of mMinF64: - if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(b), n) - else: result = newFloatNodeT(getFloat(a), n) - of mIsNil: result = newIntNodeT(ord(a.kind == nkNilLit), n) + if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(b), n, g) + else: result = newFloatNodeT(getFloat(a), n, g) + of mIsNil: result = newIntNodeT(ord(a.kind == nkNilLit), n, g) of mLtI, mLtB, mLtEnum, mLtCh: - result = newIntNodeT(ord(getOrdValue(a) < getOrdValue(b)), n) + result = newIntNodeT(ord(getOrdValue(a) < getOrdValue(b)), n, g) of mLeI, mLeB, mLeEnum, mLeCh: - result = newIntNodeT(ord(getOrdValue(a) <= getOrdValue(b)), n) + result = newIntNodeT(ord(getOrdValue(a) <= getOrdValue(b)), n, g) of mEqI, mEqB, mEqEnum, mEqCh: - result = newIntNodeT(ord(getOrdValue(a) == getOrdValue(b)), n) - of mLtF64: result = newIntNodeT(ord(getFloat(a) < getFloat(b)), n) - of mLeF64: result = newIntNodeT(ord(getFloat(a) <= getFloat(b)), n) - of mEqF64: result = newIntNodeT(ord(getFloat(a) == getFloat(b)), n) - of mLtStr: result = newIntNodeT(ord(getStr(a) < getStr(b)), n) - of mLeStr: result = newIntNodeT(ord(getStr(a) <= getStr(b)), n) - of mEqStr: result = newIntNodeT(ord(getStr(a) == getStr(b)), n) + result = newIntNodeT(ord(getOrdValue(a) == getOrdValue(b)), n, g) + of mLtF64: result = newIntNodeT(ord(getFloat(a) < getFloat(b)), n, g) + of mLeF64: result = newIntNodeT(ord(getFloat(a) <= getFloat(b)), n, g) + of mEqF64: result = newIntNodeT(ord(getFloat(a) == getFloat(b)), n, g) + of mLtStr: result = newIntNodeT(ord(getStr(a) < getStr(b)), n, g) + of mLeStr: result = newIntNodeT(ord(getStr(a) <= getStr(b)), n, g) + of mEqStr: result = newIntNodeT(ord(getStr(a) == getStr(b)), n, g) of mLtU, mLtU64: - result = newIntNodeT(ord(`<%`(getOrdValue(a), getOrdValue(b))), n) + result = newIntNodeT(ord(`<%`(getOrdValue(a), getOrdValue(b))), n, g) of mLeU, mLeU64: - result = newIntNodeT(ord(`<=%`(getOrdValue(a), getOrdValue(b))), n) - of mBitandI, mAnd: result = newIntNodeT(a.getInt and b.getInt, n) - of mBitorI, mOr: result = newIntNodeT(getInt(a) or getInt(b), n) - of mBitxorI, mXor: result = newIntNodeT(a.getInt xor b.getInt, n) - of mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n) - of mSubU: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n) - of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n) - of mModU: - let y = getInt(b) - if y != 0: - result = newIntNodeT(`%%`(getInt(a), y), n) - of mDivU: - let y = getInt(b) - if y != 0: - result = newIntNodeT(`/%`(getInt(a), y), n) - of mLeSet: result = newIntNodeT(ord(containsSets(a, b)), n) - of mEqSet: result = newIntNodeT(ord(equalSets(a, b)), n) + result = newIntNodeT(ord(`<=%`(getOrdValue(a), getOrdValue(b))), n, g) + of mBitandI, mAnd: result = newIntNodeT(a.getInt and b.getInt, n, g) + of mBitorI, mOr: result = newIntNodeT(getInt(a) or getInt(b), n, g) + of mBitxorI, mXor: result = newIntNodeT(a.getInt xor b.getInt, n, g) + of mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n, g) + of mSubU: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n, g) + of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n, g) + of mModU: result = foldModU(getInt(a), getInt(b), n, g) + of mDivU: result = foldDivU(getInt(a), getInt(b), n, g) + of mLeSet: result = newIntNodeT(ord(containsSets(a, b)), n, g) + of mEqSet: result = newIntNodeT(ord(equalSets(a, b)), n, g) of mLtSet: - result = newIntNodeT(ord(containsSets(a, b) and not equalSets(a, b)), n) + result = newIntNodeT(ord(containsSets(a, b) and not equalSets(a, b)), n, g) of mMulSet: result = nimsets.intersectSets(a, b) result.info = n.info @@ -276,120 +320,125 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of mSymDiffSet: result = nimsets.symdiffSets(a, b) result.info = n.info - of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n) - of mInSet: result = newIntNodeT(ord(inSet(a, b)), n) + of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n, g) + of mInSet: result = newIntNodeT(ord(inSet(a, b)), n, g) of mRepr: # BUGFIX: we cannot eval mRepr here for reasons that I forgot. discard - of mIntToStr, mInt64ToStr: result = newStrNodeT($(getOrdValue(a)), n) + of mIntToStr, mInt64ToStr: result = newStrNodeT($(getOrdValue(a)), n, g) of mBoolToStr: - if getOrdValue(a) == 0: result = newStrNodeT("false", n) - else: result = newStrNodeT("true", n) - of mCopyStr: result = newStrNodeT(substr(getStr(a), int(getOrdValue(b))), n) + if getOrdValue(a) == 0: result = newStrNodeT("false", n, g) + else: result = newStrNodeT("true", n, g) + of mCopyStr: result = newStrNodeT(substr(getStr(a), int(getOrdValue(b))), n, g) of mCopyStrLast: result = newStrNodeT(substr(getStr(a), int(getOrdValue(b)), - int(getOrdValue(c))), n) - of mFloatToStr: result = newStrNodeT($getFloat(a), n) + int(getOrdValue(c))), n, g) + of mFloatToStr: result = newStrNodeT($getFloat(a), n, g) of mCStrToStr, mCharToStr: if a.kind == nkBracket: var s = "" for b in a.sons: s.add b.getStrOrChar - result = newStrNodeT(s, n) + result = newStrNodeT(s, n, g) else: - result = newStrNodeT(getStrOrChar(a), n) + result = newStrNodeT(getStrOrChar(a), n, g) of mStrToStr: result = a - of mEnumToStr: result = newStrNodeT(ordinalValToString(a), n) + of mEnumToStr: result = newStrNodeT(ordinalValToString(a, g), n, g) of mArrToSeq: result = copyTree(a) result.typ = n.typ of mCompileOption: - result = newIntNodeT(ord(commands.testCompileOption(a.getStr, n.info)), n) + result = newIntNodeT(ord(commands.testCompileOption(g.config, a.getStr, n.info)), n, g) of mCompileOptionArg: result = newIntNodeT(ord( - testCompileOptionArg(getStr(a), getStr(b), n.info)), n) + testCompileOptionArg(g.config, getStr(a), getStr(b), n.info)), n, g) of mEqProc: result = newIntNodeT(ord( - exprStructuralEquivalent(a, b, strictSymEquality=true)), n) + exprStructuralEquivalent(a, b, strictSymEquality=true)), n, g) else: discard -proc getConstIfExpr(c: PSym, n: PNode): PNode = +proc getConstIfExpr(c: PSym, n: PNode; g: ModuleGraph): PNode = result = nil for i in countup(0, sonsLen(n) - 1): var it = n.sons[i] if it.len == 2: - var e = getConstExpr(c, it.sons[0]) + var e = getConstExpr(c, it.sons[0], g) if e == nil: return nil if getOrdValue(e) != 0: if result == nil: - result = getConstExpr(c, it.sons[1]) + result = getConstExpr(c, it.sons[1], g) if result == nil: return elif it.len == 1: - if result == nil: result = getConstExpr(c, it.sons[0]) - else: internalError(it.info, "getConstIfExpr()") + if result == nil: result = getConstExpr(c, it.sons[0], g) + else: internalError(g.config, it.info, "getConstIfExpr()") -proc leValueConv(a, b: PNode): bool = +proc leValueConv*(a, b: PNode): bool = result = false case a.kind of nkCharLit..nkUInt64Lit: case b.kind of nkCharLit..nkUInt64Lit: result = a.intVal <= b.intVal of nkFloatLit..nkFloat128Lit: result = a.intVal <= round(b.floatVal).int - else: internalError(a.info, "leValueConv") + else: result = false #internalError(a.info, "leValueConv") of nkFloatLit..nkFloat128Lit: case b.kind of nkFloatLit..nkFloat128Lit: result = a.floatVal <= b.floatVal of nkCharLit..nkUInt64Lit: result = a.floatVal <= toFloat(int(b.intVal)) - else: internalError(a.info, "leValueConv") - else: internalError(a.info, "leValueConv") + else: result = false # internalError(a.info, "leValueConv") + else: result = false # internalError(a.info, "leValueConv") -proc magicCall(m: PSym, n: PNode): PNode = +proc magicCall(m: PSym, n: PNode; g: ModuleGraph): PNode = if sonsLen(n) <= 1: return var s = n.sons[0].sym - var a = getConstExpr(m, n.sons[1]) + var a = getConstExpr(m, n.sons[1], g) var b, c: PNode if a == nil: return if sonsLen(n) > 2: - b = getConstExpr(m, n.sons[2]) + b = getConstExpr(m, n.sons[2], g) if b == nil: return if sonsLen(n) > 3: - c = getConstExpr(m, n.sons[3]) + c = getConstExpr(m, n.sons[3], g) if c == nil: return - result = evalOp(s.magic, n, a, b, c) - -proc getAppType(n: PNode): PNode = - if gGlobalOptions.contains(optGenDynLib): - result = newStrNodeT("lib", n) - elif gGlobalOptions.contains(optGenStaticLib): - result = newStrNodeT("staticlib", n) - elif gGlobalOptions.contains(optGenGuiApp): - result = newStrNodeT("gui", n) + result = evalOp(s.magic, n, a, b, c, g) + +proc getAppType(n: PNode; g: ModuleGraph): PNode = + if g.config.globalOptions.contains(optGenDynLib): + result = newStrNodeT("lib", n, g) + elif g.config.globalOptions.contains(optGenStaticLib): + result = newStrNodeT("staticlib", n, g) + elif g.config.globalOptions.contains(optGenGuiApp): + result = newStrNodeT("gui", n, g) else: - result = newStrNodeT("console", n) + result = newStrNodeT("console", n, g) -proc rangeCheck(n: PNode, value: BiggestInt) = - if value < firstOrd(n.typ) or value > lastOrd(n.typ): - localError(n.info, errGenerated, "cannot convert " & $value & +proc rangeCheck(n: PNode, value: BiggestInt; g: ModuleGraph) = + var err = false + if n.typ.skipTypes({tyRange}).kind in {tyUInt..tyUInt64}: + err = value <% firstOrd(n.typ) or value >% lastOrd(n.typ, fixedUnsigned=true) + else: + err = value < firstOrd(n.typ) or value > lastOrd(n.typ) + if err: + localError(g.config, n.info, "cannot convert " & $value & " to " & typeToString(n.typ)) -proc foldConv*(n, a: PNode; check = false): PNode = +proc foldConv*(n, a: PNode; g: ModuleGraph; check = false): PNode = # XXX range checks? case skipTypes(n.typ, abstractRange).kind of tyInt..tyInt64, tyUInt..tyUInt64: case skipTypes(a.typ, abstractRange).kind of tyFloat..tyFloat64: - result = newIntNodeT(int(getFloat(a)), n) - of tyChar: result = newIntNodeT(getOrdValue(a), n) + result = newIntNodeT(int(getFloat(a)), n, g) + of tyChar: result = newIntNodeT(getOrdValue(a), n, g) else: result = a result.typ = n.typ if check and result.kind in {nkCharLit..nkUInt64Lit}: - rangeCheck(n, result.intVal) + rangeCheck(n, result.intVal, g) of tyFloat..tyFloat64: case skipTypes(a.typ, abstractRange).kind of tyInt..tyInt64, tyEnum, tyBool, tyChar: - result = newFloatNodeT(toBiggestFloat(getOrdValue(a)), n) + result = newFloatNodeT(toBiggestFloat(getOrdValue(a)), n, g) else: result = a result.typ = n.typ @@ -399,47 +448,47 @@ proc foldConv*(n, a: PNode; check = false): PNode = result = a result.typ = n.typ -proc getArrayConstr(m: PSym, n: PNode): PNode = +proc getArrayConstr(m: PSym, n: PNode; g: ModuleGraph): PNode = if n.kind == nkBracket: result = n else: - result = getConstExpr(m, n) + result = getConstExpr(m, n, g) if result == nil: result = n -proc foldArrayAccess(m: PSym, n: PNode): PNode = - var x = getConstExpr(m, n.sons[0]) - if x == nil or x.typ.skipTypes({tyGenericInst, tyAlias}).kind == tyTypeDesc: +proc foldArrayAccess(m: PSym, n: PNode; g: ModuleGraph): PNode = + var x = getConstExpr(m, n.sons[0], g) + if x == nil or x.typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyTypeDesc: return - var y = getConstExpr(m, n.sons[1]) + var y = getConstExpr(m, n.sons[1], g) if y == nil: return var idx = getOrdValue(y) case x.kind - of nkPar: + of nkPar, nkTupleConstr: if idx >= 0 and idx < sonsLen(x): result = x.sons[int(idx)] if result.kind == nkExprColonExpr: result = result.sons[1] else: - localError(n.info, errIndexOutOfBounds) + localError(g.config, n.info, "index out of bounds: " & $n) of nkBracket: idx = idx - x.typ.firstOrd if idx >= 0 and idx < x.len: result = x.sons[int(idx)] - else: localError(n.info, errIndexOutOfBounds) + else: localError(g.config, n.info, "index out of bounds: " & $n) of nkStrLit..nkTripleStrLit: result = newNodeIT(nkCharLit, x.info, n.typ) if idx >= 0 and idx < len(x.strVal): result.intVal = ord(x.strVal[int(idx)]) - elif idx == len(x.strVal): + elif idx == len(x.strVal) and optLaxStrings in g.config.options: discard else: - localError(n.info, errIndexOutOfBounds) + localError(g.config, n.info, "index out of bounds: " & $n) else: discard -proc foldFieldAccess(m: PSym, n: PNode): PNode = +proc foldFieldAccess(m: PSym, n: PNode; g: ModuleGraph): PNode = # a real field access; proc calls have already been transformed - var x = getConstExpr(m, n.sons[0]) - if x == nil or x.kind notin {nkObjConstr, nkPar}: return + var x = getConstExpr(m, n.sons[0], g) + if x == nil or x.kind notin {nkObjConstr, nkPar, nkTupleConstr}: return var field = n.sons[1].sym for i in countup(ord(x.kind == nkObjConstr), sonsLen(x) - 1): @@ -452,13 +501,13 @@ proc foldFieldAccess(m: PSym, n: PNode): PNode = if it.sons[0].sym.name.id == field.name.id: result = x.sons[i].sons[1] return - localError(n.info, errFieldXNotFound, field.name.s) + localError(g.config, n.info, "field not found: " & field.name.s) -proc foldConStrStr(m: PSym, n: PNode): PNode = +proc foldConStrStr(m: PSym, n: PNode; g: ModuleGraph): PNode = result = newNodeIT(nkStrLit, n.info, n.typ) result.strVal = "" for i in countup(1, sonsLen(n) - 1): - let a = getConstExpr(m, n.sons[i]) + let a = getConstExpr(m, n.sons[i], g) if a == nil: return nil result.strVal.add(getStrOrChar(a)) @@ -470,37 +519,55 @@ proc newSymNodeTypeDesc*(s: PSym; info: TLineInfo): PNode = else: result.typ = s.typ -proc getConstExpr(m: PSym, n: PNode): PNode = +proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode = result = nil + + proc getSrcTimestamp(): DateTime = + try: + result = utc(fromUnix(parseInt(getEnv("SOURCE_DATE_EPOCH", + "not a number")))) + except ValueError: + # Environment variable malformed. + # https://reproducible-builds.org/specs/source-date-epoch/: "If the + # value is malformed, the build process SHOULD exit with a non-zero + # error code", which this doesn't do. This uses local time, because + # that maintains compatibility with existing usage. + result = local(getTime()) + case n.kind of nkSym: var s = n.sym case s.kind of skEnumField: - result = newIntNodeT(s.position, n) + result = newIntNodeT(s.position, n, g) of skConst: case s.magic - of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n) - of mCompileDate: result = newStrNodeT(times.getDateStr(), n) - of mCompileTime: result = newStrNodeT(times.getClockStr(), n) - of mCpuEndian: result = newIntNodeT(ord(CPU[targetCPU].endian), n) - of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[targetOS].name), n) - of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLowerAscii, n) - of mBuildOS: result = newStrNodeT(toLowerAscii(platform.OS[platform.hostOS].name), n) - of mBuildCPU: result = newStrNodeT(platform.CPU[platform.hostCPU].name.toLowerAscii, n) - of mAppType: result = getAppType(n) - of mNaN: result = newFloatNodeT(NaN, n) - of mInf: result = newFloatNodeT(Inf, n) - of mNegInf: result = newFloatNodeT(NegInf, n) + of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n, g) + of mCompileDate: result = newStrNodeT(format(getSrcTimestamp(), + "yyyy-MM-dd"), n, g) + of mCompileTime: result = newStrNodeT(format(getSrcTimestamp(), + "HH:mm:ss"), n, g) + of mCpuEndian: result = newIntNodeT(ord(CPU[targetCPU].endian), n, g) + of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[targetOS].name), n, g) + of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLowerAscii, n, g) + of mBuildOS: result = newStrNodeT(toLowerAscii(platform.OS[platform.hostOS].name), n, g) + of mBuildCPU: result = newStrNodeT(platform.CPU[platform.hostCPU].name.toLowerAscii, n, g) + of mAppType: result = getAppType(n, g) + of mNaN: result = newFloatNodeT(NaN, n, g) + of mInf: result = newFloatNodeT(Inf, n, g) + of mNegInf: result = newFloatNodeT(NegInf, n, g) of mIntDefine: - if isDefined(s.name): - result = newIntNodeT(lookupSymbol(s.name).parseInt, n) + if isDefined(g.config, s.name.s): + try: + result = newIntNodeT(g.config.symbols[s.name.s].parseInt, n, g) + except ValueError: + localError(g.config, n.info, "expression is not an integer literal") of mStrDefine: - if isDefined(s.name): - result = newStrNodeT(lookupSymbol(s.name), n) + if isDefined(g.config, s.name.s): + result = newStrNodeT(g.config.symbols[s.name.s], n, g) else: result = copyTree(s.ast) - of {skProc, skFunc, skMethod}: + of skProc, skFunc, skMethod: result = n of skType: # XXX gensym'ed symbols can come here and cannot be resolved. This is @@ -520,7 +587,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode = of nkCharLit..nkNilLit: result = copyNode(n) of nkIfExpr: - result = getConstIfExpr(m, n) + result = getConstIfExpr(m, n, g) of nkCallKinds: if n.sons[0].kind != nkSym: return var s = n.sons[0].sym @@ -533,68 +600,67 @@ proc getConstExpr(m: PSym, n: PNode): PNode = of mSizeOf: var a = n.sons[1] if computeSize(a.typ) < 0: - localError(a.info, errCannotEvalXBecauseIncompletelyDefined, - "sizeof") + localError(g.config, a.info, "cannot evaluate 'sizeof' because its type is not defined completely") result = nil elif skipTypes(a.typ, typedescInst+{tyRange}).kind in IntegralTypes+NilableTypes+{tySet}: #{tyArray,tyObject,tyTuple}: - result = newIntNodeT(getSize(a.typ), n) + result = newIntNodeT(getSize(a.typ), n, g) else: result = nil # XXX: size computation for complex types is still wrong of mLow: - result = newIntNodeT(firstOrd(n.sons[1].typ), n) + result = newIntNodeT(firstOrd(n.sons[1].typ), n, g) of mHigh: if skipTypes(n.sons[1].typ, abstractVar).kind notin {tySequence, tyString, tyCString, tyOpenArray, tyVarargs}: - result = newIntNodeT(lastOrd(skipTypes(n[1].typ, abstractVar)), n) + result = newIntNodeT(lastOrd(skipTypes(n[1].typ, abstractVar)), n, g) else: - var a = getArrayConstr(m, n.sons[1]) + var a = getArrayConstr(m, n.sons[1], g) if a.kind == nkBracket: # we can optimize it away: - result = newIntNodeT(sonsLen(a)-1, n) + result = newIntNodeT(sonsLen(a)-1, n, g) of mLengthOpenArray: - var a = getArrayConstr(m, n.sons[1]) + var a = getArrayConstr(m, n.sons[1], g) if a.kind == nkBracket: # we can optimize it away! This fixes the bug ``len(134)``. - result = newIntNodeT(sonsLen(a), n) + result = newIntNodeT(sonsLen(a), n, g) else: - result = magicCall(m, n) + result = magicCall(m, n, g) of mLengthArray: # It doesn't matter if the argument is const or not for mLengthArray. # This fixes bug #544. - result = newIntNodeT(lengthOrd(n.sons[1].typ), n) + result = newIntNodeT(lengthOrd(n.sons[1].typ), n, g) of mAstToStr: - result = newStrNodeT(renderTree(n[1], {renderNoComments}), n) + result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, g) of mConStrStr: - result = foldConStrStr(m, n) + result = foldConStrStr(m, n, g) of mIs: - let a = getConstExpr(m, n[1]) + let a = getConstExpr(m, n[1], g) if a != nil and a.kind == nkSym and a.sym.kind == skType: result = evalIs(n, a) else: - result = magicCall(m, n) + result = magicCall(m, n, g) except OverflowError: - localError(n.info, errOverOrUnderflow) + localError(g.config, n.info, "over- or underflow") except DivByZeroError: - localError(n.info, errConstantDivisionByZero) + localError(g.config, n.info, "division by zero") of nkAddr: - var a = getConstExpr(m, n.sons[0]) + var a = getConstExpr(m, n.sons[0], g) if a != nil: result = n n.sons[0] = a of nkBracket: result = copyTree(n) for i in countup(0, sonsLen(n) - 1): - var a = getConstExpr(m, n.sons[i]) + var a = getConstExpr(m, n.sons[i], g) if a == nil: return nil result.sons[i] = a incl(result.flags, nfAllConst) of nkRange: - var a = getConstExpr(m, n.sons[0]) + var a = getConstExpr(m, n.sons[0], g) if a == nil: return - var b = getConstExpr(m, n.sons[1]) + var b = getConstExpr(m, n.sons[1], g) if b == nil: return result = copyNode(n) addSon(result, a) @@ -602,7 +668,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode = of nkCurly: result = copyTree(n) for i in countup(0, sonsLen(n) - 1): - var a = getConstExpr(m, n.sons[i]) + var a = getConstExpr(m, n.sons[i], g) if a == nil: return nil result.sons[i] = a incl(result.flags, nfAllConst) @@ -613,50 +679,50 @@ proc getConstExpr(m: PSym, n: PNode): PNode = # if a == nil: return nil # result.sons[i].sons[1] = a # incl(result.flags, nfAllConst) - of nkPar: + of nkPar, nkTupleConstr: # tuple constructor result = copyTree(n) if (sonsLen(n) > 0) and (n.sons[0].kind == nkExprColonExpr): for i in countup(0, sonsLen(n) - 1): - var a = getConstExpr(m, n.sons[i].sons[1]) + var a = getConstExpr(m, n.sons[i].sons[1], g) if a == nil: return nil result.sons[i].sons[1] = a else: for i in countup(0, sonsLen(n) - 1): - var a = getConstExpr(m, n.sons[i]) + var a = getConstExpr(m, n.sons[i], g) if a == nil: return nil result.sons[i] = a incl(result.flags, nfAllConst) of nkChckRangeF, nkChckRange64, nkChckRange: - var a = getConstExpr(m, n.sons[0]) + var a = getConstExpr(m, n.sons[0], g) if a == nil: return if leValueConv(n.sons[1], a) and leValueConv(a, n.sons[2]): result = a # a <= x and x <= b result.typ = n.typ else: - localError(n.info, errGenerated, `%`( - msgKindToString(errIllegalConvFromXtoY), - [typeToString(n.sons[0].typ), typeToString(n.typ)])) + localError(g.config, n.info, + "conversion from $1 to $2 is invalid" % + [typeToString(n.sons[0].typ), typeToString(n.typ)]) of nkStringToCString, nkCStringToString: - var a = getConstExpr(m, n.sons[0]) + var a = getConstExpr(m, n.sons[0], g) if a == nil: return result = a result.typ = n.typ of nkHiddenStdConv, nkHiddenSubConv, nkConv: - var a = getConstExpr(m, n.sons[1]) + var a = getConstExpr(m, n.sons[1], g) if a == nil: return - result = foldConv(n, a, check=n.kind == nkHiddenStdConv) + result = foldConv(n, a, g, check=n.kind == nkHiddenStdConv) of nkCast: - var a = getConstExpr(m, n.sons[1]) + var a = getConstExpr(m, n.sons[1], g) if a == nil: return if n.typ != nil and n.typ.kind in NilableTypes: # we allow compile-time 'cast' for pointer types: result = a result.typ = n.typ - of nkBracketExpr: result = foldArrayAccess(m, n) - of nkDotExpr: result = foldFieldAccess(m, n) + of nkBracketExpr: result = foldArrayAccess(m, n, g) + of nkDotExpr: result = foldFieldAccess(m, n, g) of nkStmtListExpr: if n.len == 2 and n[0].kind == nkComesFrom: - result = getConstExpr(m, n[1]) + result = getConstExpr(m, n[1], g) else: discard diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 16da06952..8f06e748e 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -17,13 +17,13 @@ # included from sem.nim -proc getIdentNode(n: PNode): PNode = +proc getIdentNode(c: PContext; n: PNode): PNode = case n.kind - of nkPostfix: result = getIdentNode(n.sons[1]) - of nkPragmaExpr: result = getIdentNode(n.sons[0]) + of nkPostfix: result = getIdentNode(c, n.sons[1]) + of nkPragmaExpr: result = getIdentNode(c, n.sons[0]) of nkIdent, nkAccQuoted, nkSym: result = n else: - illFormedAst(n) + illFormedAst(n, c.config) result = n type @@ -103,8 +103,8 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx): PNode = result = n - let ident = considerQuotedIdent(n) - var s = searchInScopes(c, ident).skipAlias(n) + let ident = considerQuotedIdent(c.config, n) + var s = searchInScopes(c, ident).skipAlias(n, c.config) if s == nil: s = strTableGet(c.pureEnumFields, ident) if s != nil and contains(c.ambiguousSymbols, s.id): @@ -140,8 +140,8 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx) result = n let n = n[1] - let ident = considerQuotedIdent(n) - var s = searchInScopes(c, ident).skipAlias(n) + let ident = considerQuotedIdent(c.config, n) + var s = searchInScopes(c, ident).skipAlias(n, c.config) if s != nil and s.kind in routineKinds: isMacro = s.kind in {skTemplate, skMacro} if withinBind in flags: @@ -158,7 +158,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, result = newDot(result, syms) proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) = - let s = newSymS(skUnknown, getIdentNode(n), c) + let s = newSymS(skUnknown, getIdentNode(c, n), c) addPrelimDecl(c, s) styleCheckDef(n.info, s, kind) @@ -169,7 +169,7 @@ proc semGenericStmt(c: PContext, n: PNode, when defined(nimsuggest): if withinTypeDesc in flags: inc c.inTypeContext - #if gCmd == cmdIdeTools: suggestStmt(c, n) + #if conf.cmd == cmdIdeTools: suggestStmt(c, n) semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody) case n.kind @@ -201,13 +201,13 @@ proc semGenericStmt(c: PContext, n: PNode, result = semMixinStmt(c, n, ctx.toMixin) of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit: # check if it is an expression macro: - checkMinSonsLen(n, 1) + checkMinSonsLen(n, 1, c.config) let fn = n.sons[0] var s = qualifiedLookUp(c, fn, {}) if s == nil and {withinMixin, withinConcept}*flags == {} and fn.kind in {nkIdent, nkAccQuoted} and - considerQuotedIdent(fn).id notin ctx.toMixin: + considerQuotedIdent(c.config, fn).id notin ctx.toMixin: errorUndeclaredIdentifier(c, n.info, fn.renderTree) var first = int ord(withinConcept in flags) @@ -285,7 +285,7 @@ proc semGenericStmt(c: PContext, n: PNode, withBracketExpr ctx, n.sons[0]: result = semGenericStmt(c, result, flags, ctx) of nkAsgn, nkFastAsgn: - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.config) let a = n.sons[0] let b = n.sons[1] @@ -323,7 +323,7 @@ proc semGenericStmt(c: PContext, n: PNode, n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx) for i in countup(1, sonsLen(n)-1): var a = n.sons[i] - checkMinSonsLen(a, 1) + checkMinSonsLen(a, 1, c.config) var L = sonsLen(a) for j in countup(0, L-2): a.sons[j] = semGenericStmt(c, a.sons[j], flags, ctx) @@ -340,23 +340,23 @@ proc semGenericStmt(c: PContext, n: PNode, closeScope(c) closeScope(c) of nkBlockStmt, nkBlockExpr, nkBlockType: - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.config) openScope(c) if n.sons[0].kind != nkEmpty: addTempDecl(c, n.sons[0], skLabel) n.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx) closeScope(c) of nkTryStmt: - checkMinSonsLen(n, 2) + checkMinSonsLen(n, 2, c.config) n.sons[0] = semGenericStmtScope(c, n.sons[0], flags, ctx) for i in countup(1, sonsLen(n)-1): var a = n.sons[i] - checkMinSonsLen(a, 1) + checkMinSonsLen(a, 1, c.config) var L = sonsLen(a) openScope(c) for j in countup(0, L-2): if a.sons[j].isInfixAs(): - addTempDecl(c, getIdentNode(a.sons[j][2]), skLet) + addTempDecl(c, getIdentNode(c, a.sons[j][2]), skLet) a.sons[j].sons[1] = semGenericStmt(c, a.sons[j][1], flags+{withinTypeDesc}, ctx) else: a.sons[j] = semGenericStmt(c, a.sons[j], flags+{withinTypeDesc}, ctx) @@ -367,44 +367,44 @@ proc semGenericStmt(c: PContext, n: PNode, for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue - if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a) - checkMinSonsLen(a, 3) + if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a, c.config) + checkMinSonsLen(a, 3, c.config) var L = sonsLen(a) a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx) for j in countup(0, L-3): - addTempDecl(c, getIdentNode(a.sons[j]), skVar) + addTempDecl(c, getIdentNode(c, a.sons[j]), skVar) of nkGenericParams: for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] - if (a.kind != nkIdentDefs): illFormedAst(a) - checkMinSonsLen(a, 3) + if (a.kind != nkIdentDefs): illFormedAst(a, c.config) + checkMinSonsLen(a, 3, c.config) var L = sonsLen(a) a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) # do not perform symbol lookup for default expressions for j in countup(0, L-3): - addTempDecl(c, getIdentNode(a.sons[j]), skType) + addTempDecl(c, getIdentNode(c, a.sons[j]), skType) of nkConstSection: for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue - if (a.kind != nkConstDef): illFormedAst(a) - checkSonsLen(a, 3) - addTempDecl(c, getIdentNode(a.sons[0]), skConst) + if (a.kind != nkConstDef): illFormedAst(a, c.config) + checkSonsLen(a, 3, c.config) + addTempDecl(c, getIdentNode(c, a.sons[0]), skConst) a.sons[1] = semGenericStmt(c, a.sons[1], flags+{withinTypeDesc}, ctx) a.sons[2] = semGenericStmt(c, a.sons[2], flags, ctx) of nkTypeSection: for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue - if (a.kind != nkTypeDef): illFormedAst(a) - checkSonsLen(a, 3) - addTempDecl(c, getIdentNode(a.sons[0]), skType) + if (a.kind != nkTypeDef): illFormedAst(a, c.config) + checkSonsLen(a, 3, c.config) + addTempDecl(c, getIdentNode(c, a.sons[0]), skType) for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue - if (a.kind != nkTypeDef): illFormedAst(a) - checkSonsLen(a, 3) + if (a.kind != nkTypeDef): illFormedAst(a, c.config) + checkSonsLen(a, 3, c.config) if a.sons[1].kind != nkEmpty: openScope(c) a.sons[1] = semGenericStmt(c, a.sons[1], flags, ctx) @@ -421,28 +421,28 @@ proc semGenericStmt(c: PContext, n: PNode, case n.sons[i].kind of nkEnumFieldDef: a = n.sons[i].sons[0] of nkIdent: a = n.sons[i] - else: illFormedAst(n) - addDecl(c, newSymS(skUnknown, getIdentNode(a), c)) + else: illFormedAst(n, c.config) + addDecl(c, newSymS(skUnknown, getIdentNode(c, a), c)) of nkObjectTy, nkTupleTy, nkTupleClassTy: discard of nkFormalParams: - checkMinSonsLen(n, 1) + checkMinSonsLen(n, 1, c.config) if n.sons[0].kind != nkEmpty: n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, ctx) for i in countup(1, sonsLen(n) - 1): var a = n.sons[i] - if (a.kind != nkIdentDefs): illFormedAst(a) - checkMinSonsLen(a, 3) + if (a.kind != nkIdentDefs): illFormedAst(a, c.config) + checkMinSonsLen(a, 3, c.config) var L = sonsLen(a) a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx) for j in countup(0, L-3): - addTempDecl(c, getIdentNode(a.sons[j]), skParam) + addTempDecl(c, getIdentNode(c, a.sons[j]), skParam) of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, nkFuncDef, nkIteratorDef, nkLambdaKinds: - checkSonsLen(n, bodyPos + 1) + checkSonsLen(n, bodyPos + 1, c.config) if n.sons[namePos].kind != nkEmpty: - addTempDecl(c, getIdentNode(n.sons[0]), skProc) + addTempDecl(c, getIdentNode(c, n.sons[0]), skProc) openScope(c) n.sons[genericParamsPos] = semGenericStmt(c, n.sons[genericParamsPos], flags, ctx) @@ -463,7 +463,7 @@ proc semGenericStmt(c: PContext, n: PNode, closeScope(c) of nkPragma, nkPragmaExpr: discard of nkExprColonExpr, nkExprEqExpr: - checkMinSonsLen(n, 2) + checkMinSonsLen(n, 2, c.config) result.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx) else: for i in countup(0, sonsLen(n) - 1): diff --git a/compiler/seminst.nim b/compiler/seminst.nim index acea9330b..a5f0ca7d8 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -54,10 +54,13 @@ proc pushProcCon*(c: PContext; owner: PSym) = rawPushProcCon(c, owner) rawHandleSelf(c, owner) +const + errCannotInstantiateX = "cannot instantiate: '$1'" + iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym = - internalAssert n.kind == nkGenericParams + internalAssert c.config, n.kind == nkGenericParams for i, a in n.pairs: - internalAssert a.kind == nkSym + internalAssert c.config, a.kind == nkSym var q = a.sym if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic}+tyTypeClasses: continue @@ -71,10 +74,10 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym # later by semAsgn in return type inference scenario t = q.typ else: - localError(a.info, errCannotInstantiateX, s.name.s) + localError(c.config, a.info, errCannotInstantiateX % s.name.s) t = errorType(c) elif t.kind == tyGenericParam: - localError(a.info, errCannotInstantiateX, q.name.s) + localError(c.config, a.info, errCannotInstantiateX % q.name.s) t = errorType(c) elif t.kind == tyGenericInvocation: #t = instGenericContainer(c, a, t) @@ -145,7 +148,7 @@ proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) = freshGenSyms(b, result, orig, symMap) b = semProcBody(c, b) b = hloBody(c, b) - n.sons[bodyPos] = transformBody(c.module, b, result) + n.sons[bodyPos] = transformBody(c.graph, c.module, b, result) #echo "code instantiated ", result.name.s excl(result.flags, sfForward) dec c.inGenericInst @@ -174,6 +177,8 @@ proc sideEffectsCheck(c: PContext, s: PSym) = proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, allowMetaTypes = false): PType = + internalAssert c.config, header.kind == tyGenericInvocation + var typeMap: LayeredIdTable cl: TReplTypeVars @@ -185,7 +190,35 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, cl.info = info cl.c = c cl.allowMetaTypes = allowMetaTypes + + # We must add all generic params in scope, because the generic body + # may include tyFromExpr nodes depending on these generic params. + # XXX: This looks quite similar to the code in matchUserTypeClass, + # perhaps the code can be extracted in a shared function. + openScope(c) + let genericTyp = header.base + for i in 0 .. (genericTyp.len - 2): + let genParam = genericTyp[i] + var param: PSym + + template paramSym(kind): untyped = + newSym(kind, genParam.sym.name, genericTyp.sym, genParam.sym.info) + + if genParam.kind == tyStatic: + param = paramSym skConst + param.ast = header[i+1].n + param.typ = header[i+1] + else: + param = paramSym skType + param.typ = makeTypeDesc(c, header[i+1]) + + # this scope was not created by the user, + # unused params shoudn't be reported. + param.flags.incl sfUsed + addDecl(c, param) + result = replaceTypeVarsT(cl, header) + closeScope(c) proc instantiateProcType(c: PContext, pt: TIdTable, prc: PSym, info: TLineInfo) = @@ -203,14 +236,12 @@ proc instantiateProcType(c: PContext, pt: TIdTable, # at this point semtypinst have to become part of sem, because it # will need to use openScope, addDecl, etc. #addDecl(c, prc) - pushInfoContext(info) var typeMap = initLayeredTypeMap(pt) var cl = initTypeVars(c, addr(typeMap), info, nil) var result = instCopyType(cl, prc.typ) let originalParams = result.n result.n = originalParams.shallowCopy - for i in 1 ..< result.len: # twrong_field_caching requires these 'resetIdTable' calls: if i > 1: @@ -218,7 +249,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable, resetIdTable(cl.localCache) result.sons[i] = replaceTypeVarsT(cl, result.sons[i]) propagateToOwner(result, result.sons[i]) - internalAssert originalParams[i].kind == nkSym + internalAssert c.config, originalParams[i].kind == nkSym when true: let oldParam = originalParams[i].sym let param = copySym(oldParam) @@ -255,9 +286,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, ## The `pt` parameter is a type-unsafe mapping table used to link generic ## parameters to their concrete types within the generic instance. # no need to instantiate generic templates/macros: - internalAssert fn.kind notin {skMacro, skTemplate} + internalAssert c.config, fn.kind notin {skMacro, skTemplate} # generates an instantiated proc - if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep") + if c.instCounter > 1000: internalError(c.config, fn.ast.info, "nesting too deep") inc(c.instCounter) # careful! we copy the whole AST including the possibly nil body! var n = copyTree(fn.ast) @@ -276,7 +307,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, openScope(c) let gp = n.sons[genericParamsPos] - internalAssert gp.kind != nkEmpty + internalAssert c.config, gp.kind != nkEmpty n.sons[namePos] = newSymNode(result) pushInfoContext(info) var entry = TInstantiation.new @@ -316,7 +347,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, if c.inGenericContext == 0: instantiateBody(c, n, fn.typ.n, result, fn) sideEffectsCheck(c, result) - paramsTypeCheck(c, result.typ) + if result.magic != mSlice: + # 'toOpenArray' is special and it is allowed to return 'openArray': + paramsTypeCheck(c, result.typ) else: result = oldPrc popProcCon(c) diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim index fe9bb6c8d..02c56c035 100644 --- a/compiler/semmacrosanity.nim +++ b/compiler/semmacrosanity.nim @@ -10,7 +10,7 @@ ## Implements type sanity checking for ASTs resulting from macros. Lots of ## room for improvement here. -import ast, astalgo, msgs, types +import ast, astalgo, msgs, types, options proc ithField(n: PNode, field: var int): PSym = result = nil @@ -20,7 +20,7 @@ proc ithField(n: PNode, field: var int): PSym = result = ithField(n.sons[i], field) if result != nil: return of nkRecCase: - if n.sons[0].kind != nkSym: internalError(n.info, "ithField") + if n.sons[0].kind != nkSym: return result = ithField(n.sons[0], field) if result != nil: return for i in countup(1, sonsLen(n) - 1): @@ -28,13 +28,13 @@ proc ithField(n: PNode, field: var int): PSym = of nkOfBranch, nkElse: result = ithField(lastSon(n.sons[i]), field) if result != nil: return - else: internalError(n.info, "ithField(record case branch)") + else: discard of nkSym: if field == 0: result = n.sym else: dec(field) else: discard -proc annotateType*(n: PNode, t: PType) = +proc annotateType*(n: PNode, t: PType; conf: ConfigRef) = let x = t.skipTypes(abstractInst+{tyRange}) # Note: x can be unequal to t and we need to be careful to use 't' # to not to skip tyGenericInst @@ -46,50 +46,50 @@ proc annotateType*(n: PNode, t: PType) = var j = i-1 let field = x.n.ithField(j) if field.isNil: - globalError n.info, "invalid field at index " & $i + globalError conf, n.info, "invalid field at index " & $i else: - internalAssert(n.sons[i].kind == nkExprColonExpr) - annotateType(n.sons[i].sons[1], field.typ) - of nkPar: + internalAssert(conf, n.sons[i].kind == nkExprColonExpr) + annotateType(n.sons[i].sons[1], field.typ, conf) + of nkPar, nkTupleConstr: if x.kind == tyTuple: n.typ = t for i in 0 ..< n.len: - if i >= x.len: globalError n.info, "invalid field at index " & $i - else: annotateType(n.sons[i], x.sons[i]) + if i >= x.len: globalError conf, n.info, "invalid field at index " & $i + else: annotateType(n.sons[i], x.sons[i], conf) elif x.kind == tyProc and x.callConv == ccClosure: n.typ = t else: - globalError(n.info, "() must have a tuple type") + globalError(conf, n.info, "() must have a tuple type") of nkBracket: if x.kind in {tyArray, tySequence, tyOpenArray}: n.typ = t - for m in n: annotateType(m, x.elemType) + for m in n: annotateType(m, x.elemType, conf) else: - globalError(n.info, "[] must have some form of array type") + globalError(conf, n.info, "[] must have some form of array type") of nkCurly: if x.kind in {tySet}: n.typ = t - for m in n: annotateType(m, x.elemType) + for m in n: annotateType(m, x.elemType, conf) else: - globalError(n.info, "{} must have the set type") + globalError(conf, n.info, "{} must have the set type") of nkFloatLit..nkFloat128Lit: if x.kind in {tyFloat..tyFloat128}: n.typ = t else: - globalError(n.info, "float literal must have some float type") + globalError(conf, n.info, "float literal must have some float type") of nkCharLit..nkUInt64Lit: if x.kind in {tyInt..tyUInt64, tyBool, tyChar, tyEnum}: n.typ = t else: - globalError(n.info, "integer literal must have some int type") + globalError(conf, n.info, "integer literal must have some int type") of nkStrLit..nkTripleStrLit: if x.kind in {tyString, tyCString}: n.typ = t else: - globalError(n.info, "string literal must be of some string type") + globalError(conf, n.info, "string literal must be of some string type") of nkNilLit: if x.kind in NilableTypes: n.typ = t else: - globalError(n.info, "nil literal must be of some pointer type") + globalError(conf, n.info, "nil literal must be of some pointer type") else: discard diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 0d0f2ee82..8515e971d 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -16,7 +16,7 @@ proc semAddr(c: PContext; n: PNode; isUnsafeAddr=false): PNode = if x.kind == nkSym: x.sym.flags.incl(sfAddrTaken) if isAssignable(c, x, isUnsafeAddr) notin {arLValue, arLocalLValue}: - localError(n.info, errExprHasNoAddress) + localError(c.config, n.info, errExprHasNoAddress) result.add x result.typ = makePtrType(c, x.typ) @@ -43,7 +43,7 @@ proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode = let x = copyTree(n) x.sons[0] = newIdentNode(getIdent"[]", n.info) bracketNotFoundError(c, x) - #localError(n.info, "could not resolve: " & $n) + #localError(c.config, n.info, "could not resolve: " & $n) result = n proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode = @@ -64,25 +64,28 @@ proc semAsgnOpr(c: PContext; n: PNode): PNode = proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode = var r = isPartOf(n[1], n[2]) - result = newIntNodeT(ord(r), n) + result = newIntNodeT(ord(r), n, c.graph) proc expectIntLit(c: PContext, n: PNode): int = let x = c.semConstExpr(c, n) case x.kind of nkIntLit..nkInt64Lit: result = int(x.intVal) - else: localError(n.info, errIntLiteralExpected) + else: localError(c.config, n.info, errIntLiteralExpected) proc semInstantiationInfo(c: PContext, n: PNode): PNode = - result = newNodeIT(nkPar, n.info, n.typ) + result = newNodeIT(nkTupleConstr, n.info, n.typ) let idx = expectIntLit(c, n.sons[1]) let useFullPaths = expectIntLit(c, n.sons[2]) let info = getInfoContext(idx) - var filename = newNodeIT(nkStrLit, n.info, getSysType(tyString)) + var filename = newNodeIT(nkStrLit, n.info, getSysType(c.graph, n.info, tyString)) filename.strVal = if useFullPaths != 0: info.toFullPath else: info.toFilename - var line = newNodeIT(nkIntLit, n.info, getSysType(tyInt)) + var line = newNodeIT(nkIntLit, n.info, getSysType(c.graph, n.info, tyInt)) line.intVal = toLinenumber(info) + var column = newNodeIT(nkIntLit, n.info, getSysType(c.graph, n.info, tyInt)) + column.intVal = toColumn(info) result.add(filename) result.add(line) + result.add(column) proc toNode(t: PType, i: TLineInfo): PNode = result = newNodeIT(nkType, i, t) @@ -107,10 +110,10 @@ proc uninstantiate(t: PType): PType = of tyCompositeTypeClass: uninstantiate t.sons[1] else: t -proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode = - const skippedTypes = {tyTypeDesc, tyAlias} +proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym): PNode = + const skippedTypes = {tyTypeDesc, tyAlias, tySink} let trait = traitCall[0] - internalAssert trait.kind == nkSym + internalAssert c.config, trait.kind == nkSym var operand = operand.skipTypes(skippedTypes) template operand2: PType = @@ -137,7 +140,7 @@ proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode = of "genericHead": var res = uninstantiate(operand) if res == operand and res.kind notin tyMagicGenerics: - localError(traitCall.info, + localError(c.config, traitCall.info, "genericHead expects a generic type. The given type was " & typeToString(operand)) return newType(tyError, context).toNode(traitCall.info) @@ -145,22 +148,22 @@ proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode = of "stripGenericParams": result = uninstantiate(operand).toNode(traitCall.info) of "supportsCopyMem": - let t = operand.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred}) + let t = operand.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink, tyInferred}) let complexObj = containsGarbageCollectedRef(t) or hasDestructor(t) - result = newIntNodeT(ord(not complexObj), traitCall) + result = newIntNodeT(ord(not complexObj), traitCall, c.graph) else: - localError(traitCall.info, "unknown trait") + localError(c.config, traitCall.info, "unknown trait") result = emptyNode proc semTypeTraits(c: PContext, n: PNode): PNode = - checkMinSonsLen(n, 2) + checkMinSonsLen(n, 2, c.config) let t = n.sons[1].typ - internalAssert t != nil and t.kind == tyTypeDesc + internalAssert c.config, t != nil and t.kind == tyTypeDesc if t.sonsLen > 0: # This is either a type known to sem or a typedesc # param to a regular proc (again, known at instantiation) - result = evalTypeTrait(n, t, getCurrOwner(c)) + result = evalTypeTrait(c, n, t, getCurrOwner(c)) else: # a typedesc variable, pass unmodified to evals result = n @@ -173,7 +176,7 @@ proc semOrd(c: PContext, n: PNode): PNode = elif parType.kind == tySet: result.typ = makeRangeType(c, firstOrd(parType), lastOrd(parType), n.info) else: - localError(n.info, errOrdinalTypeExpected) + localError(c.config, n.info, errOrdinalTypeExpected) result.typ = errorType(c) proc semBindSym(c: PContext, n: PNode): PNode = @@ -182,13 +185,13 @@ proc semBindSym(c: PContext, n: PNode): PNode = let sl = semConstExpr(c, n.sons[1]) if sl.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}: - localError(n.sons[1].info, errStringLiteralExpected) + localError(c.config, n.sons[1].info, errStringLiteralExpected) return errorNode(c, n) let isMixin = semConstExpr(c, n.sons[2]) if isMixin.kind != nkIntLit or isMixin.intVal < 0 or isMixin.intVal > high(TSymChoiceRule).int: - localError(n.sons[2].info, errConstExprExpected) + localError(c.config, n.sons[2].info, errConstExprExpected) return errorNode(c, n) let id = newIdentNode(getIdent(sl.strVal), n.info) @@ -196,6 +199,10 @@ proc semBindSym(c: PContext, n: PNode): PNode = if s != nil: # we need to mark all symbols: var sc = symChoice(c, id, s, TSymChoiceRule(isMixin.intVal)) + if not (c.inStaticContext > 0 or getCurrOwner(c).isCompileTimeProc): + # inside regular code, bindSym resolves to the sym-choice + # nodes (see tinspectsymbol) + return sc result.add(sc) else: errorUndeclaredIdentifier(c, n.sons[1].info, sl.strVal) @@ -218,9 +225,9 @@ proc semOf(c: PContext, n: PNode): PNode = let y = skipTypes(n.sons[2].typ, abstractPtrs-{tyTypeDesc}) if x.kind == tyTypeDesc or y.kind != tyTypeDesc: - localError(n.info, errXExpectsObjectTypes, "of") + localError(c.config, n.info, "'of' takes object types") elif b.kind != tyObject or a.kind != tyObject: - localError(n.info, errXExpectsObjectTypes, "of") + localError(c.config, n.info, "'of' takes object types") else: let diff = inheritanceDiff(a, b) # | returns: 0 iff `a` == `b` @@ -229,26 +236,26 @@ proc semOf(c: PContext, n: PNode): PNode = # | returns: `maxint` iff `a` and `b` are not compatible at all if diff <= 0: # optimize to true: - message(n.info, hintConditionAlwaysTrue, renderTree(n)) + message(c.config, n.info, hintConditionAlwaysTrue, renderTree(n)) result = newIntNode(nkIntLit, 1) result.info = n.info - result.typ = getSysType(tyBool) + result.typ = getSysType(c.graph, n.info, tyBool) return result elif diff == high(int): - localError(n.info, errXcanNeverBeOfThisSubtype, typeToString(a)) + localError(c.config, n.info, "'$1' cannot be of this subtype" % typeToString(a)) else: - localError(n.info, errXExpectsTwoArguments, "of") - n.typ = getSysType(tyBool) + localError(c.config, n.info, "'of' takes 2 arguments") + n.typ = getSysType(c.graph, n.info, tyBool) result = n proc magicsAfterOverloadResolution(c: PContext, n: PNode, flags: TExprFlags): PNode = case n[0].sym.magic of mAddr: - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.config) result = semAddr(c, n.sons[1], n[0].sym.name.s == "unsafeAddr") of mTypeOf: - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.config) result = semTypeOf(c, n.sons[1]) of mArrGet: result = semArrGet(c, n, flags) of mArrPut: result = semArrPut(c, n, flags) @@ -260,8 +267,8 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, of mIsPartOf: result = semIsPartOf(c, n, flags) of mTypeTrait: result = semTypeTraits(c, n) of mAstToStr: - result = newStrNodeT(renderTree(n[1], {renderNoComments}), n) - result.typ = getSysType(tyString) + result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, c.graph) + result.typ = getSysType(c.graph, n.info, tyString) of mInstantiationInfo: result = semInstantiationInfo(c, n) of mOrd: result = semOrd(c, n) of mOf: result = semOf(c, n) @@ -274,11 +281,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, of mDotDot: result = n of mRoof: - localError(n.info, "builtin roof operator is not supported anymore") + localError(c.config, n.info, "builtin roof operator is not supported anymore") of mPlugin: let plugin = getPlugin(n[0].sym) if plugin.isNil: - localError(n.info, "cannot find plugin " & n[0].sym.name.s) + localError(c.config, n.info, "cannot find plugin " & n[0].sym.name.s) result = n else: result = plugin(c, n) diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index a0bf084fa..1e0802953 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -39,32 +39,32 @@ proc mergeInitStatus(existing: var InitStatus, newStatus: InitStatus) = of initUnknown: discard -proc invalidObjConstr(n: PNode) = +proc invalidObjConstr(c: PContext, n: PNode) = if n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s[0] == ':': - localError(n.info, "incorrect object construction syntax; use a space after the colon") + localError(c.config, n.info, "incorrect object construction syntax; use a space after the colon") else: - localError(n.info, "incorrect object construction syntax") + localError(c.config, n.info, "incorrect object construction syntax") -proc locateFieldInInitExpr(field: PSym, initExpr: PNode): PNode = +proc locateFieldInInitExpr(c: PContext, field: PSym, initExpr: PNode): PNode = # Returns the assignment nkExprColonExpr node or nil let fieldId = field.name.id for i in 1 ..< initExpr.len: let assignment = initExpr[i] if assignment.kind != nkExprColonExpr: - invalidObjConstr(assignment) + invalidObjConstr(c, assignment) continue - if fieldId == considerQuotedIdent(assignment[0]).id: + if fieldId == considerQuotedIdent(c.config, assignment[0]).id: return assignment proc semConstrField(c: PContext, flags: TExprFlags, field: PSym, initExpr: PNode): PNode = - let assignment = locateFieldInInitExpr(field, initExpr) + let assignment = locateFieldInInitExpr(c, field, initExpr) if assignment != nil: if nfSem in assignment.flags: return assignment[1] if not fieldVisible(c, field): - localError(initExpr.info, - "the field '$1' is not accessible.", [field.name.s]) + localError(c.config, initExpr.info, + "the field '$1' is not accessible." % [field.name.s]) return var initValue = semExprFlagDispatched(c, assignment[1], flags) @@ -76,8 +76,10 @@ proc semConstrField(c: PContext, flags: TExprFlags, return initValue proc caseBranchMatchesExpr(branch, matched: PNode): bool = - for i in 0 .. (branch.len - 2): - if exprStructuralEquivalent(branch[i], matched): + for i in 0 .. branch.len-2: + if branch[i].kind == nkRange: + if overlap(branch[i], matched): return true + elif exprStructuralEquivalent(branch[i], matched): return true return false @@ -99,25 +101,25 @@ iterator directFieldsInRecList(recList: PNode): PNode = if recList.kind == nkSym: yield recList else: - internalAssert recList.kind == nkRecList + doAssert recList.kind == nkRecList for field in recList: if field.kind != nkSym: continue yield field template quoteStr(s: string): string = "'" & s & "'" -proc fieldsPresentInInitExpr(fieldsRecList, initExpr: PNode): string = +proc fieldsPresentInInitExpr(c: PContext, fieldsRecList, initExpr: PNode): string = result = "" for field in directFieldsInRecList(fieldsRecList): - let assignment = locateFieldInInitExpr(field.sym, initExpr) + let assignment = locateFieldInInitExpr(c, field.sym, initExpr) if assignment != nil: if result.len != 0: result.add ", " result.add field.sym.name.s.quoteStr -proc missingMandatoryFields(fieldsRecList, initExpr: PNode): string = +proc missingMandatoryFields(c: PContext, fieldsRecList, initExpr: PNode): string = for r in directFieldsInRecList(fieldsRecList): if {tfNotNil, tfNeedsInit} * r.sym.typ.flags != {}: - let assignment = locateFieldInInitExpr(r.sym, initExpr) + let assignment = locateFieldInInitExpr(c, r.sym, initExpr) if assignment == nil: if result == nil: result = r.sym.name.s @@ -125,10 +127,10 @@ proc missingMandatoryFields(fieldsRecList, initExpr: PNode): string = result.add ", " result.add r.sym.name.s -proc checkForMissingFields(recList, initExpr: PNode) = - let missing = missingMandatoryFields(recList, initExpr) +proc checkForMissingFields(c: PContext, recList, initExpr: PNode) = + let missing = missingMandatoryFields(c, recList, initExpr) if missing != nil: - localError(initExpr.info, "fields not initialized: $1.", [missing]) + localError(c.config, initExpr.info, "fields not initialized: $1.", [missing]) proc semConstructFields(c: PContext, recNode: PNode, initExpr: PNode, flags: TExprFlags): InitStatus = @@ -144,14 +146,14 @@ proc semConstructFields(c: PContext, recNode: PNode, template fieldsPresentInBranch(branchIdx: int): string = let branch = recNode[branchIdx] let fields = branch[branch.len - 1] - fieldsPresentInInitExpr(fields, initExpr) + fieldsPresentInInitExpr(c, fields, initExpr) template checkMissingFields(branchNode: PNode) = let fields = branchNode[branchNode.len - 1] - checkForMissingFields(fields, initExpr) + checkForMissingFields(c, fields, initExpr) - let discriminator = recNode.sons[0]; - internalAssert discriminator.kind == nkSym + let discriminator = recNode.sons[0] + internalAssert c.config, discriminator.kind == nkSym var selectedBranch = -1 for i in 1 ..< recNode.len: @@ -162,9 +164,9 @@ proc semConstructFields(c: PContext, recNode: PNode, if selectedBranch != -1: let prevFields = fieldsPresentInBranch(selectedBranch) let currentFields = fieldsPresentInBranch(i) - localError(initExpr.info, - "The fields ($1) and ($2) cannot be initialized together, " & - "because they are from conflicting branches in the case object.", + localError(c.config, initExpr.info, + ("The fields '$1' and '$2' cannot be initialized together, " & + "because they are from conflicting branches in the case object.") % [prevFields, currentFields]) result = initConflict else: @@ -177,9 +179,9 @@ proc semConstructFields(c: PContext, recNode: PNode, discriminator.sym, initExpr) if discriminatorVal == nil: let fields = fieldsPresentInBranch(selectedBranch) - localError(initExpr.info, - "you must provide a compile-time value for the discriminator '$1' " & - "in order to prove that it's safe to initialize $2.", + localError(c.config, initExpr.info, + ("you must provide a compile-time value for the discriminator '$1' " & + "in order to prove that it's safe to initialize $2.") % [discriminator.sym.name.s, fields]) mergeInitStatus(result, initNone) else: @@ -187,7 +189,7 @@ proc semConstructFields(c: PContext, recNode: PNode, template wrongBranchError(i) = let fields = fieldsPresentInBranch(i) - localError(initExpr.info, + localError(c.config, initExpr.info, "a case selecting discriminator '$1' with value '$2' " & "appears in the object construction, but the field(s) $3 " & "are in conflict with this value.", @@ -217,7 +219,7 @@ proc semConstructFields(c: PContext, recNode: PNode, # value was given to the discrimator. We can assume that it will be # initialized to zero and this will select a particular branch as # a result: - let matchedBranch = recNode.pickCaseBranch newIntLit(0) + let matchedBranch = recNode.pickCaseBranch newIntLit(c.graph, initExpr.info, 0) checkMissingFields matchedBranch else: result = initPartial @@ -237,20 +239,17 @@ proc semConstructFields(c: PContext, recNode: PNode, result = if e != nil: initFull else: initNone else: - internalAssert false + internalAssert c.config, false proc semConstructType(c: PContext, initExpr: PNode, t: PType, flags: TExprFlags): InitStatus = var t = t result = initUnknown - while true: let status = semConstructFields(c, t.n, initExpr, flags) mergeInitStatus(result, status) - if status in {initPartial, initNone, initUnknown}: - checkForMissingFields t.n, initExpr - + checkForMissingFields c, t.n, initExpr let base = t.sons[0] if base == nil: break t = skipTypes(base, skipPtrs) @@ -260,10 +259,14 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = result = newNodeIT(nkObjConstr, n.info, t) for child in n: result.add child - t = skipTypes(t, {tyGenericInst, tyAlias}) - if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst, tyAlias}) + if t == nil: + localError(c.config, n.info, errGenerated, "object constructor needs an object type") + return + + t = skipTypes(t, {tyGenericInst, tyAlias, tySink}) + if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst, tyAlias, tySink}) if t.kind != tyObject: - localError(n.info, errGenerated, "object constructor needs an object type") + localError(c.config, n.info, errGenerated, "object constructor needs an object type") return # Check if the object is fully initialized by recursively testing each @@ -290,17 +293,16 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = let field = result[i] if nfSem notin field.flags: if field.kind != nkExprColonExpr: - invalidObjConstr(field) + invalidObjConstr(c, field) continue - let id = considerQuotedIdent(field[0]) + let id = considerQuotedIdent(c.config, field[0]) # This node was not processed. There are two possible reasons: # 1) It was shadowed by a field with the same name on the left for j in 1 ..< i: - let prevId = considerQuotedIdent(result[j][0]) + let prevId = considerQuotedIdent(c.config, result[j][0]) if prevId.id == id.id: - localError(field.info, errFieldInitTwice, id.s) + localError(c.config, field.info, errFieldInitTwice % id.s) return # 2) No such field exists in the constructed type - localError(field.info, errUndeclaredFieldX, id.s) + localError(c.config, field.info, errUndeclaredFieldX % id.s) return - diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim index 057ade01d..ea5aab628 100644 --- a/compiler/semparallel.nim +++ b/compiler/semparallel.nim @@ -23,7 +23,8 @@ import ast, astalgo, idents, lowerings, magicsys, guards, sempass2, msgs, - renderer, types + renderer, types, modulegraphs, options + from trees import getMagic from strutils import `%` @@ -73,12 +74,15 @@ type # the 'parallel' section currentSpawnId: int inLoop: int + graph: ModuleGraph -proc initAnalysisCtx(): AnalysisCtx = +proc initAnalysisCtx(g: ModuleGraph): AnalysisCtx = result.locals = @[] result.slices = @[] result.args = @[] - result.guards = @[] + result.guards.s = @[] + result.guards.o = initOperators(g) + result.graph = g proc lookupSlot(c: AnalysisCtx; s: PSym): int = for i in 0..<c.locals.len: @@ -117,7 +121,7 @@ proc checkLocal(c: AnalysisCtx; n: PNode) = if isLocal(n): let s = c.lookupSlot(n.sym) if s >= 0 and c.locals[s].stride != nil: - localError(n.info, "invalid usage of counter after increment") + localError(c.graph.config, n.info, "invalid usage of counter after increment") else: for i in 0 ..< n.safeLen: checkLocal(c, n.sons[i]) @@ -126,14 +130,14 @@ template `?`(x): untyped = x.renderTree proc checkLe(c: AnalysisCtx; a, b: PNode) = case proveLe(c.guards, a, b) of impUnknown: - localError(a.info, "cannot prove: " & ?a & " <= " & ?b & " (bounds check)") + localError(c.graph.config, a.info, "cannot prove: " & ?a & " <= " & ?b & " (bounds check)") of impYes: discard of impNo: - localError(a.info, "can prove: " & ?a & " > " & ?b & " (bounds check)") + localError(c.graph.config, a.info, "can prove: " & ?a & " > " & ?b & " (bounds check)") proc checkBounds(c: AnalysisCtx; arr, idx: PNode) = checkLe(c, arr.lowBound, idx) - checkLe(c, idx, arr.highBound) + checkLe(c, idx, arr.highBound(c.guards.o)) proc addLowerBoundAsFacts(c: var AnalysisCtx) = for v in c.locals: @@ -142,34 +146,34 @@ proc addLowerBoundAsFacts(c: var AnalysisCtx) = proc addSlice(c: var AnalysisCtx; n: PNode; x, le, ri: PNode) = checkLocal(c, n) - let le = le.canon - let ri = ri.canon + let le = le.canon(c.guards.o) + let ri = ri.canon(c.guards.o) # perform static bounds checking here; and not later! - let oldState = c.guards.len + let oldState = c.guards.s.len addLowerBoundAsFacts(c) c.checkBounds(x, le) c.checkBounds(x, ri) - c.guards.setLen(oldState) + c.guards.s.setLen(oldState) c.slices.add((x, le, ri, c.currentSpawnId, c.inLoop > 0)) -proc overlap(m: TModel; x,y,c,d: PNode) = +proc overlap(m: TModel; conf: ConfigRef; x,y,c,d: PNode) = # X..Y and C..D overlap iff (X <= D and C <= Y) case proveLe(m, c, y) of impUnknown: case proveLe(m, x, d) of impNo: discard of impUnknown, impYes: - localError(x.info, + localError(conf, x.info, "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" % [?c, ?y, ?x, ?y, ?c, ?d]) of impYes: case proveLe(m, x, d) of impUnknown: - localError(x.info, + localError(conf, x.info, "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" % [?x, ?d, ?x, ?y, ?c, ?d]) of impYes: - localError(x.info, "($#)..($#) not disjoint from ($#)..($#)" % + localError(conf, x.info, "($#)..($#) not disjoint from ($#)..($#)" % [?c, ?y, ?x, ?y, ?c, ?d]) of impNo: discard of impNo: discard @@ -187,7 +191,7 @@ proc subStride(c: AnalysisCtx; n: PNode): PNode = if isLocal(n): let s = c.lookupSlot(n.sym) if s >= 0 and c.locals[s].stride != nil: - result = n +@ c.locals[s].stride.intVal + result = buildAdd(n, c.locals[s].stride.intVal, c.guards.o) else: result = n elif n.safeLen > 0: @@ -203,7 +207,7 @@ proc checkSlicesAreDisjoint(c: var AnalysisCtx) = addLowerBoundAsFacts(c) # Every slice used in a loop needs to be disjoint with itself: for x,a,b,id,inLoop in items(c.slices): - if inLoop: overlap(c.guards, a,b, c.subStride(a), c.subStride(b)) + if inLoop: overlap(c.guards, c.graph.config, a,b, c.subStride(a), c.subStride(b)) # Another tricky example is: # while true: # spawn f(a[i]) @@ -231,21 +235,21 @@ proc checkSlicesAreDisjoint(c: var AnalysisCtx) = # XXX strictly speaking, 'or' is not correct here and it needs to # be 'and'. However this prevents too many obviously correct programs # like f(a[0..x]); for i in x+1 .. a.high: f(a[i]) - overlap(c.guards, x.a, x.b, y.a, y.b) + overlap(c.guards, c.graph.config, x.a, x.b, y.a, y.b) elif (let k = simpleSlice(x.a, x.b); let m = simpleSlice(y.a, y.b); k >= 0 and m >= 0): # ah I cannot resist the temptation and add another sweet heuristic: # if both slices have the form (i+k)..(i+k) and (i+m)..(i+m) we # check they are disjoint and k < stride and m < stride: - overlap(c.guards, x.a, x.b, y.a, y.b) + overlap(c.guards, c.graph.config, x.a, x.b, y.a, y.b) let stride = min(c.stride(x.a), c.stride(y.a)) if k < stride and m < stride: discard else: - localError(x.x.info, "cannot prove ($#)..($#) disjoint from ($#)..($#)" % + localError(c.graph.config, x.x.info, "cannot prove ($#)..($#) disjoint from ($#)..($#)" % [?x.a, ?x.b, ?y.a, ?y.b]) else: - localError(x.x.info, "cannot prove ($#)..($#) disjoint from ($#)..($#)" % + localError(c.graph.config, x.x.info, "cannot prove ($#)..($#) disjoint from ($#)..($#)" % [?x.a, ?x.b, ?y.a, ?y.b]) proc analyse(c: var AnalysisCtx; n: PNode) @@ -292,31 +296,31 @@ proc analyseCall(c: var AnalysisCtx; n: PNode; op: PSym) = proc analyseCase(c: var AnalysisCtx; n: PNode) = analyse(c, n.sons[0]) - let oldFacts = c.guards.len + let oldFacts = c.guards.s.len for i in 1..<n.len: let branch = n.sons[i] - setLen(c.guards, oldFacts) + setLen(c.guards.s, oldFacts) addCaseBranchFacts(c.guards, n, i) for i in 0 ..< branch.len: analyse(c, branch.sons[i]) - setLen(c.guards, oldFacts) + setLen(c.guards.s, oldFacts) proc analyseIf(c: var AnalysisCtx; n: PNode) = analyse(c, n.sons[0].sons[0]) - let oldFacts = c.guards.len - addFact(c.guards, canon(n.sons[0].sons[0])) + let oldFacts = c.guards.s.len + addFact(c.guards, canon(n.sons[0].sons[0], c.guards.o)) analyse(c, n.sons[0].sons[1]) for i in 1..<n.len: let branch = n.sons[i] - setLen(c.guards, oldFacts) + setLen(c.guards.s, oldFacts) for j in 0..i-1: - addFactNeg(c.guards, canon(n.sons[j].sons[0])) + addFactNeg(c.guards, canon(n.sons[j].sons[0], c.guards.o)) if branch.len > 1: - addFact(c.guards, canon(branch.sons[0])) + addFact(c.guards, canon(branch.sons[0], c.guards.o)) for i in 0 ..< branch.len: analyse(c, branch.sons[i]) - setLen(c.guards, oldFacts) + setLen(c.guards.s, oldFacts) proc analyse(c: var AnalysisCtx; n: PNode) = case n.kind @@ -345,10 +349,11 @@ proc analyse(c: var AnalysisCtx; n: PNode) = if n[0].kind == nkSym: analyseCall(c, n, n[0].sym) else: analyseSons(c, n) of nkBracketExpr: - c.addSlice(n, n[0], n[1], n[1]) + if n[0].typ != nil and skipTypes(n[0].typ, abstractVar).kind != tyTuple: + c.addSlice(n, n[0], n[1], n[1]) analyseSons(c, n) of nkReturnStmt, nkRaiseStmt, nkTryStmt: - localError(n.info, "invalid control flow for 'parallel'") + localError(c.graph.config, n.info, "invalid control flow for 'parallel'") # 'break' that leaves the 'parallel' section is not valid either # or maybe we should generate a 'try' XXX of nkVarSection, nkLetSection: @@ -364,7 +369,7 @@ proc analyse(c: var AnalysisCtx; n: PNode) = if it[j].isLocal: let slot = c.getSlot(it[j].sym) if slot.lower.isNil: slot.lower = value - else: internalError(it.info, "slot already has a lower bound") + else: internalError(c.graph.config, it.info, "slot already has a lower bound") if not isSpawned: analyse(c, value) of nkCaseStmt: analyseCase(c, n) of nkWhen, nkIfStmt, nkIfExpr: analyseIf(c, n) @@ -377,14 +382,14 @@ proc analyse(c: var AnalysisCtx; n: PNode) = else: # loop may never execute: let oldState = c.locals.len - let oldFacts = c.guards.len - addFact(c.guards, canon(n.sons[0])) + let oldFacts = c.guards.s.len + addFact(c.guards, canon(n.sons[0], c.guards.o)) analyse(c, n.sons[1]) setLen(c.locals, oldState) - setLen(c.guards, oldFacts) + setLen(c.guards.s, oldFacts) # we know after the loop the negation holds: if not hasSubnodeWith(n.sons[1], nkBreakStmt): - addFactNeg(c.guards, canon(n.sons[0])) + addFactNeg(c.guards, canon(n.sons[0], c.guards.o)) dec c.inLoop of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkConstSection, nkPragma, nkFuncDef: @@ -392,13 +397,13 @@ proc analyse(c: var AnalysisCtx; n: PNode) = else: analyseSons(c, n) -proc transformSlices(n: PNode): PNode = +proc transformSlices(g: ModuleGraph; n: PNode): PNode = if n.kind in nkCallKinds and n[0].kind == nkSym: let op = n[0].sym if op.name.s == "[]" and op.fromSystem: result = copyNode(n) - let opSlice = newSymNode(createMagic("slice", mSlice)) - opSlice.typ = getSysType(tyInt) + let opSlice = newSymNode(createMagic(g, "slice", mSlice)) + opSlice.typ = getSysType(g, n.info, tyInt) result.add opSlice result.add n[1] let slice = n[2].skipStmtList @@ -408,49 +413,49 @@ proc transformSlices(n: PNode): PNode = if n.safeLen > 0: result = shallowCopy(n) for i in 0 ..< n.len: - result.sons[i] = transformSlices(n.sons[i]) + result.sons[i] = transformSlices(g, n.sons[i]) else: result = n -proc transformSpawn(owner: PSym; n, barrier: PNode): PNode -proc transformSpawnSons(owner: PSym; n, barrier: PNode): PNode = +proc transformSpawn(g: ModuleGraph; owner: PSym; n, barrier: PNode): PNode +proc transformSpawnSons(g: ModuleGraph; owner: PSym; n, barrier: PNode): PNode = result = shallowCopy(n) for i in 0 ..< n.len: - result.sons[i] = transformSpawn(owner, n.sons[i], barrier) + result.sons[i] = transformSpawn(g, owner, n.sons[i], barrier) -proc transformSpawn(owner: PSym; n, barrier: PNode): PNode = +proc transformSpawn(g: ModuleGraph; owner: PSym; n, barrier: PNode): PNode = case n.kind of nkVarSection, nkLetSection: result = nil for it in n: let b = it.lastSon if getMagic(b) == mSpawn: - if it.len != 3: localError(it.info, "invalid context for 'spawn'") - let m = transformSlices(b) + if it.len != 3: localError(g.config, it.info, "invalid context for 'spawn'") + let m = transformSlices(g, b) if result.isNil: result = newNodeI(nkStmtList, n.info) result.add n let t = b[1][0].typ.sons[0] if spawnResult(t, true) == srByVar: - result.add wrapProcForSpawn(owner, m, b.typ, barrier, it[0]) + result.add wrapProcForSpawn(g, owner, m, b.typ, barrier, it[0]) it.sons[it.len-1] = emptyNode else: - it.sons[it.len-1] = wrapProcForSpawn(owner, m, b.typ, barrier, nil) + it.sons[it.len-1] = wrapProcForSpawn(g, owner, m, b.typ, barrier, nil) if result.isNil: result = n of nkAsgn, nkFastAsgn: let b = n[1] if getMagic(b) == mSpawn and (let t = b[1][0].typ.sons[0]; spawnResult(t, true) == srByVar): - let m = transformSlices(b) - return wrapProcForSpawn(owner, m, b.typ, barrier, n[0]) - result = transformSpawnSons(owner, n, barrier) + let m = transformSlices(g, b) + return wrapProcForSpawn(g, owner, m, b.typ, barrier, n[0]) + result = transformSpawnSons(g, owner, n, barrier) of nkCallKinds: if getMagic(n) == mSpawn: - result = transformSlices(n) - return wrapProcForSpawn(owner, result, n.typ, barrier, nil) - result = transformSpawnSons(owner, n, barrier) + result = transformSlices(g, n) + return wrapProcForSpawn(g, owner, result, n.typ, barrier, nil) + result = transformSpawnSons(g, owner, n, barrier) elif n.safeLen > 0: - result = transformSpawnSons(owner, n, barrier) + result = transformSpawnSons(g, owner, n, barrier) else: result = n @@ -460,7 +465,7 @@ proc checkArgs(a: var AnalysisCtx; n: PNode) = proc generateAliasChecks(a: AnalysisCtx; result: PNode) = discard "too implement" -proc liftParallel*(owner: PSym; n: PNode): PNode = +proc liftParallel*(g: ModuleGraph; owner: PSym; n: PNode): PNode = # this needs to be called after the 'for' loop elimination # first pass: @@ -469,17 +474,17 @@ proc liftParallel*(owner: PSym; n: PNode): PNode = # - detect used arguments #echo "PAR ", renderTree(n) - var a = initAnalysisCtx() + var a = initAnalysisCtx(g) let body = n.lastSon analyse(a, body) if a.spawns == 0: - localError(n.info, "'parallel' section without 'spawn'") + localError(g.config, n.info, "'parallel' section without 'spawn'") checkSlicesAreDisjoint(a) checkArgs(a, body) var varSection = newNodeI(nkVarSection, n.info) var temp = newSym(skTemp, getIdent"barrier", owner, n.info) - temp.typ = magicsys.getCompilerProc("Barrier").typ + temp.typ = magicsys.getCompilerProc(g, "Barrier").typ incl(temp.flags, sfFromGeneric) let tempNode = newSymNode(temp) varSection.addVar tempNode @@ -488,6 +493,6 @@ proc liftParallel*(owner: PSym; n: PNode): PNode = result = newNodeI(nkStmtList, n.info) generateAliasChecks(a, result) result.add varSection - result.add callCodegenProc("openBarrier", barrier) - result.add transformSpawn(owner, body, barrier) - result.add callCodegenProc("closeBarrier", barrier) + result.add callCodegenProc(g, "openBarrier", barrier) + result.add transformSpawn(g, owner, body, barrier) + result.add callCodegenProc(g, "closeBarrier", barrier) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index d427750e4..b66d7d9f2 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -9,7 +9,8 @@ import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, - wordrecg, strutils, options, guards, writetracking + wordrecg, strutils, options, guards, writetracking, configuration, + modulegraphs when defined(useDfa): import dfa @@ -52,6 +53,8 @@ type locked: seq[PNode] # locked locations gcUnsafe, isRecursive, isToplevel, hasSideEffect, inEnforcedGcSafe: bool maxLockLevel, currLockLevel: TLockLevel + config: ConfigRef + graph: ModuleGraph PEffects = var TEffects proc `<`(a, b: TLockLevel): bool {.borrow.} @@ -72,24 +75,23 @@ proc getLockLevel(t: PType): TLockLevel = proc lockLocations(a: PEffects; pragma: PNode) = if pragma.kind != nkExprColonExpr: - localError(pragma.info, errGenerated, "locks pragma without argument") + localError(a.config, pragma.info, "locks pragma without argument") return var firstLL = TLockLevel(-1'i16) for x in pragma[1]: let thisLL = getLockLevel(x.typ) if thisLL != 0.TLockLevel: if thisLL < 0.TLockLevel or thisLL > MaxLockLevel.TLockLevel: - localError(x.info, "invalid lock level: " & $thisLL) + localError(a.config, x.info, "invalid lock level: " & $thisLL) elif firstLL < 0.TLockLevel: firstLL = thisLL elif firstLL != thisLL: - localError(x.info, errGenerated, + localError(a.config, x.info, "multi-lock requires the same static lock level for every operand") a.maxLockLevel = max(a.maxLockLevel, firstLL) a.locked.add x if firstLL >= 0.TLockLevel and firstLL != a.currLockLevel: if a.currLockLevel > 0.TLockLevel and a.currLockLevel <= firstLL: - localError(pragma.info, errGenerated, - "invalid nested locking") + localError(a.config, pragma.info, "invalid nested locking") a.currLockLevel = firstLL proc guardGlobal(a: PEffects; n: PNode; guard: PSym) = @@ -102,7 +104,7 @@ proc guardGlobal(a: PEffects; n: PNode; guard: PSym) = # message(n.info, warnUnguardedAccess, renderTree(n)) #else: if not a.isTopLevel: - localError(n.info, errGenerated, "unguarded access: " & renderTree(n)) + localError(a.config, n.info, "unguarded access: " & renderTree(n)) # 'guard*' are checks which are concerned with 'guard' annotations # (var x{.guard: y.}: int) @@ -125,7 +127,7 @@ proc guardDotAccess(a: PEffects; n: PNode) = if ty == nil: break ty = ty.skipTypes(skipPtrs) if field == nil: - localError(n.info, errGenerated, "invalid guard field: " & g.name.s) + localError(a.config, n.info, "invalid guard field: " & g.name.s) return g = field #ri.sym.guard = field @@ -138,13 +140,13 @@ proc guardDotAccess(a: PEffects; n: PNode) = for L in a.locked: #if a.guards.sameSubexprs(dot, L): return if guards.sameTree(dot, L): return - localError(n.info, errGenerated, "unguarded access: " & renderTree(n)) + localError(a.config, n.info, "unguarded access: " & renderTree(n)) else: guardGlobal(a, n, g) proc makeVolatile(a: PEffects; s: PSym) {.inline.} = template compileToCpp(a): untyped = - gCmd == cmdCompileToCpp or sfCompileToCpp in getModule(a.owner).flags + a.config.cmd == cmdCompileToCpp or sfCompileToCpp in getModule(a.owner).flags if a.inTryStmt > 0 and not compileToCpp(a): incl(s.flags, sfVolatile) @@ -167,9 +169,9 @@ proc initVarViaNew(a: PEffects, n: PNode) = elif isLocalVar(a, s): makeVolatile(a, s) -proc warnAboutGcUnsafe(n: PNode) = +proc warnAboutGcUnsafe(n: PNode; conf: ConfigRef) = #assert false - message(n.info, warnGcUnsafe, renderTree(n)) + message(conf, n.info, warnGcUnsafe, renderTree(n)) proc markGcUnsafe(a: PEffects; reason: PSym) = if not a.inEnforcedGcSafe: @@ -184,7 +186,7 @@ proc markGcUnsafe(a: PEffects; reason: PNode) = a.owner.gcUnsafetyReason = reason.sym else: a.owner.gcUnsafetyReason = newSym(skUnknown, getIdent("<unknown>"), - a.owner, reason.info) + a.owner, reason.info, {}) when true: template markSideEffect(a: PEffects; reason: typed) = @@ -194,42 +196,42 @@ else: a.hasSideEffect = true markGcUnsafe(a, reason) -proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet) = +proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet; conf: ConfigRef) = let u = s.gcUnsafetyReason if u != nil and not cycleCheck.containsOrIncl(u.id): let msgKind = if onlyWarning: warnGcUnsafe2 else: errGenerated case u.kind of skLet, skVar: - message(s.info, msgKind, + message(conf, s.info, msgKind, ("'$#' is not GC-safe as it accesses '$#'" & " which is a global using GC'ed memory") % [s.name.s, u.name.s]) of routineKinds: # recursive call *always* produces only a warning so the full error # message is printed: - listGcUnsafety(u, true, cycleCheck) - message(s.info, msgKind, + listGcUnsafety(u, true, cycleCheck, conf) + message(conf, s.info, msgKind, "'$#' is not GC-safe as it calls '$#'" % [s.name.s, u.name.s]) of skParam, skForVar: - message(s.info, msgKind, + message(conf, s.info, msgKind, "'$#' is not GC-safe as it performs an indirect call via '$#'" % [s.name.s, u.name.s]) else: - message(u.info, msgKind, + message(conf, u.info, msgKind, "'$#' is not GC-safe as it performs an indirect call here" % s.name.s) -proc listGcUnsafety(s: PSym; onlyWarning: bool) = +proc listGcUnsafety(s: PSym; onlyWarning: bool; conf: ConfigRef) = var cycleCheck = initIntSet() - listGcUnsafety(s, onlyWarning, cycleCheck) + listGcUnsafety(s, onlyWarning, cycleCheck, conf) proc useVar(a: PEffects, n: PNode) = let s = n.sym if isLocalVar(a, s): if s.id notin a.init: if {tfNeedsInit, tfNotNil} * s.typ.flags != {}: - message(n.info, warnProveInit, s.name.s) + message(a.config, n.info, warnProveInit, s.name.s) else: - message(n.info, warnUninit, s.name.s) + message(a.config, n.info, warnUninit, s.name.s) # prevent superfluous warnings about the same variable: a.init.add s.id if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet} and @@ -257,34 +259,30 @@ proc addToIntersection(inter: var TIntersection, s: int) = proc throws(tracked, n: PNode) = if n.typ == nil or n.typ.kind != tyError: tracked.add n -proc getEbase(): PType = - result = if getCompilerProc("Exception") != nil: sysTypeFromName"Exception" - else: sysTypeFromName"E_Base" +proc getEbase(g: ModuleGraph; info: TLineInfo): PType = + result = g.sysTypeFromName(info, "Exception") -proc excType(n: PNode): PType = +proc excType(g: ModuleGraph; n: PNode): PType = # reraise is like raising E_Base: - let t = if n.kind == nkEmpty or n.typ.isNil: getEbase() else: n.typ + let t = if n.kind == nkEmpty or n.typ.isNil: getEbase(g, n.info) else: n.typ result = skipTypes(t, skipPtrs) -proc createRaise(n: PNode): PNode = +proc createRaise(g: ModuleGraph; n: PNode): PNode = result = newNode(nkType) - result.typ = getEbase() + result.typ = getEbase(g, n.info) if not n.isNil: result.info = n.info -proc createTag(n: PNode): PNode = +proc createTag(g: ModuleGraph; n: PNode): PNode = result = newNode(nkType) - if getCompilerProc("RootEffect") != nil: - result.typ = sysTypeFromName"RootEffect" - else: - result.typ = sysTypeFromName"TEffect" + result.typ = g.sysTypeFromName(n.info, "RootEffect") if not n.isNil: result.info = n.info proc addEffect(a: PEffects, e: PNode, useLineInfo=true) = assert e.kind != nkRaiseStmt var aa = a.exc for i in a.bottom ..< aa.len: - if sameType(aa[i].excType, e.excType): - if not useLineInfo or gCmd == cmdDoc: return + if sameType(a.graph.excType(aa[i]), a.graph.excType(e)): + if not useLineInfo or a.config.cmd == cmdDoc: return elif aa[i].info == e.info: return throws(a.exc, e) @@ -292,25 +290,25 @@ proc addTag(a: PEffects, e: PNode, useLineInfo=true) = var aa = a.tags for i in 0 ..< aa.len: if sameType(aa[i].typ.skipTypes(skipPtrs), e.typ.skipTypes(skipPtrs)): - if not useLineInfo or gCmd == cmdDoc: return + if not useLineInfo or a.config.cmd == cmdDoc: return elif aa[i].info == e.info: return throws(a.tags, e) proc mergeEffects(a: PEffects, b, comesFrom: PNode) = if b.isNil: - addEffect(a, createRaise(comesFrom)) + addEffect(a, createRaise(a.graph, comesFrom)) else: for effect in items(b): addEffect(a, effect, useLineInfo=comesFrom != nil) proc mergeTags(a: PEffects, b, comesFrom: PNode) = if b.isNil: - addTag(a, createTag(comesFrom)) + addTag(a, createTag(a.graph, comesFrom)) else: for effect in items(b): addTag(a, effect, useLineInfo=comesFrom != nil) proc listEffects(a: PEffects) = - for e in items(a.exc): message(e.info, hintUser, typeToString(e.typ)) - for e in items(a.tags): message(e.info, hintUser, typeToString(e.typ)) + for e in items(a.exc): message(a.config, e.info, hintUser, typeToString(e.typ)) + for e in items(a.tags): message(a.config, e.info, hintUser, typeToString(e.typ)) #if a.maxLockLevel != 0: # message(e.info, hintUser, "lockLevel: " & a.maxLockLevel) @@ -320,7 +318,7 @@ proc catches(tracked: PEffects, e: PType) = var i = tracked.bottom while i < L: # r supertype of e? - if safeInheritanceDiff(tracked.exc[i].excType, e) <= 0: + if safeInheritanceDiff(tracked.graph.excType(tracked.exc[i]), e) <= 0: tracked.exc.sons[i] = tracked.exc.sons[L-1] dec L else: @@ -359,9 +357,12 @@ proc trackTryStmt(tracked: PEffects, n: PNode) = catchesAll(tracked) else: for j in countup(0, blen - 2): - assert(b.sons[j].kind == nkType) - catches(tracked, b.sons[j].typ) - + if b.sons[j].isInfixAs(): + assert(b.sons[j][1].kind == nkType) + catches(tracked, b.sons[j][1].typ) + else: + assert(b.sons[j].kind == nkType) + catches(tracked, b.sons[j].typ) setLen(tracked.init, oldState) track(tracked, b.sons[blen-1]) for i in oldState..<tracked.init.len: @@ -486,7 +487,7 @@ proc mergeLockLevels(tracked: PEffects, n: PNode, lockLevel: TLockLevel) = if lockLevel >= tracked.currLockLevel: # if in lock section: if tracked.currLockLevel > 0.TLockLevel: - localError n.info, errGenerated, + localError tracked.config, n.info, errGenerated, "expected lock level < " & $tracked.currLockLevel & " but got lock level " & $lockLevel tracked.maxLockLevel = max(tracked.maxLockLevel, lockLevel) @@ -500,22 +501,22 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) = mergeTags(tracked, tagSpec, n) if notGcSafe(s.typ) and sfImportc notin s.flags: - if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) + if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config) markGcUnsafe(tracked, s) if tfNoSideEffect notin s.typ.flags: markSideEffect(tracked, s) mergeLockLevels(tracked, n, s.getLockLevel) -proc procVarcheck(n: PNode) = +proc procVarcheck(n: PNode; conf: ConfigRef) = if n.kind in nkSymChoices: - for x in n: procVarCheck(x) + for x in n: procVarCheck(x, conf) elif n.kind == nkSym and n.sym.magic != mNone and n.sym.kind in routineKinds: - localError(n.info, errXCannotBePassedToProcVar, n.sym.name.s) + localError(conf, n.info, "'$1' cannot be passed to a procvar" % n.sym.name.s) proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = let n = n.skipConv if paramType.isNil or paramType.kind != tyTypeDesc: - procVarcheck skipConvAndClosure(n) + procVarcheck skipConvAndClosure(n), tracked.config #elif n.kind in nkSymChoices: # echo "came here" let paramType = paramType.skipTypesOrNil(abstractInst) @@ -531,15 +532,16 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = return case impliesNotNil(tracked.guards, n) of impUnknown: - message(n.info, errGenerated, + message(tracked.config, n.info, errGenerated, "cannot prove '$1' is not nil" % n.renderTree) of impNo: - message(n.info, errGenerated, "'$1' is provably nil" % n.renderTree) + message(tracked.config, n.info, errGenerated, + "'$1' is provably nil" % n.renderTree) of impYes: discard proc assumeTheWorst(tracked: PEffects; n: PNode; op: PType) = - addEffect(tracked, createRaise(n)) - addTag(tracked, createTag(n)) + addEffect(tracked, createRaise(tracked.graph, n)) + addTag(tracked, createTag(tracked.graph, n)) let lockLevel = if op.lockLevel == UnspecifiedLockLevel: UnknownLockLevel else: op.lockLevel #if lockLevel == UnknownLockLevel: @@ -554,7 +556,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) = let a = skipConvAndClosure(n) let op = a.typ if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit: - internalAssert op.n.sons[0].kind == nkEffectList + internalAssert tracked.config, op.n.sons[0].kind == nkEffectList var effectList = op.n.sons[0] let s = n.skipConv if s.kind == nkSym and s.sym.kind in routineKinds: @@ -569,7 +571,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) = assumeTheWorst(tracked, n, op) # assume GcUnsafe unless in its type; 'forward' does not matter: if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner): - if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) + if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config) markGcUnsafe(tracked, a) elif tfNoSideEffect notin op.flags and not isOwnedProcVar(a, tracked.owner): markSideEffect(tracked, a) @@ -577,7 +579,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) = mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n) if notGcSafe(op): - if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) + if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config) markGcUnsafe(tracked, a) elif tfNoSideEffect notin op.flags: markSideEffect(tracked, a) @@ -589,37 +591,34 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) = # XXX figure out why this can be a non tyProc here. See httpclient.nim for an # example that triggers it. if argtype.kind == tyProc and notGcSafe(argtype) and not tracked.inEnforcedGcSafe: - localError(n.info, $n & " is not GC safe") + localError(tracked.config, n.info, $n & " is not GC safe") notNilCheck(tracked, n, paramType) proc breaksBlock(n: PNode): bool = - case n.kind - of nkStmtList, nkStmtListExpr: - for c in n: - if breaksBlock(c): return true - of nkBreakStmt, nkReturnStmt, nkRaiseStmt: - return true - of nkCallKinds: - if n.sons[0].kind == nkSym and sfNoReturn in n.sons[0].sym.flags: - return true - else: - discard + # sematic check doesn't allow statements after raise, break, return or + # call to noreturn proc, so it is safe to check just the last statements + var it = n + while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0: + it = it.lastSon + + result = it.kind in {nkBreakStmt, nkReturnStmt, nkRaiseStmt} or + it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags proc trackCase(tracked: PEffects, n: PNode) = track(tracked, n.sons[0]) let oldState = tracked.init.len - let oldFacts = tracked.guards.len + let oldFacts = tracked.guards.s.len 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 + warnProveField in tracked.config.notes var inter: TIntersection = @[] var toCover = 0 for i in 1..<n.len: let branch = n.sons[i] setLen(tracked.init, oldState) if interesting: - setLen(tracked.guards, oldFacts) + setLen(tracked.guards.s, oldFacts) addCaseBranchFacts(tracked.guards, n, i) for i in 0 ..< branch.len: track(tracked, branch.sons[i]) @@ -632,11 +631,11 @@ proc trackCase(tracked: PEffects, n: PNode) = for id, count in items(inter): if count >= toCover: tracked.init.add id # else we can't merge - setLen(tracked.guards, oldFacts) + setLen(tracked.guards.s, oldFacts) proc trackIf(tracked: PEffects, n: PNode) = track(tracked, n.sons[0].sons[0]) - let oldFacts = tracked.guards.len + let oldFacts = tracked.guards.s.len addFact(tracked.guards, n.sons[0].sons[0]) let oldState = tracked.init.len @@ -649,7 +648,7 @@ proc trackIf(tracked: PEffects, n: PNode) = for i in 1..<n.len: let branch = n.sons[i] - setLen(tracked.guards, oldFacts) + setLen(tracked.guards.s, oldFacts) for j in 0..i-1: addFactNeg(tracked.guards, n.sons[j].sons[0]) if branch.len > 1: @@ -665,7 +664,7 @@ proc trackIf(tracked: PEffects, n: PNode) = for id, count in items(inter): if count >= toCover: tracked.init.add id # else we can't merge as it is not exhaustive - setLen(tracked.guards, oldFacts) + setLen(tracked.guards.s, oldFacts) proc trackBlock(tracked: PEffects, n: PNode) = if n.kind in {nkStmtList, nkStmtListExpr}: @@ -693,7 +692,7 @@ proc paramType(op: PType, i: int): PType = proc cstringCheck(tracked: PEffects; n: PNode) = if n.sons[0].typ.kind == tyCString and (let a = skipConv(n[1]); a.typ.kind == tyString and a.kind notin {nkStrLit..nkTripleStrLit}): - message(n.info, warnUnsafeCode, renderTree(n)) + message(tracked.config, n.info, warnUnsafeCode, renderTree(n)) proc track(tracked: PEffects, n: PNode) = case n.kind @@ -735,7 +734,7 @@ proc track(tracked: PEffects, n: PNode) = if notGcSafe(op) and not importedFromC(a): # and it's not a recursive call: if not (a.kind == nkSym and a.sym == tracked.owner): - if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) + if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config) markGcUnsafe(tracked, a) if tfNoSideEffect notin op.flags and not importedFromC(a): # and it's not a recursive call: @@ -747,13 +746,13 @@ proc track(tracked: PEffects, n: PNode) = # may not look like an assignment, but it is: let arg = n.sons[1] initVarViaNew(tracked, arg) - if {tfNeedsInit} * arg.typ.lastSon.flags != {}: + if arg.typ.len != 0 and {tfNeedsInit} * arg.typ.lastSon.flags != {}: if a.sym.magic == mNewSeq and n[2].kind in {nkCharLit..nkUInt64Lit} and n[2].intVal == 0: # var s: seq[notnil]; newSeq(s, 0) is a special case! discard else: - message(arg.info, warnProveInit, $arg) + message(tracked.config, arg.info, warnProveInit, $arg) for i in 0 ..< safeLen(n): track(tracked, n.sons[i]) of nkDotExpr: @@ -761,7 +760,8 @@ proc track(tracked: PEffects, n: PNode) = for i in 0 ..< len(n): track(tracked, n.sons[i]) of nkCheckedFieldExpr: track(tracked, n.sons[0]) - if warnProveField in gNotes: checkFieldAccess(tracked.guards, n) + if warnProveField in tracked.config.notes: + checkFieldAccess(tracked.guards, n, tracked.config) of nkTryStmt: trackTryStmt(tracked, n) of nkPragma: trackPragmaStmt(tracked, n) of nkAsgn, nkFastAsgn: @@ -798,11 +798,11 @@ proc track(tracked: PEffects, n: PNode) = else: # loop may never execute: let oldState = tracked.init.len - let oldFacts = tracked.guards.len + let oldFacts = tracked.guards.s.len addFact(tracked.guards, n.sons[0]) track(tracked, n.sons[1]) setLen(tracked.init, oldState) - setLen(tracked.guards, oldFacts) + setLen(tracked.guards.s, oldFacts) of nkForStmt, nkParForStmt: # we are very conservative here and assume the loop is never executed: let oldState = tracked.init.len @@ -811,13 +811,13 @@ proc track(tracked: PEffects, n: PNode) = setLen(tracked.init, oldState) of nkObjConstr: when false: track(tracked, n.sons[0]) - let oldFacts = tracked.guards.len + let oldFacts = tracked.guards.s.len for i in 1 ..< len(n): let x = n.sons[i] track(tracked, x) if x.sons[0].kind == nkSym and sfDiscriminant in x.sons[0].sym.flags: addDiscriminantFact(tracked.guards, x) - setLen(tracked.guards, oldFacts) + setLen(tracked.guards.s, oldFacts) of nkPragmaBlock: let pragmaList = n.sons[0] let oldLocked = tracked.locked.len @@ -844,31 +844,31 @@ proc track(tracked: PEffects, n: PNode) = else: for i in 0 ..< safeLen(n): track(tracked, n.sons[i]) -proc subtypeRelation(spec, real: PNode): bool = - result = safeInheritanceDiff(real.excType, spec.typ) <= 0 +proc subtypeRelation(g: ModuleGraph; spec, real: PNode): bool = + result = safeInheritanceDiff(g.excType(real), spec.typ) <= 0 -proc checkRaisesSpec(spec, real: PNode, msg: string, hints: bool; - effectPredicate: proc (a, b: PNode): bool {.nimcall.}) = +proc checkRaisesSpec(g: ModuleGraph; spec, real: PNode, msg: string, hints: bool; + effectPredicate: proc (g: ModuleGraph; a, b: PNode): bool {.nimcall.}) = # check that any real exception is listed in 'spec'; mark those as used; # report any unused exception var used = initIntSet() for r in items(real): block search: for s in 0 ..< spec.len: - if effectPredicate(spec[s], r): + if effectPredicate(g, spec[s], r): used.incl(s) break search # XXX call graph analysis would be nice here! pushInfoContext(spec.info) - localError(r.info, errGenerated, msg & typeToString(r.typ)) + localError(g.config, r.info, errGenerated, msg & typeToString(r.typ)) popInfoContext() # hint about unnecessarily listed exception types: if hints: for s in 0 ..< spec.len: if not used.contains(s): - message(spec[s].info, hintXDeclaredButNotUsed, renderTree(spec[s])) + message(g.config, spec[s].info, hintXDeclaredButNotUsed, renderTree(spec[s])) -proc checkMethodEffects*(disp, branch: PSym) = +proc checkMethodEffects*(g: ModuleGraph; disp, branch: PSym) = ## checks for consistent effects for multi methods. let actual = branch.typ.n.sons[0] if actual.len != effectListLen: return @@ -876,42 +876,42 @@ proc checkMethodEffects*(disp, branch: PSym) = let p = disp.ast.sons[pragmasPos] let raisesSpec = effectSpec(p, wRaises) if not isNil(raisesSpec): - checkRaisesSpec(raisesSpec, actual.sons[exceptionEffects], + checkRaisesSpec(g, raisesSpec, actual.sons[exceptionEffects], "can raise an unlisted exception: ", hints=off, subtypeRelation) let tagsSpec = effectSpec(p, wTags) if not isNil(tagsSpec): - checkRaisesSpec(tagsSpec, actual.sons[tagEffects], + checkRaisesSpec(g, tagsSpec, actual.sons[tagEffects], "can have an unlisted effect: ", hints=off, subtypeRelation) if sfThread in disp.flags and notGcSafe(branch.typ): - localError(branch.info, "base method is GC-safe, but '$1' is not" % + localError(g.config, branch.info, "base method is GC-safe, but '$1' is not" % branch.name.s) if branch.typ.lockLevel > disp.typ.lockLevel: when true: - message(branch.info, warnLockLevel, + message(g.config, branch.info, warnLockLevel, "base method has lock level $1, but dispatcher has $2" % [$branch.typ.lockLevel, $disp.typ.lockLevel]) else: # XXX make this an error after bigbreak has been released: - localError(branch.info, + localError(g.config, branch.info, "base method has lock level $1, but dispatcher has $2" % [$branch.typ.lockLevel, $disp.typ.lockLevel]) -proc setEffectsForProcType*(t: PType, n: PNode) = +proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) = var effects = t.n.sons[0] - internalAssert t.kind == tyProc and effects.kind == nkEffectList + if t.kind != tyProc or effects.kind != nkEffectList: return let raisesSpec = effectSpec(n, wRaises) tagsSpec = effectSpec(n, wTags) if not isNil(raisesSpec) or not isNil(tagsSpec): - internalAssert effects.len == 0 + internalAssert g.config, effects.len == 0 newSeq(effects.sons, effectListLen) if not isNil(raisesSpec): effects.sons[exceptionEffects] = raisesSpec if not isNil(tagsSpec): effects.sons[tagEffects] = tagsSpec -proc initEffects(effects: PNode; s: PSym; t: var TEffects) = +proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) = newSeq(effects.sons, effectListLen) effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info) effects.sons[tagEffects] = newNodeI(nkArgList, s.info) @@ -922,52 +922,55 @@ proc initEffects(effects: PNode; s: PSym; t: var TEffects) = t.tags = effects.sons[tagEffects] t.owner = s t.init = @[] - t.guards = @[] + t.guards.s = @[] + t.guards.o = initOperators(g) t.locked = @[] + t.graph = g + t.config = g.config -proc trackProc*(s: PSym, body: PNode) = +proc trackProc*(g: ModuleGraph; s: PSym, body: PNode) = var effects = s.typ.n.sons[0] - internalAssert effects.kind == nkEffectList + if effects.kind != nkEffectList: return # effects already computed? if sfForward in s.flags: return if effects.len == effectListLen: return var t: TEffects - initEffects(effects, s, t) + initEffects(g, effects, s, t) track(t, body) if not isEmptyType(s.typ.sons[0]) and {tfNeedsInit, tfNotNil} * s.typ.sons[0].flags != {} and s.kind in {skProc, skFunc, skConverter, skMethod}: var res = s.ast.sons[resultPos].sym # get result symbol if res.id notin t.init: - message(body.info, warnProveInit, "result") + message(g.config, body.info, warnProveInit, "result") let p = s.ast.sons[pragmasPos] let raisesSpec = effectSpec(p, wRaises) if not isNil(raisesSpec): - checkRaisesSpec(raisesSpec, t.exc, "can raise an unlisted exception: ", + checkRaisesSpec(g, raisesSpec, t.exc, "can raise an unlisted exception: ", hints=on, subtypeRelation) # after the check, use the formal spec: effects.sons[exceptionEffects] = raisesSpec let tagsSpec = effectSpec(p, wTags) if not isNil(tagsSpec): - checkRaisesSpec(tagsSpec, t.tags, "can have an unlisted effect: ", + checkRaisesSpec(g, tagsSpec, t.tags, "can have an unlisted effect: ", hints=off, subtypeRelation) # after the check, use the formal spec: effects.sons[tagEffects] = tagsSpec if sfThread in s.flags and t.gcUnsafe: - if optThreads in gGlobalOptions and optThreadAnalysis in gGlobalOptions: + if optThreads in g.config.globalOptions and optThreadAnalysis in g.config.globalOptions: #localError(s.info, "'$1' is not GC-safe" % s.name.s) - listGcUnsafety(s, onlyWarning=false) + listGcUnsafety(s, onlyWarning=false, g.config) else: - listGcUnsafety(s, onlyWarning=true) + listGcUnsafety(s, onlyWarning=true, g.config) #localError(s.info, warnGcUnsafe2, s.name.s) if sfNoSideEffect in s.flags and t.hasSideEffect: when false: - listGcUnsafety(s, onlyWarning=false) + listGcUnsafety(s, onlyWarning=false, g.config) else: - localError(s.info, errXhasSideEffects, s.name.s) + localError(g.config, s.info, "'$1' can have side effects" % s.name.s) if not t.gcUnsafe: s.typ.flags.incl tfGcSafe if not t.hasSideEffect and sfSideEffect notin s.flags: @@ -976,7 +979,7 @@ proc trackProc*(s: PSym, body: PNode) = s.typ.lockLevel = t.maxLockLevel elif t.maxLockLevel > s.typ.lockLevel: #localError(s.info, - message(s.info, warnLockLevel, + message(g.config, s.info, warnLockLevel, "declared lock level is $1, but real lock level is $2" % [$s.typ.lockLevel, $t.maxLockLevel]) when defined(useDfa): @@ -984,12 +987,12 @@ proc trackProc*(s: PSym, body: PNode) = dataflowAnalysis(s, body) when false: trackWrites(s, body) -proc trackTopLevelStmt*(module: PSym; n: PNode) = +proc trackTopLevelStmt*(g: ModuleGraph; module: PSym; n: PNode) = if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef, nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}: return var effects = newNode(nkEffectList, n.info) var t: TEffects - initEffects(effects, module, t) + initEffects(g, effects, module, t) t.isToplevel = true track(t, n) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index dcaa0263b..3687e50e9 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -10,51 +10,77 @@ ## this module does the semantic checking of statements # included from sem.nim -var enforceVoidContext = PType(kind: tyStmt) +const + errNoSymbolToBorrowFromFound = "no symbol to borrow from found" + errDiscardValueX = "value of type '$1' has to be discarded" + errInvalidDiscard = "statement returns no value that can be discarded" + errInvalidControlFlowX = "invalid control flow: $1" + errSelectorMustBeOfCertainTypes = "selector must be of an ordinal type, float or string" + errExprCannotBeRaised = "only a 'ref object' can be raised" + errBreakOnlyInLoop = "'break' only allowed in loop construct" + errExceptionAlreadyHandled = "exception already handled" + errYieldNotAllowedHere = "'yield' only allowed in an iterator" + errYieldNotAllowedInTryStmt = "'yield' cannot be used within 'try' in a non-inlined iterator" + errInvalidNumberOfYieldExpr = "invalid number of 'yield' expressions" + errCannotReturnExpr = "current routine cannot return an expression" + errGenericLambdaNotAllowed = "A nested proc can have generic parameters only when " & + "it is used as an operand to another routine and the types " & + "of the generic paramers can be inferred from the expected signature." + errCannotInferTypeOfTheLiteral = "cannot infer the type of the $1" + errCannotInferReturnType = "cannot infer the return type of the proc" + errCannotInferStaticParam = "cannot infer the value of the static param '$1'" + errProcHasNoConcreteType = "'$1' doesn't have a concrete type, due to unspecified generic parameters." + errLetNeedsInit = "'let' symbol requires an initialization" + errThreadvarCannotInit = "a thread var cannot be initialized explicitly; this would only run for the main thread" + errImplOfXexpected = "implementation of '$1' expected" + errRecursiveDependencyX = "recursive dependency: '$1'" + errPragmaOnlyInHeaderOfProcX = "pragmas are only allowed in the header of a proc; redefinition of $1" + +var enforceVoidContext = PType(kind: tyStmt) # XXX global variable here proc semDiscard(c: PContext, n: PNode): PNode = result = n - checkSonsLen(n, 1) + checkSonsLen(n, 1, c.config) if n.sons[0].kind != nkEmpty: n.sons[0] = semExprWithType(c, n.sons[0]) - if isEmptyType(n.sons[0].typ) or n.sons[0].typ.kind == tyNone: - localError(n.info, errInvalidDiscard) + if isEmptyType(n.sons[0].typ) or n.sons[0].typ.kind == tyNone or n.sons[0].kind == nkTypeOfExpr: + localError(c.config, n.info, errInvalidDiscard) proc semBreakOrContinue(c: PContext, n: PNode): PNode = result = n - checkSonsLen(n, 1) + checkSonsLen(n, 1, c.config) if n.sons[0].kind != nkEmpty: if n.kind != nkContinueStmt: var s: PSym case n.sons[0].kind of nkIdent: s = lookUp(c, n.sons[0]) of nkSym: s = n.sons[0].sym - else: illFormedAst(n) + else: illFormedAst(n, c.config) s = getGenSym(c, s) if s.kind == skLabel and s.owner.id == c.p.owner.id: var x = newSymNode(s) x.info = n.info incl(s.flags, sfUsed) n.sons[0] = x - suggestSym(x.info, s, c.graph.usageSym) + suggestSym(c.config, x.info, s, c.graph.usageSym) styleCheckUse(x.info, s) else: - localError(n.info, errInvalidControlFlowX, s.name.s) + localError(c.config, n.info, errInvalidControlFlowX % s.name.s) else: - localError(n.info, errGenerated, "'continue' cannot have a label") + localError(c.config, n.info, errGenerated, "'continue' cannot have a label") elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0): - localError(n.info, errInvalidControlFlowX, + localError(c.config, n.info, errInvalidControlFlowX % renderTree(n, {renderNoComments})) -proc semAsm(con: PContext, n: PNode): PNode = - checkSonsLen(n, 2) - var marker = pragmaAsm(con, n.sons[0]) +proc semAsm(c: PContext, n: PNode): PNode = + checkSonsLen(n, 2, c.config) + var marker = pragmaAsm(c, n.sons[0]) if marker == '\0': marker = '`' # default marker - result = semAsmOrEmit(con, n, marker) + result = semAsmOrEmit(c, n, marker) proc semWhile(c: PContext, n: PNode): PNode = result = n - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.config) openScope(c) n.sons[0] = forceBool(c, semExprWithType(c, n.sons[0])) inc(c.p.nestedLoopCounter) @@ -71,38 +97,13 @@ proc toCover(t: PType): BiggestInt = else: result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc})) -when false: - proc performProcvarCheck(c: PContext, info: TLineInfo, s: PSym) = - ## Checks that the given symbol is a proper procedure variable, meaning - ## that it - var smoduleId = getModule(s).id - if sfProcvar notin s.flags and s.typ.callConv == ccDefault and - smoduleId != c.module.id: - block outer: - for module in c.friendModules: - if smoduleId == module.id: - break outer - localError(info, errXCannotBePassedToProcVar, s.name.s) - -template semProcvarCheck(c: PContext, n: PNode) = - when false: - var n = n.skipConv - if n.kind in nkSymChoices: - for x in n: - if x.sym.kind in {skProc, skMethod, skConverter, skIterator}: - performProcvarCheck(c, n.info, x.sym) - elif n.kind == nkSym and n.sym.kind in {skProc, skMethod, skConverter, - skIterator}: - performProcvarCheck(c, n.info, n.sym) - proc semProc(c: PContext, n: PNode): PNode proc semExprBranch(c: PContext, n: PNode): PNode = result = semExpr(c, n) if result.typ != nil: # XXX tyGenericInst here? - semProcvarCheck(c, result) - if result.typ.kind == tyVar: result = newDeref(result) + if result.typ.kind in {tyVar, tyLent}: result = newDeref(result) proc semExprBranchScope(c: PContext, n: PNode): PNode = openScope(c) @@ -120,39 +121,35 @@ proc implicitlyDiscardable(n: PNode): bool = result = isCallExpr(n) and n.sons[0].kind == nkSym and sfDiscardable in n.sons[0].sym.flags -proc fixNilType(n: PNode) = +proc fixNilType(c: PContext; n: PNode) = if isAtom(n): if n.kind != nkNilLit and n.typ != nil: - localError(n.info, errDiscardValueX, n.typ.typeToString) + localError(c.config, n.info, errDiscardValueX % n.typ.typeToString) elif n.kind in {nkStmtList, nkStmtListExpr}: n.kind = nkStmtList - for it in n: fixNilType(it) + for it in n: fixNilType(c, it) n.typ = nil proc discardCheck(c: PContext, result: PNode) = if c.matchedConcept != nil: return if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}: - if result.kind == nkNilLit: - result.typ = nil - message(result.info, warnNilStatement) - elif implicitlyDiscardable(result): + if implicitlyDiscardable(result): var n = result result.typ = nil while n.kind in skipForDiscardable: n = n.lastSon n.typ = nil - elif result.typ.kind != tyError and gCmd != cmdInteractive: - if result.typ.kind == tyNil: - fixNilType(result) - message(result.info, warnNilStatement) - else: - var n = result - while n.kind in skipForDiscardable: n = n.lastSon - var s = "expression '" & $n & "' is of type '" & - result.typ.typeToString & "' and has to be discarded" - if result.typ.kind == tyProc: - s.add "; for a function call use ()" - localError(n.info, s) + elif result.typ.kind != tyError and c.config.cmd != cmdInteractive: + var n = result + while n.kind in skipForDiscardable: n = n.lastSon + var s = "expression '" & $n & "' is of type '" & + result.typ.typeToString & "' and has to be discarded" + if result.info.line != n.info.line or + result.info.fileIndex != n.info.fileIndex: + s.add "; start of expression here: " & $result.info + if result.typ.kind == tyProc: + s.add "; for a function call use ()" + localError(c.config, n.info, s) proc semIf(c: PContext, n: PNode): PNode = result = n @@ -171,7 +168,7 @@ proc semIf(c: PContext, n: PNode): PNode = hasElse = true it.sons[0] = semExprBranchScope(c, it.sons[0]) typ = commonType(typ, it.sons[0]) - else: illFormedAst(it) + else: illFormedAst(it, c.config) if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse: for it in n: discardCheck(c, it.lastSon) result.kind = nkIfStmt @@ -187,7 +184,7 @@ proc semIf(c: PContext, n: PNode): PNode = proc semCase(c: PContext, n: PNode): PNode = result = n - checkMinSonsLen(n, 2) + checkMinSonsLen(n, 2, c.config) openScope(c) n.sons[0] = semExprWithType(c, n.sons[0]) var chckCovered = false @@ -201,23 +198,23 @@ proc semCase(c: PContext, n: PNode): PNode = of tyFloat..tyFloat128, tyString, tyError: discard else: - localError(n.info, errSelectorMustBeOfCertainTypes) + localError(c.config, n.info, errSelectorMustBeOfCertainTypes) return for i in countup(1, sonsLen(n) - 1): var x = n.sons[i] when defined(nimsuggest): - if gIdeCmd == ideSug and exactEquals(gTrackPos, x.info) and caseTyp.kind == tyEnum: + if c.config.ideCmd == ideSug and exactEquals(gTrackPos, x.info) and caseTyp.kind == tyEnum: suggestEnum(c, x, caseTyp) case x.kind of nkOfBranch: - checkMinSonsLen(x, 2) + checkMinSonsLen(x, 2, c.config) semCaseBranch(c, n, x, i, covered) var last = sonsLen(x)-1 x.sons[last] = semExprBranchScope(c, x.sons[last]) typ = commonType(typ, x.sons[last]) of nkElifBranch: chckCovered = false - checkSonsLen(x, 2) + checkSonsLen(x, 2, c.config) when newScopeForIf: openScope(c) x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0])) when not newScopeForIf: openScope(c) @@ -226,17 +223,17 @@ proc semCase(c: PContext, n: PNode): PNode = closeScope(c) of nkElse: chckCovered = false - checkSonsLen(x, 1) + checkSonsLen(x, 1, c.config) x.sons[0] = semExprBranchScope(c, x.sons[0]) typ = commonType(typ, x.sons[0]) hasElse = true else: - illFormedAst(x) + illFormedAst(x, c.config) if chckCovered: if covered == toCover(n.sons[0].typ): hasElse = true else: - localError(n.info, errNotAllCasesCovered) + localError(c.config, n.info, "not all cases are covered") closeScope(c) if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse: for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon) @@ -252,69 +249,73 @@ proc semCase(c: PContext, n: PNode): PNode = result.typ = typ proc semTry(c: PContext, n: PNode): PNode = + + var check = initIntSet() + template semExceptBranchType(typeNode: PNode): bool = + # returns true if exception type is imported type + let typ = semTypeNode(c, typeNode, nil).toObject() + var is_imported = false + if isImportedException(typ, c.config): + is_imported = true + elif not isException(typ): + localError(c.config, typeNode.info, errExprCannotBeRaised) + + if containsOrIncl(check, typ.id): + localError(c.config, typeNode.info, errExceptionAlreadyHandled) + typeNode = newNodeIT(nkType, typeNode.info, typ) + is_imported + result = n inc c.p.inTryStmt - checkMinSonsLen(n, 2) + checkMinSonsLen(n, 2, c.config) var typ = commonTypeBegin - n.sons[0] = semExprBranchScope(c, n.sons[0]) - typ = commonType(typ, n.sons[0].typ) + n[0] = semExprBranchScope(c, n[0]) + typ = commonType(typ, n[0].typ) - var check = initIntSet() var last = sonsLen(n) - 1 for i in countup(1, last): - var a = n.sons[i] - checkMinSonsLen(a, 1) - var length = sonsLen(a) + let a = n.sons[i] + checkMinSonsLen(a, 1, c.config) openScope(c) if a.kind == nkExceptBranch: - # so that ``except [a, b, c]`` is supported: - if length == 2 and a.sons[0].kind == nkBracket: - a.sons[0..0] = a.sons[0].sons - length = a.sonsLen - - # Iterate through each exception type in the except branch. - for j in countup(0, length-2): - var typeNode = a.sons[j] # e.g. `Exception` - var symbolNode: PNode = nil # e.g. `foobar` - # Handle the case where the `Exception as foobar` syntax is used. - if typeNode.isInfixAs(): - typeNode = a.sons[j].sons[1] - symbolNode = a.sons[j].sons[2] - - # Resolve the type ident into a PType. - var typ = semTypeNode(c, typeNode, nil).toObject() - if typ.kind != tyObject: - localError(a.sons[j].info, errExprCannotBeRaised) - - let newTypeNode = newNodeI(nkType, typeNode.info) - newTypeNode.typ = typ - if symbolNode.isNil: - a.sons[j] = newTypeNode - else: - a.sons[j].sons[1] = newTypeNode - # Add the exception ident to the symbol table. - let symbol = newSymG(skLet, symbolNode, c) - symbol.typ = typ.toRef() - addDecl(c, symbol) - # Overwrite symbol in AST with the symbol in the symbol table. - let symNode = newNodeI(nkSym, typeNode.info) - symNode.sym = symbol - a.sons[j].sons[2] = symNode - - if containsOrIncl(check, typ.id): - localError(a.sons[j].info, errExceptionAlreadyHandled) + + if a.len == 2 and a[0].kind == nkBracket: + # rewrite ``except [a, b, c]: body`` -> ```except a, b, c: body``` + a.sons[0..0] = a[0].sons + + if a.len == 2 and a[0].isInfixAs(): + # support ``except Exception as ex: body`` + let is_imported = semExceptBranchType(a[0][1]) + let symbol = newSymG(skLet, a[0][2], c) + symbol.typ = if is_imported: a[0][1].typ + else: a[0][1].typ.toRef() + addDecl(c, symbol) + # Overwrite symbol in AST with the symbol in the symbol table. + a[0][2] = newSymNode(symbol, a[0][2].info) + + else: + # support ``except KeyError, ValueError, ... : body`` + var is_native, is_imported: bool + for j in 0..a.len-2: + let tmp = semExceptBranchType(a[j]) + if tmp: is_imported = true + else: is_native = true + + if is_native and is_imported: + localError(c.config, a[0].info, "Mix of imported and native exception types is not allowed in one except branch") + elif a.kind != nkFinally: - illFormedAst(n) + illFormedAst(n, c.config) # last child of an nkExcept/nkFinally branch is a statement: - a.sons[length-1] = semExprBranchScope(c, a.sons[length-1]) - if a.kind != nkFinally: typ = commonType(typ, a.sons[length-1].typ) + a[^1] = semExprBranchScope(c, a[^1]) + if a.kind != nkFinally: typ = commonType(typ, a[^1]) else: dec last closeScope(c) dec c.p.inTryStmt - if isEmptyType(typ) or typ.kind == tyNil: + if isEmptyType(typ) or typ.kind in {tyNil, tyExpr}: discardCheck(c, n.sons[0]) for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon) if typ == enforceVoidContext: @@ -337,10 +338,10 @@ proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode = result.info = n.info result.typ = typ else: - changeType(r1, typ, check=true) + changeType(c, r1, typ, check=true) result = r1 elif not sameType(result.typ, typ): - changeType(result, typ, check=false) + changeType(c, result, typ, check=false) proc findShadowedVar(c: PContext, v: PSym): PSym = for scope in walkScopes(c.currentScope.parent): @@ -364,44 +365,23 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym = result = semIdentWithPragma(c, kind, n, {}) if result.owner.kind == skModule: incl(result.flags, sfGlobal) - suggestSym(n.info, result, c.graph.usageSym) + suggestSym(c.config, n.info, result, c.graph.usageSym) styleCheckDef(result) -proc checkNilable(v: PSym) = +proc checkNilable(c: PContext; v: PSym) = if {sfGlobal, sfImportC} * v.flags == {sfGlobal} and {tfNotNil, tfNeedsInit} * v.typ.flags != {}: if v.ast.isNil: - message(v.info, warnProveInit, v.name.s) + message(c.config, v.info, warnProveInit, v.name.s) elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags: - message(v.info, warnProveInit, v.name.s) + message(c.config, 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. +proc addToVarSection(c: PContext; result: PNode; orig, identDefs: PNode) = let L = identDefs.len let value = identDefs[L-1] - if value.typ != nil and tfHasAsgn in value.typ.flags and not newDestructors: - # 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: + if result.kind == nkStmtList: let o = copyNode(orig) o.add identDefs result.add o @@ -415,13 +395,13 @@ proc isDiscardUnderscore(v: PSym): bool = proc semUsing(c: PContext; n: PNode): PNode = result = ast.emptyNode - if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "using") + if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "using") for i in countup(0, sonsLen(n)-1): var a = n.sons[i] - if gCmd == cmdIdeTools: suggestStmt(c, a) + if c.config.cmd == cmdIdeTools: suggestStmt(c, a) if a.kind == nkCommentStmt: continue - if a.kind notin {nkIdentDefs, nkVarTuple, nkConstDef}: illFormedAst(a) - checkMinSonsLen(a, 3) + if a.kind notin {nkIdentDefs, nkVarTuple, nkConstDef}: illFormedAst(a, c.config) + checkMinSonsLen(a, 3, c.config) var length = sonsLen(a) if a.sons[length-2].kind != nkEmpty: let typ = semTypeNode(c, a.sons[length-2], nil) @@ -430,10 +410,10 @@ proc semUsing(c: PContext; n: PNode): PNode = v.typ = typ strTableIncl(c.signatures, v) else: - localError(a.info, "'using' section must have a type") + localError(c.config, a.info, "'using' section must have a type") var def: PNode if a.sons[length-1].kind != nkEmpty: - localError(a.info, "'using' sections cannot contain assignments") + localError(c.config, a.info, "'using' sections cannot contain assignments") proc hasEmpty(typ: PType): bool = if typ.kind in {tySequence, tyArray, tySet}: @@ -446,23 +426,23 @@ proc makeDeref(n: PNode): PNode = var t = n.typ if t.kind in tyUserTypeClasses and t.isResolvedUserTypeClass: t = t.lastSon - t = skipTypes(t, {tyGenericInst, tyAlias}) + t = skipTypes(t, {tyGenericInst, tyAlias, tySink}) result = n - if t.kind == tyVar: + if t.kind in {tyVar, tyLent}: result = newNodeIT(nkHiddenDeref, n.info, t.sons[0]) addSon(result, n) - t = skipTypes(t.sons[0], {tyGenericInst, tyAlias}) + t = skipTypes(t.sons[0], {tyGenericInst, tyAlias, tySink}) while t.kind in {tyPtr, tyRef}: var a = result let baseTyp = t.lastSon result = newNodeIT(nkHiddenDeref, n.info, baseTyp) addSon(result, a) - t = skipTypes(baseTyp, {tyGenericInst, tyAlias}) + t = skipTypes(baseTyp, {tyGenericInst, tyAlias, tySink}) proc fillPartialObject(c: PContext; n: PNode; typ: PType) = if n.len == 2: let x = semExprWithType(c, n[0]) - let y = considerQuotedIdent(n[1]) + let y = considerQuotedIdent(c.config, n[1]) let obj = x.typ.skipTypes(abstractPtrs) if obj.kind == tyObject and tfPartial in obj.flags: let field = newSym(skField, getIdent(y.s), obj.sym, n[1].info) @@ -473,14 +453,14 @@ proc fillPartialObject(c: PContext; n: PNode; typ: PType) = n.sons[1] = newSymNode(field) n.typ = field.typ else: - localError(n.info, "implicit object field construction " & + localError(c.config, n.info, "implicit object field construction " & "requires a .partial object, but got " & typeToString(obj)) else: - localError(n.info, "nkDotNode requires 2 children") + localError(c.config, n.info, "nkDotNode requires 2 children") -proc setVarType(v: PSym, typ: PType) = +proc setVarType(c: PContext; v: PSym, typ: PType) = if v.typ != nil and not sameTypeOrNil(v.typ, typ): - localError(v.info, "inconsistent typing for reintroduced symbol '" & + localError(c.config, v.info, "inconsistent typing for reintroduced symbol '" & v.name.s & "': previous type was: " & typeToString(v.typ) & "; new type is: " & typeToString(typ)) v.typ = typ @@ -491,10 +471,10 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var hasCompileTime = false for i in countup(0, sonsLen(n)-1): var a = n.sons[i] - if gCmd == cmdIdeTools: suggestStmt(c, a) + if c.config.cmd == cmdIdeTools: suggestStmt(c, a) if a.kind == nkCommentStmt: continue - if a.kind notin {nkIdentDefs, nkVarTuple, nkConstDef}: illFormedAst(a) - checkMinSonsLen(a, 3) + if a.kind notin {nkIdentDefs, nkVarTuple, nkConstDef}: illFormedAst(a, c.config) + checkMinSonsLen(a, 3, c.config) var length = sonsLen(a) var typ: PType if a.sons[length-2].kind != nkEmpty: @@ -506,7 +486,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = 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 ':'?") + localError(c.config, def.info, "'typedesc' metatype is not valid here; typed '=' instead of ':'?") def.typ = errorType(c) if typ != nil: if typ.isMetaType: @@ -522,32 +502,31 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if typ.kind in tyUserTypeClasses and typ.isResolvedUserTypeClass: typ = typ.lastSon if hasEmpty(typ): - localError(def.info, errCannotInferTypeOfTheLiteral, + localError(c.config, def.info, errCannotInferTypeOfTheLiteral % ($typ.kind).substr(2).toLowerAscii) elif typ.kind == tyProc and tfUnresolved in typ.flags: - localError(def.info, errProcHasNoConcreteType, def.renderTree) + localError(c.config, def.info, errProcHasNoConcreteType % def.renderTree) else: - if symkind == skLet: localError(a.info, errLetNeedsInit) + if symkind == skLet: localError(c.config, a.info, errLetNeedsInit) # this can only happen for errornous var statements: if typ == nil: continue - typeAllowedCheck(a.info, typ, symkind) + typeAllowedCheck(c.config, a.info, typ, symkind, if c.matchedConcept != nil: {taConcept} else: {}) liftTypeBoundOps(c, typ, a.info) - var tup = skipTypes(typ, {tyGenericInst, tyAlias}) + var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink}) if a.kind == nkVarTuple: if tup.kind != tyTuple: - localError(a.info, errXExpected, "tuple") + localError(c.config, a.info, errXExpected, "tuple") elif length-2 != sonsLen(tup): - localError(a.info, errWrongNumberOfVariables) - else: - b = newNodeI(nkVarTuple, a.info) - newSons(b, length) - b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator - b.sons[length-1] = def - addToVarSection(c, result, n, b) - elif tup.kind == tyTuple and def.kind == nkPar and + localError(c.config, a.info, errWrongNumberOfVariables) + b = newNodeI(nkVarTuple, a.info) + newSons(b, length) + b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator + b.sons[length-1] = def + addToVarSection(c, result, n, b) + elif tup.kind == tyTuple and def.kind in {nkPar, nkTupleConstr} and a.kind == nkIdentDefs and a.len > 3: - message(a.info, warnEachIdentIsTuple) + message(c.config, a.info, warnEachIdentIsTuple) for j in countup(0, length-3): if a[j].kind == nkDotExpr: @@ -565,19 +544,19 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if shadowed != nil: shadowed.flags.incl(sfShadowed) if shadowed.kind == skResult and sfGenSym notin v.flags: - message(a.info, warnResultShadowed) + message(c.config, a.info, warnResultShadowed) # a shadowed variable is an error unless it appears on the right # side of the '=': - if warnShadowIdent in gNotes and not identWithin(def, v.name): - message(a.info, warnShadowIdent, v.name.s) + if warnShadowIdent in c.config.notes and not identWithin(def, v.name): + message(c.config, a.info, warnShadowIdent, v.name.s) if a.kind != nkVarTuple: if def.kind != nkEmpty: # this is needed for the evaluation pass and for the guard checking: v.ast = def - if sfThread in v.flags: localError(def.info, errThreadvarCannotInit) - setVarType(v, typ) + if sfThread in v.flags: localError(c.config, def.info, errThreadvarCannotInit) + setVarType(c, v, typ) b = newNodeI(nkIdentDefs, a.info) - if importantComments(): + if importantComments(c.config): # keep documentation information: b.comment = a.comment addSon(b, newSymNode(v)) @@ -585,28 +564,31 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = addSon(b, copyTree(def)) addToVarSection(c, result, n, b) else: - if def.kind == nkPar: v.ast = def[j] - setVarType(v, tup.sons[j]) + if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j] + # bug #7663, for 'nim check' this can be a non-tuple: + if tup.kind == tyTuple: setVarType(c, v, tup.sons[j]) + else: v.typ = tup b.sons[j] = newSymNode(v) - checkNilable(v) + checkNilable(c, v) if sfCompileTime in v.flags: hasCompileTime = true - if hasCompileTime: vm.setupCompileTimeVar(c.module, c.cache, result) + if hasCompileTime: + vm.setupCompileTimeVar(c.module, c.cache, c.graph, result) proc semConst(c: PContext, n: PNode): PNode = result = copyNode(n) for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] - if gCmd == cmdIdeTools: suggestStmt(c, a) + if c.config.cmd == cmdIdeTools: suggestStmt(c, a) if a.kind == nkCommentStmt: continue - if (a.kind != nkConstDef): illFormedAst(a) - checkSonsLen(a, 3) + if a.kind != nkConstDef: illFormedAst(a, c.config) + checkSonsLen(a, 3, c.config) var v = semIdentDef(c, a.sons[0], skConst) var typ: PType = nil if a.sons[1].kind != nkEmpty: typ = semTypeNode(c, a.sons[1], nil) var def = semConstExpr(c, a.sons[2]) if def == nil: - localError(a.sons[2].info, errConstExprExpected) + localError(c.config, a.sons[2].info, errConstExprExpected) continue # check type compatibility between def.typ and typ: if typ != nil: @@ -614,16 +596,16 @@ proc semConst(c: PContext, n: PNode): PNode = else: typ = def.typ if typ == nil: - localError(a.sons[2].info, errConstExprExpected) + localError(c.config, a.sons[2].info, errConstExprExpected) continue if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit: - localError(a.info, "invalid type for const: " & typeToString(typ)) + localError(c.config, a.info, "invalid type for const: " & typeToString(typ)) continue - setVarType(v, typ) + setVarType(c, v, typ) v.ast = def # no need to copy if sfGenSym notin v.flags: addInterfaceDecl(c, v) var b = newNodeI(nkConstDef, a.info) - if importantComments(): b.comment = a.comment + if importantComments(c.config): b.comment = a.comment addSon(b, newSymNode(v)) addSon(b, a.sons[1]) addSon(b, copyTree(def)) @@ -632,12 +614,12 @@ proc semConst(c: PContext, n: PNode): PNode = include semfields proc addForVarDecl(c: PContext, v: PSym) = - if warnShadowIdent in gNotes: + if warnShadowIdent in c.config.notes: let shadowed = findShadowedVar(c, v) if shadowed != nil: # XXX should we do this here? #shadowed.flags.incl(sfShadowed) - message(v.info, warnShadowIdent, v.name.s) + message(c.config, v.info, warnShadowIdent, v.name.s) addDecl(c, v) proc symForVar(c: PContext, n: PNode): PSym = @@ -649,7 +631,7 @@ proc semForVars(c: PContext, n: PNode): PNode = result = n var length = sonsLen(n) let iterBase = n.sons[length-2].typ - var iter = skipTypes(iterBase, {tyGenericInst, tyAlias}) + var iter = skipTypes(iterBase, {tyGenericInst, tyAlias, tySink}) # length == 3 means that there is one for loop variable # and thus no tuple unpacking: if iter.kind != tyTuple or length == 3: @@ -663,9 +645,9 @@ proc semForVars(c: PContext, n: PNode): PNode = n.sons[0] = newSymNode(v) if sfGenSym notin v.flags: addForVarDecl(c, v) else: - localError(n.info, errWrongNumberOfVariables) + localError(c.config, n.info, errWrongNumberOfVariables) elif length-2 != sonsLen(iter): - localError(n.info, errWrongNumberOfVariables) + localError(c.config, n.info, errWrongNumberOfVariables) else: for i in countup(0, length - 3): var v = symForVar(c, n.sons[i]) @@ -683,19 +665,63 @@ proc semForVars(c: PContext, n: PNode): PNode = proc implicitIterator(c: PContext, it: string, arg: PNode): PNode = result = newNodeI(nkCall, arg.info) result.add(newIdentNode(it.getIdent, arg.info)) - if arg.typ != nil and arg.typ.kind == tyVar: + if arg.typ != nil and arg.typ.kind in {tyVar, tyLent}: result.add newDeref(arg) else: result.add arg result = semExprNoDeref(c, result, {efWantIterator}) +proc isTrivalStmtExpr(n: PNode): bool = + for i in 0 .. n.len-2: + if n[i].kind notin {nkEmpty, nkCommentStmt}: + return false + result = true + +proc handleForLoopMacro(c: PContext; n: PNode): PNode = + let iterExpr = n[^2] + if iterExpr.kind in nkCallKinds: + # we transform + # n := for a, b, c in m(x, y, z): Y + # to + # m(n) + let forLoopStmt = magicsys.getCompilerProc(c.graph, "ForLoopStmt") + if forLoopStmt == nil: return + + let headSymbol = iterExpr[0] + var o: TOverloadIter + var match: PSym = nil + var symx = initOverloadIter(o, c, headSymbol) + while symx != nil: + if symx.kind in {skTemplate, skMacro}: + if symx.typ.len == 2 and symx.typ[1] == forLoopStmt.typ: + if match == nil: + match = symx + else: + localError(c.config, n.info, errAmbiguousCallXYZ % [ + getProcHeader(match), getProcHeader(symx), $iterExpr]) + symx = nextOverloadIter(o, c, headSymbol) + + if match == nil: return + var callExpr = newNodeI(nkCall, n.info) + callExpr.add newSymNode(match) + callExpr.add n + case match.kind + of skMacro: result = semMacroExpr(c, callExpr, callExpr, match, {}) + of skTemplate: result = semTemplateExpr(c, callExpr, match, {}) + else: result = nil + proc semFor(c: PContext, n: PNode): PNode = - result = n - checkMinSonsLen(n, 3) + checkMinSonsLen(n, 3, c.config) var length = sonsLen(n) + result = handleForLoopMacro(c, n) + if result != nil: return result openScope(c) + result = n n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator}) var call = n.sons[length-2] + if call.kind == nkStmtListExpr and isTrivalStmtExpr(call): + call = call.lastSon + n.sons[length-2] = call let isCallExpr = call.kind in nkCallKinds if isCallExpr and call[0].kind == nkSym and call[0].sym.magic in {mFields, mFieldPairs, mOmpParFor}: @@ -715,7 +741,7 @@ proc semFor(c: PContext, n: PNode): PNode = elif length == 4: n.sons[length-2] = implicitIterator(c, "pairs", n.sons[length-2]) else: - localError(n.sons[length-2].info, errIteratorExpected) + localError(c.config, n.sons[length-2].info, "iterator within for loop context expected") result = semForVars(c, n) else: result = semForVars(c, n) @@ -726,29 +752,32 @@ proc semFor(c: PContext, n: PNode): PNode = proc semRaise(c: PContext, n: PNode): PNode = result = n - checkSonsLen(n, 1) - if n.sons[0].kind != nkEmpty: - n.sons[0] = semExprWithType(c, n.sons[0]) - var typ = n.sons[0].typ - if typ.kind != tyRef or typ.lastSon.kind != tyObject: - localError(n.info, errExprCannotBeRaised) - - # check if the given object inherits from Exception - var base = typ.lastSon - while true: - if base.sym.magic == mException: - break - if base.lastSon == nil: - localError(n.info, "raised object of type $1 does not inherit from Exception", [typ.sym.name.s]) - return - base = base.lastSon + checkSonsLen(n, 1, c.config) + if n[0].kind != nkEmpty: + n[0] = semExprWithType(c, n[0]) + let typ = n[0].typ + if not isImportedException(typ, c.config): + if typ.kind != tyRef or typ.lastSon.kind != tyObject: + localError(c.config, n.info, errExprCannotBeRaised) + if not isException(typ.lastSon): + localError(c.config, n.info, "raised object of type $1 does not inherit from Exception", + [typeToString(typ)]) proc addGenericParamListToScope(c: PContext, n: PNode) = - if n.kind != nkGenericParams: illFormedAst(n) + if n.kind != nkGenericParams: illFormedAst(n, c.config) for i in countup(0, sonsLen(n)-1): var a = n.sons[i] if a.kind == nkSym: addDecl(c, a.sym) - else: illFormedAst(a) + else: illFormedAst(a, c.config) + +proc typeSectionTypeName(c: PContext; n: PNode): PNode = + if n.kind == nkPragmaExpr: + if n.len == 0: illFormedAst(n, c.config) + result = n.sons[0] + else: + result = n + if result.kind != nkSym: illFormedAst(n, c.config) + proc typeSectionLeftSidePass(c: PContext, n: PNode) = # process the symbols on the left side for the whole type section, before @@ -756,21 +785,21 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] when defined(nimsuggest): - if gCmd == cmdIdeTools: + if c.config.cmd == cmdIdeTools: inc c.inTypeContext suggestStmt(c, a) dec c.inTypeContext if a.kind == nkCommentStmt: continue - if a.kind != nkTypeDef: illFormedAst(a) - checkSonsLen(a, 3) + if a.kind != nkTypeDef: illFormedAst(a, c.config) + checkSonsLen(a, 3, c.config) let name = a.sons[0] var s: PSym if name.kind == nkDotExpr and a[2].kind == nkObjectTy: - let pkgName = considerQuotedIdent(name[0]) - let typName = considerQuotedIdent(name[1]) + let pkgName = considerQuotedIdent(c.config, name[0]) + let typName = considerQuotedIdent(c.config, name[1]) let pkg = c.graph.packageSyms.strTableGet(pkgName) if pkg.isNil or pkg.kind != skPackage: - localError(name.info, "unknown package name: " & pkgName.s) + localError(c.config, name.info, "unknown package name: " & pkgName.s) else: let typsym = pkg.tab.strTableGet(typName) if typsym.isNil: @@ -784,7 +813,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = s = typsym addInterfaceDecl(c, s) else: - localError(name.info, typsym.name.s & " is not a type that can be forwarded") + localError(c.config, name.info, typsym.name.s & " is not a type that can be forwarded") s = typsym else: s = semIdentDef(c, name, skType) @@ -796,7 +825,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = # check if the symbol already exists: let pkg = c.module.owner if not isTopLevel(c) or pkg.isNil: - localError(name.info, "only top level types in a package can be 'package'") + localError(c.config, name.info, "only top level types in a package can be 'package'") else: let typsym = pkg.tab.strTableGet(s.name) if typsym != nil: @@ -804,22 +833,23 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = typeCompleted(typsym) typsym.info = s.info else: - localError(name.info, "cannot complete type '" & s.name.s & "' twice; " & + localError(c.config, name.info, "cannot complete type '" & s.name.s & "' twice; " & "previous type completion was here: " & $typsym.info) s = typsym # add it here, so that recursive types are possible: if sfGenSym notin s.flags: addInterfaceDecl(c, s) - a.sons[0] = newSymNode(s) + if name.kind == nkPragmaExpr: + a.sons[0].sons[0] = newSymNode(s) + else: + a.sons[0] = newSymNode(s) -proc checkCovariantParamsUsages(genericType: PType) = +proc checkCovariantParamsUsages(c: PContext; genericType: PType) = var body = genericType[^1] - proc traverseSubTypes(t: PType): bool = - template error(msg) = localError(genericType.sym.info, msg) - + proc traverseSubTypes(c: PContext; t: PType): bool = + template error(msg) = localError(c.config, genericType.sym.info, msg) result = false - template subresult(r) = let sub = r result = result or sub @@ -828,24 +858,19 @@ proc checkCovariantParamsUsages(genericType: PType) = of tyGenericParam: t.flags.incl tfWeakCovariant return true - of tyObject: for field in t.n: - subresult traverseSubTypes(field.typ) - + subresult traverseSubTypes(c, field.typ) of tyArray: - return traverseSubTypes(t[1]) - + return traverseSubTypes(c, t[1]) of tyProc: for subType in t.sons: if subType != nil: - subresult traverseSubTypes(subType) + subresult traverseSubTypes(c, subType) if result: - error("non-invariant type param used in a proc type: " & $t) - + error("non-invariant type param used in a proc type: " & $t) of tySequence: - return traverseSubTypes(t[0]) - + return traverseSubTypes(c, t[0]) of tyGenericInvocation: let targetBody = t[0] for i in 1 ..< t.len: @@ -866,44 +891,35 @@ proc checkCovariantParamsUsages(genericType: PType) = "' used in a non-contravariant position") result = true else: - subresult traverseSubTypes(param) - + subresult traverseSubTypes(c, param) of tyAnd, tyOr, tyNot, tyStatic, tyBuiltInTypeClass, tyCompositeTypeClass: error("non-invariant type parameters cannot be used with types such '" & $t & "'") - of tyUserTypeClass, tyUserTypeClassInst: error("non-invariant type parameters are not supported in concepts") - of tyTuple: for fieldType in t.sons: - subresult traverseSubTypes(fieldType) - - of tyPtr, tyRef, tyVar: + subresult traverseSubTypes(c, fieldType) + of tyPtr, tyRef, tyVar, tyLent: if t.base.kind == tyGenericParam: return true - return traverseSubTypes(t.base) - - of tyDistinct, tyAlias: - return traverseSubTypes(t.lastSon) - + return traverseSubTypes(c, t.base) + of tyDistinct, tyAlias, tySink: + return traverseSubTypes(c, t.lastSon) of tyGenericInst: - internalAssert false - + internalAssert c.config, false else: discard - - discard traverseSubTypes(body) + discard traverseSubTypes(c, body) proc typeSectionRightSidePass(c: PContext, n: PNode) = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue - if (a.kind != nkTypeDef): illFormedAst(a) - checkSonsLen(a, 3) - let name = a.sons[0] - if (name.kind != nkSym): illFormedAst(a) + if a.kind != nkTypeDef: illFormedAst(a, c.config) + checkSonsLen(a, 3, c.config) + let name = typeSectionTypeName(c, a.sons[0]) var s = name.sym if s.magic == mNone and a.sons[2].kind == nkEmpty: - localError(a.info, errImplOfXexpected, s.name.s) + localError(c.config, a.info, errImplOfXexpected % s.name.s) if s.magic != mNone: processMagicType(c, s) if a.sons[1].kind != nkEmpty: # We have a generic type declaration here. In generic types, @@ -934,7 +950,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = body.size = -1 # could not be computed properly s.typ.sons[sonsLen(s.typ) - 1] = body if tfCovariant in s.typ.flags: - checkCovariantParamsUsages(s.typ) + checkCovariantParamsUsages(c, s.typ) # XXX: This is a temporary limitation: # The codegen currently produces various failures with # generic imported types that have fields, but we need @@ -969,8 +985,8 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = # 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 + internalAssert c.config, st.kind in {tyPtr, tyRef} + internalAssert c.config, st.lastSon.sym == nil incl st.flags, tfRefsAnonObj let obj = newSym(skType, getIdent(s.name.s & ":ObjectType"), getCurrOwner(c), s.info) @@ -978,36 +994,36 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = st.lastSon.sym = obj -proc checkForMetaFields(n: PNode) = +proc checkForMetaFields(c: PContext; n: PNode) = template checkMeta(t) = if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags: - localError(n.info, errTIsNotAConcreteType, t.typeToString) + localError(c.config, n.info, errTIsNotAConcreteType % t.typeToString) if n.isNil: return case n.kind of nkRecList, nkRecCase: - for s in n: checkForMetaFields(s) + for s in n: checkForMetaFields(c, s) of nkOfBranch, nkElse: - checkForMetaFields(n.lastSon) + checkForMetaFields(c, n.lastSon) of nkSym: let t = n.sym.typ case t.kind - of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyPtr, tyRef, - tyProc, tyGenericInvocation, tyGenericInst, tyAlias: + of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyLent, tyPtr, tyRef, + tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink: let start = int ord(t.kind in {tyGenericInvocation, tyGenericInst}) for i in start ..< t.sons.len: checkMeta(t.sons[i]) else: checkMeta(t) else: - internalAssert false + internalAssert c.config, false proc typeSectionFinalPass(c: PContext, n: PNode) = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue - if a.sons[0].kind != nkSym: illFormedAst(a) - var s = a.sons[0].sym + let name = typeSectionTypeName(c, a.sons[0]) + var s = name.sym # compute the type's size and check for illegal recursions: if a.sons[1].kind == nkEmpty: var x = a[2] @@ -1018,7 +1034,7 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = # type aliases are hard: var t = semTypeNode(c, x, nil) assert t != nil - if s.typ != nil and s.typ.kind != tyAlias: + if s.typ != nil and s.typ.kind notin {tyAlias, tySink}: if t.kind in {tyProc, tyGenericInst} and not t.isMetaType: assignType(s.typ, t) s.typ.id = t.id @@ -1026,9 +1042,9 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = assert s.typ != nil assignType(s.typ, t) s.typ.id = t.id # same id - checkConstructedType(s.info, s.typ) + checkConstructedType(c.config, s.info, s.typ) if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil: - checkForMetaFields(s.typ.n) + checkForMetaFields(c, s.typ.n) instAllTypeBoundOp(c, n.info) @@ -1037,14 +1053,14 @@ proc semAllTypeSections(c: PContext; n: PNode): PNode = case n.kind of nkIncludeStmt: for i in 0..<n.len: - var f = checkModuleName(n.sons[i]) + var f = checkModuleName(c.config, n.sons[i]) if f != InvalidFileIDX: - if containsOrIncl(c.includedFiles, f): - localError(n.info, errRecursiveDependencyX, f.toFilename) + if containsOrIncl(c.includedFiles, f.int): + localError(c.config, n.info, errRecursiveDependencyX % f.toFilename) else: let code = gIncludeFile(c.graph, c.module, f, c.cache) gatherStmts c, code, result - excl(c.includedFiles, f) + excl(c.includedFiles, f.int) of nkStmtList: for i in 0 ..< n.len: gatherStmts(c, n.sons[i], result) @@ -1096,12 +1112,12 @@ proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) = s.typ = semProcTypeNode(c, n, genericParams, nil, s.kind) if s.kind notin {skMacro, skTemplate}: if s.typ.sons[0] != nil and s.typ.sons[0].kind == tyStmt: - localError(n.info, errGenerated, "invalid return type: 'stmt'") + localError(c.config, n.info, "invalid return type: 'stmt'") proc addParams(c: PContext, n: PNode, kind: TSymKind) = for i in countup(1, sonsLen(n)-1): if n.sons[i].kind == nkSym: addParamOrResult(c, n.sons[i].sym, kind) - else: illFormedAst(n) + else: illFormedAst(n, c.config) proc semBorrow(c: PContext, n: PNode, s: PSym) = # search for the correct alias: @@ -1110,7 +1126,7 @@ proc semBorrow(c: PContext, n: PNode, s: PSym) = # store the alias: n.sons[bodyPos] = newSymNode(b) else: - localError(n.info, errNoSymbolToBorrowFromFound) + localError(c.config, n.info, errNoSymbolToBorrowFromFound) proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) = if t != nil: @@ -1133,7 +1149,7 @@ proc lookupMacro(c: PContext, n: PNode): PSym = result = n.sym if result.kind notin {skMacro, skTemplate}: result = nil else: - result = searchInScopes(c, considerQuotedIdent(n), {skMacro, skTemplate}) + result = searchInScopes(c, considerQuotedIdent(c.config, n), {skMacro, skTemplate}) proc semProcAnnotation(c: PContext, prc: PNode; validPragmas: TSpecialWords): PNode = @@ -1141,16 +1157,19 @@ proc semProcAnnotation(c: PContext, prc: PNode; if n == nil or n.kind == nkEmpty: return for i in countup(0, n.len-1): var it = n.sons[i] - var key = if it.kind == nkExprColonExpr: it.sons[0] else: it + var key = if it.kind in nkPragmaCallKinds and it.len >= 1: it.sons[0] else: it let m = lookupMacro(c, key) if m == nil: if key.kind == nkIdent and key.ident.id == ord(wDelegator): - if considerQuotedIdent(prc.sons[namePos]).s == "()": + if considerQuotedIdent(c.config, prc.sons[namePos]).s == "()": prc.sons[namePos] = newIdentNode(c.cache.idDelegator, prc.info) prc.sons[pragmasPos] = copyExcept(n, i) else: - localError(prc.info, errOnlyACallOpCanBeDelegator) + localError(c.config, prc.info, "only a call operator can be a delegator") continue + elif sfCustomPragma in m.flags: + continue # semantic check for custom pragma happens later in semProcAux + # we transform ``proc p {.m, rest.}`` into ``m(do: proc p {.rest.})`` and # let the semantic checker deal with it: var x = newNodeI(nkCall, n.info) @@ -1159,10 +1178,12 @@ proc semProcAnnotation(c: PContext, prc: PNode; if prc[pragmasPos].kind != nkEmpty and prc[pragmasPos].len == 0: prc.sons[pragmasPos] = emptyNode - if it.kind == nkExprColonExpr: - # pass pragma argument to the macro too: - x.add(it.sons[1]) + if it.kind in nkPragmaCallKinds and it.len > 1: + # pass pragma arguments to the macro too: + for i in 1..<it.len: + x.add(it.sons[i]) x.add(prc) + # recursion assures that this works for multiple macro annotations too: result = semExpr(c, x) # since a proc annotation can set pragmas, we process these here again. @@ -1189,7 +1210,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = result = semProcAnnotation(c, n, lambdaPragmas) if result != nil: return result result = n - checkSonsLen(n, bodyPos + 1) + checkSonsLen(n, bodyPos + 1, c.config) var s: PSym if n[namePos].kind != nkSym: s = newSym(skProc, c.cache.idAnon, getCurrOwner(c), n.info) @@ -1206,9 +1227,6 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = gp = newNodeI(nkGenericParams, n.info) if n.sons[paramsPos].kind != nkEmpty: - #if n.kind == nkDo and not experimentalMode(c): - # localError(n.sons[paramsPos].info, - # "use the {.experimental.} pragma to enable 'do' with parameters") semParamList(c, n.sons[paramsPos], gp, s) # paramsTypeCheck(c, s.typ) if sonsLen(gp) > 0 and n.sons[genericParamsPos].kind == nkEmpty: @@ -1218,10 +1236,10 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = s.typ = newProcType(c, n.info) if n.sons[pragmasPos].kind != nkEmpty: pragma(c, s, n.sons[pragmasPos], lambdaPragmas) - s.options = gOptions + s.options = c.config.options if n.sons[bodyPos].kind != nkEmpty: if sfImportc in s.flags: - localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s) + localError(c.config, n.sons[bodyPos].info, errImplOfXNotAllowed % s.name.s) #if efDetermineType notin flags: # XXX not good enough; see tnamedparamanonproc.nim if gp.len == 0 or (gp.len == 1 and tfRetType in gp[0].typ.flags): @@ -1229,13 +1247,13 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = addResult(c, s.typ.sons[0], n.info, skProc) addResultNode(c, n) let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) - n.sons[bodyPos] = transformBody(c.module, semBody, s) + n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s) popProcCon(c) elif efOperand notin flags: - localError(n.info, errGenericLambdaNotAllowed) + localError(c.config, n.info, errGenericLambdaNotAllowed) sideEffectsCheck(c, s) else: - localError(n.info, errImplOfXexpected, s.name.s) + localError(c.config, n.info, errImplOfXexpected % s.name.s) closeScope(c) # close scope for parameters popOwner(c) result.typ = s.typ @@ -1260,7 +1278,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = for i in 1..<params.len: if params[i].typ.kind in {tyTypeDesc, tyGenericParam, tyFromExpr}+tyTypeClasses: - localError(params[i].info, "cannot infer type of parameter: " & + localError(c.config, params[i].info, "cannot infer type of parameter: " & params[i].sym.name.s) #params[i].sym.owner = s openScope(c) @@ -1270,7 +1288,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = addResult(c, n.typ.sons[0], n.info, skProc) addResultNode(c, n) let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) - n.sons[bodyPos] = transformBody(c.module, semBody, s) + n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s) popProcCon(c) popOwner(c) closeScope(c) @@ -1302,27 +1320,26 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) = proc semOverride(c: PContext, s: PSym, n: PNode) = case s.name.s.normalize - of "destroy", "=destroy": - if newDestructors: - let t = s.typ - var noError = false - if t.len == 2 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 in {tyGenericBody, tyGenericInst}: obj = obj.lastSon - elif obj.kind == tyGenericInvocation: obj = obj.sons[0] - else: break - if obj.kind in {tyObject, tyDistinct}: - if obj.destructor.isNil: - obj.destructor = s - else: - localError(n.info, errGenerated, - "cannot bind another '" & s.name.s & "' to: " & typeToString(obj)) - noError = true - if not noError and sfSystemModule notin s.owner.flags: - localError(n.info, errGenerated, - "signature for '" & s.name.s & "' must be proc[T: object](x: var T)") + of "=destroy": + let t = s.typ + var noError = false + if t.len == 2 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 in {tyGenericBody, tyGenericInst}: obj = obj.lastSon + elif obj.kind == tyGenericInvocation: obj = obj.sons[0] + else: break + if obj.kind in {tyObject, tyDistinct}: + if obj.destructor.isNil: + obj.destructor = s + else: + localError(c.config, n.info, errGenerated, + "cannot bind another '" & s.name.s & "' to: " & typeToString(obj)) + noError = true + if not noError and sfSystemModule notin s.owner.flags: + localError(c.config, n.info, errGenerated, + "signature for '" & s.name.s & "' must be proc[T: object](x: var T)") incl(s.flags, sfUsed) of "deepcopy", "=deepcopy": if s.typ.len == 2 and @@ -1338,13 +1355,13 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = if t.kind in {tyObject, tyDistinct, tyEnum}: if t.deepCopy.isNil: t.deepCopy = s else: - localError(n.info, errGenerated, + localError(c.config, n.info, errGenerated, "cannot bind another 'deepCopy' to: " & typeToString(t)) else: - localError(n.info, errGenerated, + localError(c.config, n.info, errGenerated, "cannot bind 'deepCopy' to: " & typeToString(t)) else: - localError(n.info, errGenerated, + localError(c.config, n.info, errGenerated, "signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T") incl(s.flags, sfUsed) of "=", "=sink": @@ -1369,15 +1386,15 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = if opr[].isNil: opr[] = s else: - localError(n.info, errGenerated, + localError(c.config, n.info, errGenerated, "cannot bind another '" & s.name.s & "' to: " & typeToString(obj)) return if sfSystemModule notin s.owner.flags: - localError(n.info, errGenerated, + localError(c.config, n.info, errGenerated, "signature for '" & s.name.s & "' must be proc[T: object](x: var T; y: T)") else: if sfOverriden in s.flags: - localError(n.info, errGenerated, + localError(c.config, n.info, errGenerated, "'destroy' or 'deepCopy' expected for 'override'") proc cursorInProcAux(n: PNode): bool = @@ -1413,14 +1430,14 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) = for col in countup(1, sonsLen(tt)-1): let t = tt.sons[col] if t != nil and t.kind == tyGenericInvocation: - var x = skipTypes(t.sons[0], {tyVar, tyPtr, tyRef, tyGenericInst, + var x = skipTypes(t.sons[0], {tyVar, tyLent, tyPtr, tyRef, tyGenericInst, tyGenericInvocation, tyGenericBody, - tyAlias}) + tyAlias, tySink}) if x.kind == tyObject and t.len-1 == n.sons[genericParamsPos].len: foundObj = true x.methods.safeAdd((col,s)) if not foundObj: - message(n.info, warnDeprecated, "generic method not attachable to object type") + message(c.config, n.info, warnDeprecated, "generic method not attachable to object type") else: # why check for the body? bug #2400 has none. Checking for sfForward makes # no sense either. @@ -1428,7 +1445,7 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) = if hasObjParam(s): methodDef(c.graph, s, fromCache=false) else: - localError(n.info, errXNeedsParamObjectType, "method") + localError(c.config, n.info, "'method' needs a parameter that has an object type") proc semProcAux(c: PContext, n: PNode, kind: TSymKind, validPragmas: TSpecialWords, @@ -1436,7 +1453,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, result = semProcAnnotation(c, n, validPragmas) if result != nil: return result result = n - checkSonsLen(n, bodyPos + 1) + checkSonsLen(n, bodyPos + 1, c.config) var s: PSym var typeIsDetermined = false var isAnon = false @@ -1493,7 +1510,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, n.sons[patternPos] = semPattern(c, n.sons[patternPos]) if s.kind == skIterator: s.typ.flags.incl(tfIterator) - + elif s.kind == skFunc: + incl(s.flags, sfNoSideEffect) + incl(s.typ.flags, tfNoSideEffect) var proto = searchForProc(c, oldScope, s) if proto == nil or isAnon: if s.kind == skIterator: @@ -1523,10 +1542,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # XXX This needs more checks eventually, for example that external # linking names do agree: if proto.typ.callConv != s.typ.callConv or proto.typ.flags < s.typ.flags: - localError(n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX, - "'" & proto.name.s & "' from " & $proto.info) + localError(c.config, n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX % + ("'" & proto.name.s & "' from " & $proto.info)) if sfForward notin proto.flags: - wrongRedefinition(n.info, proto.name.s) + wrongRedefinition(c, n.info, proto.name.s) excl(proto.flags, sfForward) closeScope(c) # close scope with wrong parameter symbols openScope(c) # open scope for old (correct) parameter symbols @@ -1539,30 +1558,32 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, n.sons[genericParamsPos] = proto.ast.sons[genericParamsPos] n.sons[paramsPos] = proto.ast.sons[paramsPos] n.sons[pragmasPos] = proto.ast.sons[pragmasPos] - if n.sons[namePos].kind != nkSym: internalError(n.info, "semProcAux") + if n.sons[namePos].kind != nkSym: internalError(c.config, n.info, "semProcAux") n.sons[namePos].sym = proto - if importantComments() and not isNil(proto.ast.comment): + if importantComments(c.config) and not isNil(proto.ast.comment): n.comment = proto.ast.comment proto.ast = n # needed for code generation popOwner(c) pushOwner(c, s) - s.options = gOptions + s.options = c.config.options if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n) if s.name.s[0] in {'.', '('}: - if s.name.s in [".", ".()", ".="] and not experimentalMode(c) and not newDestructors: - message(n.info, warnDeprecated, "overloaded '.' and '()' operators are now .experimental; " & s.name.s) - elif s.name.s == "()" and not experimentalMode(c): - message(n.info, warnDeprecated, "overloaded '()' operators are now .experimental; " & s.name.s) + if s.name.s in [".", ".()", ".="] and {destructor, dotOperators} * c.features == {}: + localError(c.config, n.info, "the overloaded " & s.name.s & + " operator has to be enabled with {.experimental: \"dotOperators\".}") + elif s.name.s == "()" and callOperator notin c.features: + localError(c.config, n.info, "the overloaded " & s.name.s & + " operator has to be enabled with {.experimental: \"callOperator\".}") if n.sons[bodyPos].kind != nkEmpty: # for DLL generation it is annoying to check for sfImportc! if sfBorrow in s.flags: - localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s) + localError(c.config, n.sons[bodyPos].info, errImplOfXNotAllowed % s.name.s) let usePseudoGenerics = kind in {skMacro, skTemplate} # Macros and Templates can have generic parameters, but they are # only used for overload resolution (there is no instantiation of # the symbol, so we must process the body now) - if not usePseudoGenerics and gIdeCmd in {ideSug, ideCon} and not + if not usePseudoGenerics and c.config.ideCmd in {ideSug, ideCon} and not cursorInProc(n.sons[bodyPos]): discard "speed up nimsuggest" if s.kind == skMethod: semMethodPrototype(c, s, n) @@ -1580,7 +1601,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) # unfortunately we cannot skip this step when in 'system.compiles' # context as it may even be evaluated in 'system.compiles': - n.sons[bodyPos] = transformBody(c.module, semBody, s) + n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s) else: if s.typ.sons[0] != nil and kind != skIterator: addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info)) @@ -1596,7 +1617,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, popProcCon(c) else: if s.kind == skMethod: semMethodPrototype(c, s, n) - if proto != nil: localError(n.info, errImplOfXexpected, proto.name.s) + if proto != nil: localError(c.config, n.info, errImplOfXexpected % proto.name.s) if {sfImportc, sfBorrow} * s.flags == {} and s.magic == mNone: incl(s.flags, sfForward) elif sfBorrow in s.flags: semBorrow(c, n, s) @@ -1611,7 +1632,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, result.typ = s.typ if isTopLevel(c) and s.kind != skIterator and s.typ.callConv == ccClosure: - localError(s.info, "'.closure' calling convention for top level routines is invalid") + localError(c.config, s.info, "'.closure' calling convention for top level routines is invalid") proc determineType(c: PContext, s: PSym) = if s.typ != nil: return @@ -1627,12 +1648,16 @@ proc semIterator(c: PContext, n: PNode): PNode = n[namePos].sym.owner = getCurrOwner(c) n[namePos].sym.kind = skIterator result = semProcAux(c, n, skIterator, iteratorPragmas) + # bug #7093: if after a macro transformation we don't have an + # nkIteratorDef aynmore, return. The iterator then might have been + # sem'checked already. (Or not, if the macro skips it.) + if result.kind != n.kind: return var s = result.sons[namePos].sym var t = s.typ if t.sons[0] == nil and s.typ.callConv != ccClosure: - localError(n.info, errXNeedsReturnType, "iterator") + localError(c.config, n.info, "iterator needs a return type") if isAnon and s.typ.callConv == ccInline: - localError(n.info, "inline iterators are not first-class / cannot be assigned to variables") + localError(c.config, n.info, "inline iterators are not first-class / cannot be assigned to variables") # iterators are either 'inline' or 'closure'; for backwards compatibility, # we require first class iterators to be marked with 'closure' explicitly # -- at least for 0.9.2. @@ -1640,13 +1665,8 @@ proc semIterator(c: PContext, n: PNode): PNode = incl(s.typ.flags, tfCapturesEnv) else: s.typ.callConv = ccInline - when false: - if s.typ.callConv != ccInline: - s.typ.callConv = ccClosure - # and they always at least use the 'env' for the state field: - incl(s.typ.flags, tfCapturesEnv) if n.sons[bodyPos].kind == nkEmpty and s.magic == mNone: - localError(n.info, errImplOfXexpected, s.name.s) + localError(c.config, n.info, errImplOfXexpected % s.name.s) proc semProc(c: PContext, n: PNode): PNode = result = semProcAux(c, n, skProc, procPragmas) @@ -1655,10 +1675,14 @@ proc semFunc(c: PContext, n: PNode): PNode = result = semProcAux(c, n, skFunc, procPragmas) proc semMethod(c: PContext, n: PNode): PNode = - if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "method") + if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "method") result = semProcAux(c, n, skMethod, methodPragmas) # macros can transform converters to nothing: if namePos >= result.safeLen: return result + # bug #7093: if after a macro transformation we don't have an + # nkIteratorDef aynmore, return. The iterator then might have been + # sem'checked already. (Or not, if the macro skips it.) + if result.kind != nkMethodDef: return var s = result.sons[namePos].sym # we need to fix the 'auto' return type for the dispatcher here (see tautonotgeneric # test case): @@ -1672,22 +1696,30 @@ proc semMethod(c: PContext, n: PNode): PNode = else: disp.ast[resultPos].sym.typ = ret proc semConverterDef(c: PContext, n: PNode): PNode = - if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "converter") - checkSonsLen(n, bodyPos + 1) + if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "converter") + checkSonsLen(n, bodyPos + 1, c.config) result = semProcAux(c, n, skConverter, converterPragmas) # macros can transform converters to nothing: if namePos >= result.safeLen: return result + # bug #7093: if after a macro transformation we don't have an + # nkIteratorDef aynmore, return. The iterator then might have been + # sem'checked already. (Or not, if the macro skips it.) + if result.kind != nkConverterDef: return var s = result.sons[namePos].sym var t = s.typ - if t.sons[0] == nil: localError(n.info, errXNeedsReturnType, "converter") - if sonsLen(t) != 2: localError(n.info, errXRequiresOneArgument, "converter") + if t.sons[0] == nil: localError(c.config, n.info, errXNeedsReturnType % "converter") + if sonsLen(t) != 2: localError(c.config, n.info, "a converter takes exactly one argument") addConverter(c, s) proc semMacroDef(c: PContext, n: PNode): PNode = - checkSonsLen(n, bodyPos + 1) + checkSonsLen(n, bodyPos + 1, c.config) result = semProcAux(c, n, skMacro, macroPragmas) # macros can transform macros to nothing: if namePos >= result.safeLen: return result + # bug #7093: if after a macro transformation we don't have an + # nkIteratorDef aynmore, return. The iterator then might have been + # sem'checked already. (Or not, if the macro skips it.) + if result.kind != nkMacroDef: return var s = result.sons[namePos].sym var t = s.typ var allUntyped = true @@ -1695,21 +1727,21 @@ proc semMacroDef(c: PContext, n: PNode): PNode = let param = t.n.sons[i].sym if param.typ.kind != tyExpr: allUntyped = false if allUntyped: incl(s.flags, sfAllUntyped) - if t.sons[0] == nil: localError(n.info, errXNeedsReturnType, "macro") + if t.sons[0] == nil: localError(c.config, n.info, "macro needs a return type") if n.sons[bodyPos].kind == nkEmpty: - localError(n.info, errImplOfXexpected, s.name.s) + localError(c.config, n.info, errImplOfXexpected % s.name.s) proc evalInclude(c: PContext, n: PNode): PNode = result = newNodeI(nkStmtList, n.info) addSon(result, n) for i in countup(0, sonsLen(n) - 1): - var f = checkModuleName(n.sons[i]) + var f = checkModuleName(c.config, n.sons[i]) if f != InvalidFileIDX: - if containsOrIncl(c.includedFiles, f): - localError(n.info, errRecursiveDependencyX, f.toFilename) + if containsOrIncl(c.includedFiles, f.int): + localError(c.config, n.info, errRecursiveDependencyX % f.toFilename) else: addSon(result, semStmt(c, gIncludeFile(c.graph, c.module, f, c.cache))) - excl(c.includedFiles, f) + excl(c.includedFiles, f.int) proc setLine(n: PNode, info: TLineInfo) = for i in 0 ..< safeLen(n): setLine(n.sons[i], info) @@ -1733,19 +1765,13 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode = proc semStaticStmt(c: PContext, n: PNode): PNode = #echo "semStaticStmt" #writeStackTrace() + inc c.inStaticContext let a = semStmt(c, n.sons[0]) + dec c.inStaticContext n.sons[0] = a - evalStaticStmt(c.module, c.cache, a, c.p.owner) + evalStaticStmt(c.module, c.cache, c.graph, a, c.p.owner) result = newNodeI(nkDiscardStmt, n.info, 1) result.sons[0] = emptyNode - when false: - result = evalStaticStmt(c.module, a, c.p.owner) - if result.isNil: - LocalError(n.info, errCannotInterpretNodeX, renderTree(n)) - result = emptyNode - elif result.kind == nkEmpty: - result = newNodeI(nkDiscardStmt, n.info, 1) - result.sons[0] = emptyNode proc usesResult(n: PNode): bool = # nkStmtList(expr) properly propagates the void context, @@ -1764,7 +1790,7 @@ proc inferConceptStaticParam(c: PContext, inferred, n: PNode) = var typ = inferred.typ let res = semConstExpr(c, n) if not sameType(res.typ, typ.base): - localError(n.info, + localError(c.config, n.info, "cannot infer the concept parameter '%s', due to a type mismatch. " & "attempt to equate '%s' and '%s'.", [inferred.renderTree, $res.typ, $typ.base]) @@ -1787,80 +1813,47 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = # nkNilLit, nkEmpty}: # dec last for i in countup(0, length - 1): - let k = n.sons[i].kind - case k - of nkFinally, nkExceptBranch: - # stand-alone finally and except blocks are - # transformed into regular try blocks: - # - # var f = fopen("somefile") | var f = fopen("somefile") - # finally: fclose(f) | try: - # ... | ... - # | finally: - # | fclose(f) - var deferPart: PNode - if k == nkDefer: - deferPart = newNodeI(nkFinally, n.sons[i].info) - deferPart.add n.sons[i].sons[0] - elif k == nkFinally: - message(n.info, warnDeprecated, - "use 'defer'; standalone 'finally'") - deferPart = n.sons[i] - else: - message(n.info, warnDeprecated, - "use an explicit 'try'; standalone 'except'") - deferPart = n.sons[i] - var tryStmt = newNodeI(nkTryStmt, n.sons[i].info) - var body = newNodeI(nkStmtList, n.sons[i].info) - if i < n.sonsLen - 1: - body.sons = n.sons[(i+1)..n.len-1] - tryStmt.addSon(body) - tryStmt.addSon(deferPart) - n.sons[i] = semTry(c, tryStmt) - n.sons.setLen(i+1) + var expr = semExpr(c, n.sons[i], flags) + n.sons[i] = expr + if c.matchedConcept != nil and expr.typ != nil and + (nfFromTemplate notin n.flags or i != last): + case expr.typ.kind + of tyBool: + if expr.kind == nkInfix and + expr[0].kind == nkSym and + expr[0].sym.name.s == "==": + if expr[1].typ.isUnresolvedStatic: + inferConceptStaticParam(c, expr[1], expr[2]) + continue + elif expr[2].typ.isUnresolvedStatic: + inferConceptStaticParam(c, expr[2], expr[1]) + continue + + let verdict = semConstExpr(c, n[i]) + if verdict.intVal == 0: + localError(c.config, result.info, "concept predicate failed") + of tyUnknown: continue + else: discard + if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]): + voidContext = true + n.typ = enforceVoidContext + if i == last and (length == 1 or efWantValue in flags): n.typ = n.sons[i].typ - return + if not isEmptyType(n.typ): n.kind = nkStmtListExpr + elif i != last or voidContext: + discardCheck(c, n.sons[i]) else: - var expr = semExpr(c, n.sons[i], flags) - n.sons[i] = expr - if c.matchedConcept != nil and expr.typ != nil and - (nfFromTemplate notin n.flags or i != last): - case expr.typ.kind - of tyBool: - if expr.kind == nkInfix and - expr[0].kind == nkSym and - expr[0].sym.name.s == "==": - if expr[1].typ.isUnresolvedStatic: - inferConceptStaticParam(c, expr[1], expr[2]) - continue - elif expr[2].typ.isUnresolvedStatic: - inferConceptStaticParam(c, expr[2], expr[1]) - continue - - let verdict = semConstExpr(c, n[i]) - if verdict.intVal == 0: - localError(result.info, "concept predicate failed") - of tyUnknown: continue - else: discard - if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]): - voidContext = true - n.typ = enforceVoidContext - if i == last and (length == 1 or efWantValue in flags): - n.typ = n.sons[i].typ - if not isEmptyType(n.typ): n.kind = nkStmtListExpr - elif i != last or voidContext: - discardCheck(c, n.sons[i]) - else: - n.typ = n.sons[i].typ - if not isEmptyType(n.typ): n.kind = nkStmtListExpr - if n.sons[i].kind in LastBlockStmts or - n.sons[i].kind in nkCallKinds and n.sons[i][0].kind == nkSym and sfNoReturn in n.sons[i][0].sym.flags: - for j in countup(i + 1, length - 1): - case n.sons[j].kind - of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr, - nkBlockStmt, nkState: discard - else: localError(n.sons[j].info, errStmtInvalidAfterReturn) - else: discard + n.typ = n.sons[i].typ + if not isEmptyType(n.typ): n.kind = nkStmtListExpr + if n.sons[i].kind in LastBlockStmts or + n.sons[i].kind in nkCallKinds and n.sons[i][0].kind == nkSym and + sfNoReturn in n.sons[i][0].sym.flags: + for j in countup(i + 1, length - 1): + case n.sons[j].kind + of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr, + nkBlockStmt, nkState: discard + else: localError(c.config, n.sons[j].info, "unreachable statement after 'return'") + else: discard if result.len == 1 and # concept bodies should be preserved as a stmt list: @@ -1876,15 +1869,6 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = not (result.comment[0] == '#' and result.comment[1] == '#'): # it is an old-style comment statement: we replace it with 'discard ""': prettybase.replaceComment(result.info) - when false: - # a statement list (s; e) has the type 'e': - if result.kind == nkStmtList and result.len > 0: - var lastStmt = lastSon(result) - if lastStmt.kind != nkNilLit and not implicitlyDiscardable(lastStmt): - result.typ = lastStmt.typ - #localError(lastStmt.info, errGenerated, - # "Last expression must be explicitly returned if it " & - # "is discardable or discarded") proc semStmt(c: PContext, n: PNode): PNode = # now: simply an alias: diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index f90dff8f1..352bc5c6b 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -26,6 +26,9 @@ discard """ a way to achieve lexical scoping at compile time. """ +const + errImplOfXNotAllowed = "implementation of '$1' is not allowed" + type TSymBinding = enum spNone, spGenSym, spInject @@ -60,7 +63,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode = # (s.kind notin routineKinds or s.magic != mNone): # for instance 'nextTry' is both in tables.nim and astalgo.nim ... result = newSymNode(s, n.info) - markUsed(n.info, s, c.graph.usageSym) + markUsed(c.config, n.info, s, c.graph.usageSym) else: # semantic checking requires a type; ``fitNode`` deals with it # appropriately @@ -91,20 +94,20 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode = else: for x in items(sc): toBind.incl(x.sym.id) else: - illFormedAst(a) + illFormedAst(a, c.config) result = newNodeI(nkEmpty, n.info) proc semMixinStmt(c: PContext, n: PNode, toMixin: var IntSet): PNode = for i in 0 ..< n.len: - toMixin.incl(considerQuotedIdent(n.sons[i]).id) + toMixin.incl(considerQuotedIdent(c.config, n.sons[i]).id) result = newNodeI(nkEmpty, n.info) -proc replaceIdentBySym(n: var PNode, s: PNode) = +proc replaceIdentBySym(c: PContext; n: var PNode, s: PNode) = case n.kind - of nkPostfix: replaceIdentBySym(n.sons[1], s) - of nkPragmaExpr: replaceIdentBySym(n.sons[0], s) + of nkPostfix: replaceIdentBySym(c, n.sons[1], s) + of nkPragmaExpr: replaceIdentBySym(c, n.sons[0], s) of nkIdent, nkAccQuoted, nkSym: n = s - else: illFormedAst(n) + else: illFormedAst(n, c.config) type TemplCtx = object @@ -129,7 +132,7 @@ proc getIdentNode(c: var TemplCtx, n: PNode): PNode = result = newSymNode(s, n.info) of nkAccQuoted, nkSym: result = n else: - illFormedAst(n) + illFormedAst(n, c.c.config) result = n proc isTemplParam(c: TemplCtx, n: PNode): bool {.inline.} = @@ -163,7 +166,7 @@ proc onlyReplaceParams(c: var TemplCtx, n: PNode): PNode = result.sons[i] = onlyReplaceParams(c, n.sons[i]) proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym = - result = newSym(kind, considerQuotedIdent(n), c.owner, n.info) + result = newSym(kind, considerQuotedIdent(c.c.config, n), c.owner, n.info) incl(result.flags, sfGenSym) incl(result.flags, sfShadowed) @@ -184,12 +187,12 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = n = onlyReplaceParams(c, n) return else: - illFormedAst(x) + illFormedAst(x, c.c.config) let ident = getIdentNode(c, x) if not isTemplParam(c, ident): c.toInject.incl(x.ident.id) else: - replaceIdentBySym(n, ident) + replaceIdentBySym(c.c, n, ident) else: let ident = getIdentNode(c, n) if not isTemplParam(c, ident): @@ -203,17 +206,17 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = # # We need to ensure that both 'a' produce the same gensym'ed symbol. # So we need only check the *current* scope. - let s = localSearchInScope(c.c, considerQuotedIdent ident) + let s = localSearchInScope(c.c, considerQuotedIdent(c.c.config, ident)) if s != nil and s.owner == c.owner and sfGenSym in s.flags: styleCheckUse(n.info, s) - replaceIdentBySym(n, newSymNode(s, n.info)) + replaceIdentBySym(c.c, n, newSymNode(s, n.info)) else: let local = newGenSym(k, ident, c) addPrelimDecl(c.c, local) styleCheckDef(n.info, local) - replaceIdentBySym(n, newSymNode(local, n.info)) + replaceIdentBySym(c.c, n, newSymNode(local, n.info)) else: - replaceIdentBySym(n, ident) + replaceIdentBySym(c.c, n, ident) proc semTemplSymbol(c: PContext, n: PNode, s: PSym): PNode = incl(s.flags, sfUsed) @@ -249,7 +252,7 @@ proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode = proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode = result = n - checkSonsLen(n, bodyPos + 1) + checkSonsLen(n, bodyPos + 1, c.c.config) # routines default to 'inject': if n.kind notin nkLambdaKinds and symBinding(n.sons[pragmasPos]) == spGenSym: let ident = getIdentNode(c, n.sons[namePos]) @@ -281,8 +284,8 @@ proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind; start=0) = for i in countup(start, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue - if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a) - checkMinSonsLen(a, 3) + if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a, c.c.config) + checkMinSonsLen(a, 3, c.c.config) var L = sonsLen(a) when defined(nimsuggest): inc c.c.inTypeContext @@ -354,7 +357,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = n.sons[0] = semTemplBody(c, n.sons[0]) for i in countup(1, sonsLen(n)-1): var a = n.sons[i] - checkMinSonsLen(a, 1) + checkMinSonsLen(a, 1, c.c.config) var L = sonsLen(a) for j in countup(0, L-2): a.sons[j] = semTemplBody(c, a.sons[j]) @@ -371,7 +374,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = closeScope(c) closeScope(c) of nkBlockStmt, nkBlockExpr, nkBlockType: - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.c.config) openScope(c) if n.sons[0].kind != nkEmpty: addLocalDecl(c, n.sons[0], skLabel) @@ -384,11 +387,11 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = n.sons[1] = semTemplBody(c, n.sons[1]) closeScope(c) of nkTryStmt: - checkMinSonsLen(n, 2) + checkMinSonsLen(n, 2, c.c.config) n.sons[0] = semTemplBodyScope(c, n.sons[0]) for i in countup(1, sonsLen(n)-1): var a = n.sons[i] - checkMinSonsLen(a, 1) + checkMinSonsLen(a, 1, c.c.config) var L = sonsLen(a) openScope(c) for j in countup(0, L-2): @@ -402,15 +405,15 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = of nkVarSection: semTemplSomeDecl(c, n, skVar) of nkLetSection: semTemplSomeDecl(c, n, skLet) of nkFormalParams: - checkMinSonsLen(n, 1) + checkMinSonsLen(n, 1, c.c.config) n.sons[0] = semTemplBody(c, n.sons[0]) semTemplSomeDecl(c, n, skParam, 1) of nkConstSection: for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue - if (a.kind != nkConstDef): illFormedAst(a) - checkSonsLen(a, 3) + if (a.kind != nkConstDef): illFormedAst(a, c.c.config) + checkSonsLen(a, 3, c.c.config) addLocalDecl(c, a.sons[0], skConst) a.sons[1] = semTemplBody(c, a.sons[1]) a.sons[2] = semTemplBody(c, a.sons[2]) @@ -418,14 +421,14 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue - if (a.kind != nkTypeDef): illFormedAst(a) - checkSonsLen(a, 3) + if (a.kind != nkTypeDef): illFormedAst(a, c.c.config) + checkSonsLen(a, 3, c.c.config) addLocalDecl(c, a.sons[0], skType) for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue - if (a.kind != nkTypeDef): illFormedAst(a) - checkSonsLen(a, 3) + if (a.kind != nkTypeDef): illFormedAst(a, c.c.config) + checkSonsLen(a, 3, c.c.config) if a.sons[1].kind != nkEmpty: openScope(c) a.sons[1] = semTemplBody(c, a.sons[1]) @@ -468,7 +471,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = for i in 0 ..< n.len: result.add(n[i]) result = semTemplBodySons(c, result) of nkAsgn, nkFastAsgn: - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.c.config) let a = n.sons[0] let b = n.sons[1] @@ -608,8 +611,11 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = popOwner(c) s.ast = n result = n - if n.sons[bodyPos].kind == nkEmpty: - localError(n.info, errImplOfXexpected, s.name.s) + if sfCustomPragma in s.flags: + if n.sons[bodyPos].kind != nkEmpty: + localError(c.config, n.sons[bodyPos].info, errImplOfXNotAllowed % s.name.s) + elif n.sons[bodyPos].kind == nkEmpty: + localError(c.config, n.info, "implementation of '$1' expected" % s.name.s) var proto = searchForProc(c, c.currentScope, s) if proto == nil: addInterfaceOverloadableSymAt(c, c.currentScope, s) @@ -652,7 +658,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode = if s != nil and s.owner == c.owner and s.kind == skParam: result = newParam(c, n, s) else: - localError(n.info, errInvalidExpression) + localError(c.c.config, n.info, "invalid expression") result = n proc stupidStmtListExpr(n: PNode): bool = @@ -672,7 +678,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode = # we support '(pattern){x}' to bind a subpattern to a parameter 'x'; # '(pattern){|x}' does the same but the matches will be gathered in 'x' if n.len != 2: - localError(n.info, errInvalidExpression) + localError(c.c.config, n.info, "invalid expression") elif n.sons[1].kind == nkIdent: n.sons[0] = semPatternBody(c, n.sons[0]) n.sons[1] = expectParam(c, n.sons[1]) @@ -682,9 +688,9 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode = n.sons[0] = semPatternBody(c, n.sons[0]) n.sons[1].sons[1] = expectParam(c, n.sons[1].sons[1]) else: - localError(n.info, errInvalidExpression) + localError(c.c.config, n.info, "invalid expression") else: - localError(n.info, errInvalidExpression) + localError(c.c.config, n.info, "invalid expression") of nkStmtList, nkStmtListExpr: if stupidStmtListExpr(n): result = semPatternBody(c, n.lastSon) @@ -756,5 +762,5 @@ proc semPattern(c: PContext, n: PNode): PNode = if result.len == 1: result = result.sons[0] elif result.len == 0: - localError(n.info, errInvalidExpression) + localError(c.config, n.info, "a pattern cannot be empty") closeScope(c) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index cb66685b2..8b5c26f99 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -10,6 +10,33 @@ # this module does the semantic checking of type declarations # included from sem.nim +const + errStringLiteralExpected = "string literal expected" + errIntLiteralExpected = "integer literal expected" + errWrongNumberOfVariables = "wrong number of variables" + errInvalidOrderInEnumX = "invalid order in enum '$1'" + errOrdinalTypeExpected = "ordinal type expected" + errSetTooBig = "set is too large" + errBaseTypeMustBeOrdinal = "base type of a set must be an ordinal" + errInheritanceOnlyWithNonFinalObjects = "inheritance only works with non-final objects" + errXExpectsOneTypeParam = "'$1' expects one type parameter" + errArrayExpectsTwoTypeParams = "array expects two type parameters" + errInvalidVisibilityX = "invalid visibility: '$1'" + errInitHereNotAllowed = "initialization not allowed here" + errXCannotBeAssignedTo = "'$1' cannot be assigned to" + errIteratorNotAllowed = "iterators can only be defined at the module's top level" + errXNeedsReturnType = "$1 needs a return type" + errNoReturnTypeDeclared = "no return type declared" + errTIsNotAConcreteType = "'$1' is not a concrete type" + errTypeExpected = "type expected" + errXOnlyAtModuleScope = "'$1' is only allowed at top level" + errDuplicateCaseLabel = "duplicate case label" + errMacroBodyDependsOnGenericTypes = "the macro body cannot be compiled, " & + "because the parameter '$1' has a generic type" + errIllegalRecursionInTypeX = "illegal recursion in type '$1'" + errNoGenericParamsAllowedForX = "no generic parameters allowed for $1" + errInOutFlagNotExtern = "the '$1' modifier can be used only with imported types" + proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType = if prev == nil: result = newTypeS(kind, c) @@ -34,11 +61,11 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = base = nil result = newOrPrevType(tyEnum, prev, c) result.n = newNodeI(nkEnumTy, n.info) - checkMinSonsLen(n, 1) + checkMinSonsLen(n, 1, c.config) if n.sons[0].kind != nkEmpty: base = semTypeNode(c, n.sons[0].sons[0], nil) if base.kind != tyEnum: - localError(n.sons[0].info, errInheritanceOnlyWithEnums) + localError(c.config, n.sons[0].info, "inheritance only works with an enum") counter = lastOrd(base) + 1 rawAddSon(result, base) let isPure = result.sym != nil and sfPure in result.sym.flags @@ -58,9 +85,9 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCString}: x = getOrdValue(v.sons[0]) # first tuple part is the ordinal else: - localError(strVal.info, errStringLiteralExpected) + localError(c.config, strVal.info, errStringLiteralExpected) else: - localError(v.info, errWrongNumberOfVariables) + localError(c.config, v.info, errWrongNumberOfVariables) of tyString, tyCString: strVal = v x = counter @@ -69,7 +96,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = if i != 1: if x != counter: incl(result.flags, tfEnumHasHoles) if x < counter: - localError(n.sons[i].info, errInvalidOrderInEnumX, e.name.s) + localError(c.config, n.sons[i].info, errInvalidOrderInEnumX % e.name.s) x = counter e.ast = strVal # might be nil counter = x @@ -78,7 +105,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = of nkIdent, nkAccQuoted: e = newSymS(skEnumField, n.sons[i], c) else: - illFormedAst(n[i]) + illFormedAst(n[i], c.config) e.typ = result e.position = int(counter) if e.position == 0: hasNull = true @@ -87,12 +114,13 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = incl(e.flags, sfExported) if not isPure: strTableAdd(c.module.tab, e) addSon(result.n, newSymNode(e)) + let conf = c.config styleCheckDef(e) if sfGenSym notin e.flags: if not isPure: addDecl(c, e) else: importPureEnumField(c, e) if isPure and strTableIncl(symbols, e): - wrongRedefinition(e.info, e.name.s) + wrongRedefinition(c, e.info, e.name.s) inc(counter) if not hasNull: incl(result.flags, tfNeedsInit) @@ -101,14 +129,14 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType = if sonsLen(n) == 2: var base = semTypeNode(c, n.sons[1], nil) addSonSkipIntLit(result, base) - if base.kind in {tyGenericInst, tyAlias}: base = lastSon(base) + if base.kind in {tyGenericInst, tyAlias, tySink}: base = lastSon(base) if base.kind != tyGenericParam: if not isOrdinalType(base): - localError(n.info, errOrdinalTypeExpected) + localError(c.config, n.info, errOrdinalTypeExpected) elif lengthOrd(base) > MaxSetElements: - localError(n.info, errSetTooBig) + localError(c.config, n.info, errSetTooBig) else: - localError(n.info, errXExpectsOneTypeParam, "set") + localError(c.config, n.info, errXExpectsOneTypeParam % "set") addSonSkipIntLit(result, errorType(c)) proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string, @@ -117,10 +145,10 @@ proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string, if sonsLen(n) == 2: var base = semTypeNode(c, n.sons[1], nil) if base.kind == tyVoid: - localError(n.info, errTIsNotAConcreteType, typeToString(base)) + localError(c.config, n.info, errTIsNotAConcreteType % typeToString(base)) addSonSkipIntLit(result, base) else: - localError(n.info, errXExpectsOneTypeParam, kindStr) + localError(c.config, n.info, errXExpectsOneTypeParam % kindStr) addSonSkipIntLit(result, errorType(c)) proc semVarargs(c: PContext, n: PNode, prev: PType): PType = @@ -129,9 +157,9 @@ proc semVarargs(c: PContext, n: PNode, prev: PType): PType = var base = semTypeNode(c, n.sons[1], nil) addSonSkipIntLit(result, base) if sonsLen(n) == 3: - result.n = newIdentNode(considerQuotedIdent(n.sons[2]), n.sons[2].info) + result.n = newIdentNode(considerQuotedIdent(c.config, n.sons[2]), n.sons[2].info) else: - localError(n.info, errXExpectsOneTypeParam, "varargs") + localError(c.config, n.info, errXExpectsOneTypeParam % "varargs") addSonSkipIntLit(result, errorType(c)) proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = @@ -140,7 +168,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = else: let isCall = int ord(n.kind in nkCallKinds+{nkBracketExpr}) let n = if n[0].kind == nkBracket: n[0] else: n - checkMinSonsLen(n, 1) + checkMinSonsLen(n, 1, c.config) var t = semTypeNode(c, n.lastSon, nil) if t.kind == tyTypeDesc and tfUnresolved notin t.flags: t = t.base @@ -153,9 +181,9 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = isNilable = true else: let region = semTypeNode(c, ni, nil) - if region.skipTypes({tyGenericInst, tyAlias}).kind notin { + if region.skipTypes({tyGenericInst, tyAlias, tySink}).kind notin { tyError, tyObject}: - message n[i].info, errGenerated, "region needs to be an object type" + message c.config, n[i].info, errGenerated, "region needs to be an object type" addSonSkipIntLit(result, region) addSonSkipIntLit(result, t) if tfPartial in result.flags: @@ -167,7 +195,7 @@ proc semVarType(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tyVar, prev, c) var base = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc}) if base.kind == tyVar: - localError(n.info, errVarVarTypeNotAllowed) + localError(c.config, n.info, "type 'var var' is not allowed") base = base.sons[0] addSonSkipIntLit(result, base) else: @@ -181,11 +209,15 @@ proc semDistinct(c: PContext, n: PNode, prev: PType): PType = proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = assert isRange(n) - checkSonsLen(n, 3) + checkSonsLen(n, 3, c.config) result = newOrPrevType(tyRange, prev, c) result.n = newNodeI(nkRange, n.info) + # always create a 'valid' range type, but overwrite it later + # because 'semExprWithType' can raise an exception. See bug #6895. + addSonSkipIntLit(result, errorType(c)) + if (n[1].kind == nkEmpty) or (n[2].kind == nkEmpty): - localError(n.info, errRangeIsEmpty) + localError(c.config, n.info, "range is empty") var range: array[2, PNode] range[0] = semExprWithType(c, n[1], {efDetermineType}) @@ -200,11 +232,11 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = if not hasUnknownTypes: if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})): - localError(n.info, errPureTypeMismatch) + localError(c.config, n.info, "type mismatch") elif not rangeT[0].isOrdinalType: - localError(n.info, errOrdinalTypeExpected) + localError(c.config, n.info, "ordinal type expected") elif enumHasHoles(rangeT[0]): - localError(n.info, errEnumXHasHoles, rangeT[0].sym.name.s) + localError(c.config, n.info, "enum '$1' has holes" % typeToString(rangeT[0])) for i in 0..1: if hasGenericArguments(range[i]): @@ -214,9 +246,9 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = result.n.addSon semConstExpr(c, range[i]) if weakLeValue(result.n[0], result.n[1]) == impNo: - localError(n.info, errRangeIsEmpty) + localError(c.config, n.info, "range is empty") - addSonSkipIntLit(result, rangeT[0]) + result[0] = rangeT[0] proc semRange(c: PContext, n: PNode, prev: PType): PType = result = nil @@ -235,13 +267,13 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType = n.sons[1].floatVal < 0.0: incl(result.flags, tfNeedsInit) else: - if n[1].kind == nkInfix and considerQuotedIdent(n[1][0]).s == "..<": - localError(n[0].info, "range types need to be constructed with '..', '..<' is not supported") + if n[1].kind == nkInfix and considerQuotedIdent(c.config, n[1][0]).s == "..<": + localError(c.config, n[0].info, "range types need to be constructed with '..', '..<' is not supported") else: - localError(n.sons[0].info, errRangeExpected) + localError(c.config, n.sons[0].info, "expected range") result = newOrPrevType(tyError, prev, c) else: - localError(n.info, errXExpectsOneTypeParam, "range") + localError(c.config, n.info, errXExpectsOneTypeParam % "range") result = newOrPrevType(tyError, prev, c) proc semArrayIndex(c: PContext, n: PNode): PType = @@ -252,17 +284,21 @@ proc semArrayIndex(c: PContext, n: PNode): PType = if e.typ.kind == tyFromExpr: result = makeRangeWithStaticExpr(c, e.typ.n) elif e.kind in {nkIntLit..nkUInt64Lit}: + if e.intVal < 0: + localError(c.config, n[1].info, + "Array length can't be negative, but was " & $e.intVal) 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) + let info = if n.safeLen > 1: n[1].info else: n.info + localError(c.config, 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) + localError(c.config, 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 @@ -278,7 +314,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType = x.typ.skipTypes({tyTypeDesc})) else: result = x.typ.skipTypes({tyTypeDesc}) - #localError(n[1].info, errConstExprExpected) + #localError(c.config, n[1].info, errConstExprExpected) proc semArray(c: PContext, n: PNode, prev: PType): PType = var base: PType @@ -286,12 +322,14 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = # 3 = length(array indx base) let indx = semArrayIndex(c, n[1]) var indxB = indx - if indxB.kind in {tyGenericInst, tyAlias}: indxB = lastSon(indxB) + if indxB.kind in {tyGenericInst, tyAlias, tySink}: indxB = lastSon(indxB) if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr}: - if not isOrdinalType(indxB): - localError(n.sons[1].info, errOrdinalTypeExpected) + if indxB.skipTypes({tyRange}).kind in {tyUInt, tyUInt64}: + discard + elif not isOrdinalType(indxB): + localError(c.config, n.sons[1].info, errOrdinalTypeExpected) elif enumHasHoles(indxB): - localError(n.sons[1].info, errEnumXHasHoles, + localError(c.config, n.sons[1].info, "enum '$1' has holes" % typeToString(indxB.skipTypes({tyRange}))) base = semTypeNode(c, n.sons[2], nil) # ensure we only construct a tyArray when there was no error (bug #3048): @@ -301,7 +339,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = rawAddSonNoPropagationOfTypeFlags(result, indx) addSonSkipIntLit(result, base) else: - localError(n.info, errArrayExpectsTwoTypeParams) + localError(c.config, n.info, errArrayExpectsTwoTypeParams) result = newOrPrevType(tyError, prev, c) proc semOrdinal(c: PContext, n: PNode, prev: PType): PType = @@ -310,10 +348,10 @@ proc semOrdinal(c: PContext, n: PNode, prev: PType): PType = var base = semTypeNode(c, n.sons[1], nil) if base.kind != tyGenericParam: if not isOrdinalType(base): - localError(n.sons[1].info, errOrdinalTypeExpected) + localError(c.config, n.sons[1].info, errOrdinalTypeExpected) addSonSkipIntLit(result, base) else: - localError(n.info, errXExpectsOneTypeParam, "ordinal") + localError(c.config, n.info, errXExpectsOneTypeParam % "ordinal") result = newOrPrevType(tyError, prev, c) proc semTypeIdent(c: PContext, n: PNode): PSym = @@ -324,7 +362,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = if result.isNil: result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared}) if result != nil: - markUsed(n.info, result, c.graph.usageSym) + markUsed(c.config, n.info, result, c.graph.usageSym) styleCheckUse(n.info, result) if result.kind == skParam and result.typ.kind == tyTypeDesc: # This is a typedesc param. is it already bound? @@ -335,7 +373,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = if bound != nil: return bound return result if result.typ.sym == nil: - localError(n.info, errTypeExpected) + localError(c.config, n.info, errTypeExpected) return errorSym(c, n) result = result.typ.sym.copySym result.typ = copyType(result.typ, result.typ.owner, true) @@ -349,7 +387,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = result.typ.flags.excl tfWildcard return else: - localError(n.info, errTypeExpected) + localError(c.config, n.info, errTypeExpected) return errorSym(c, n) if result.kind != skType: @@ -360,7 +398,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = amb = nextOverloadIter(ov, c, n) if amb != nil: result = amb else: - if result.kind != skError: localError(n.info, errTypeExpected) + if result.kind != skError: localError(c.config, n.info, errTypeExpected) return errorSym(c, n) if result.typ.kind != tyGenericParam: # XXX get rid of this hack! @@ -375,15 +413,15 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = n.info = oldInfo n.typ = result.typ else: - localError(n.info, errIdentifierExpected) + localError(c.config, n.info, "identifier expected") result = errorSym(c, n) proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType = if sonsLen(n) == 0: - localError(n.info, errTypeExpected) + localError(c.config, n.info, errTypeExpected) result = newOrPrevType(tyTuple, prev, c) - for i in countup(0, sonsLen(n) - 1): - addSonSkipIntLit(result, semTypeNode(c, n.sons[i], nil)) + for it in n: + addSonSkipIntLit(result, semTypeNode(c, it, nil)) proc semTuple(c: PContext, n: PNode, prev: PType): PType = var typ: PType @@ -393,27 +431,27 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType = var counter = 0 for i in countup(ord(n.kind == nkBracketExpr), sonsLen(n) - 1): var a = n.sons[i] - if (a.kind != nkIdentDefs): illFormedAst(a) - checkMinSonsLen(a, 3) + if (a.kind != nkIdentDefs): illFormedAst(a, c.config) + checkMinSonsLen(a, 3, c.config) var length = sonsLen(a) if a.sons[length - 2].kind != nkEmpty: typ = semTypeNode(c, a.sons[length - 2], nil) else: - localError(a.info, errTypeExpected) + localError(c.config, a.info, errTypeExpected) typ = errorType(c) if a.sons[length - 1].kind != nkEmpty: - localError(a.sons[length - 1].info, errInitHereNotAllowed) + localError(c.config, a.sons[length - 1].info, errInitHereNotAllowed) for j in countup(0, length - 3): var field = newSymG(skField, a.sons[j], c) field.typ = typ field.position = counter inc(counter) if containsOrIncl(check, field.name.id): - localError(a.sons[j].info, errAttemptToRedefine, field.name.s) + localError(c.config, a.sons[j].info, "attempt to redefine: '" & field.name.s & "'") else: addSon(result.n, newSymNode(field)) addSonSkipIntLit(result, typ) - if gCmd == cmdPretty: styleCheckDef(a.sons[j].info, field) + if c.config.cmd == cmdPretty: styleCheckDef(a.sons[j].info, field) if result.n.len == 0: result.n = nil proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, @@ -424,23 +462,23 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, # for gensym'ed identifiers the identifier may already have been # transformed to a symbol and we need to use that here: result = newSymG(kind, n.sons[1], c) - var v = considerQuotedIdent(n.sons[0]) + var v = considerQuotedIdent(c.config, n.sons[0]) if sfExported in allowed and v.id == ord(wStar): incl(result.flags, sfExported) else: if not (sfExported in allowed): - localError(n.sons[0].info, errXOnlyAtModuleScope, "export") + localError(c.config, n.sons[0].info, errXOnlyAtModuleScope % "export") else: - localError(n.sons[0].info, errInvalidVisibilityX, renderTree(n[0])) + localError(c.config, n.sons[0].info, errInvalidVisibilityX % renderTree(n[0])) else: - illFormedAst(n) + illFormedAst(n, c.config) else: result = newSymG(kind, n, c) proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, allowed: TSymFlags): PSym = if n.kind == nkPragmaExpr: - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.config) result = semIdentVis(c, kind, n.sons[0], allowed) case kind of skType: @@ -453,7 +491,7 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, else: discard else: result = semIdentVis(c, kind, n, allowed) - if gCmd == cmdPretty: styleCheckDef(n.info, result) + if c.config.cmd == cmdPretty: styleCheckDef(n.info, result) proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) = let ex = t[branchIndex][currentEx].skipConv @@ -461,10 +499,10 @@ proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) = for j in countup(0, sonsLen(t.sons[i]) - 2): if i == branchIndex and j == currentEx: break if overlap(t.sons[i].sons[j].skipConv, ex): - localError(ex.info, errDuplicateCaseLabel) + localError(c.config, ex.info, errDuplicateCaseLabel) proc semBranchRange(c: PContext, t, a, b: PNode, covered: var BiggestInt): PNode = - checkMinSonsLen(t, 1) + checkMinSonsLen(t, 1, c.config) let ac = semConstExpr(c, a) let bc = semConstExpr(c, b) let at = fitNode(c, t.sons[0].typ, ac, ac.info).skipConvTakeType @@ -473,21 +511,21 @@ proc semBranchRange(c: PContext, t, a, b: PNode, covered: var BiggestInt): PNode result = newNodeI(nkRange, a.info) result.add(at) result.add(bt) - if emptyRange(ac, bc): localError(b.info, errRangeIsEmpty) + if emptyRange(ac, bc): localError(c.config, b.info, "range is empty") else: covered = covered + getOrdValue(bc) - getOrdValue(ac) + 1 proc semCaseBranchRange(c: PContext, t, b: PNode, covered: var BiggestInt): PNode = - checkSonsLen(b, 3) + checkSonsLen(b, 3, c.config) result = semBranchRange(c, t, b.sons[1], b.sons[2], covered) proc semCaseBranchSetElem(c: PContext, t, b: PNode, covered: var BiggestInt): PNode = if isRange(b): - checkSonsLen(b, 3) + checkSonsLen(b, 3, c.config) result = semBranchRange(c, t, b.sons[1], b.sons[2], covered) elif b.kind == nkRange: - checkSonsLen(b, 2) + checkSonsLen(b, 2, c.config) result = semBranchRange(c, t, b.sons[0], b.sons[1], covered) else: result = fitNode(c, t.sons[0].typ, b, b.info) @@ -495,8 +533,8 @@ proc semCaseBranchSetElem(c: PContext, t, b: PNode, proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int, covered: var BiggestInt) = - - for i in countup(0, sonsLen(branch) - 2): + let lastIndex = sonsLen(branch) - 2 + for i in 0..lastIndex: var b = branch.sons[i] if b.kind == nkRange: branch.sons[i] = b @@ -510,18 +548,25 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int, delSon(branch, 0) return elif r.kind notin {nkCurly, nkBracket} or len(r) == 0: - checkMinSonsLen(t, 1) + checkMinSonsLen(t, 1, c.config) branch.sons[i] = skipConv(fitNode(c, t.sons[0].typ, r, r.info)) inc(covered) else: + if r.kind == nkCurly: + r = r.deduplicate + # first element is special and will overwrite: branch.sons[i]: branch.sons[i] = semCaseBranchSetElem(c, t, r[0], covered) + # other elements have to be added to ``branch`` for j in 1 ..< r.len: branch.add(semCaseBranchSetElem(c, t, r[j], covered)) # caution! last son of branch must be the actions to execute: - var L = branch.len - swap(branch.sons[L-2], branch.sons[L-1]) + swap(branch.sons[^2], branch.sons[^1]) + checkForOverlap(c, t, i, branchIndex) + + # Elements added above needs to be checked for overlaps. + for i in lastIndex.succ..(sonsLen(branch) - 2): checkForOverlap(c, t, i, branchIndex) proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, @@ -529,37 +574,37 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int, father: PNode, rectype: PType) = var a = copyNode(n) - checkMinSonsLen(n, 2) + checkMinSonsLen(n, 2, c.config) semRecordNodeAux(c, n.sons[0], check, pos, a, rectype) if a.sons[0].kind != nkSym: - internalError("semRecordCase: discriminant is no symbol") + internalError(c.config, "semRecordCase: discriminant is no symbol") return incl(a.sons[0].sym.flags, sfDiscriminant) var covered: BiggestInt = 0 var typ = skipTypes(a.sons[0].typ, abstractVar-{tyTypeDesc}) if not isOrdinalType(typ): - localError(n.info, errSelectorMustBeOrdinal) + localError(c.config, n.info, "selector must be of an ordinal type") elif firstOrd(typ) != 0: - localError(n.info, errGenerated, "low(" & $a.sons[0].sym.name.s & + localError(c.config, n.info, "low(" & $a.sons[0].sym.name.s & ") must be 0 for discriminant") elif lengthOrd(typ) > 0x00007FFF: - localError(n.info, errLenXinvalid, a.sons[0].sym.name.s) + localError(c.config, n.info, "len($1) must be less than 32768" % a.sons[0].sym.name.s) var chckCovered = true for i in countup(1, sonsLen(n) - 1): var b = copyTree(n.sons[i]) addSon(a, b) case n.sons[i].kind of nkOfBranch: - checkMinSonsLen(b, 2) + checkMinSonsLen(b, 2, c.config) semCaseBranch(c, a, b, i, covered) of nkElse: chckCovered = false - checkSonsLen(b, 1) - else: illFormedAst(n) + checkSonsLen(b, 1, c.config) + else: illFormedAst(n, c.config) delSon(b, sonsLen(b) - 1) semRecordNodeAux(c, lastSon(n.sons[i]), check, pos, b, rectype) - if chckCovered and (covered != lengthOrd(a.sons[0].typ)): - localError(a.info, errNotAllCasesCovered) + if chckCovered and covered != lengthOrd(a.sons[0].typ): + localError(c.config, a.info, "not all cases are covered") addSon(father, a) proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, @@ -570,22 +615,22 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, var branch: PNode = nil # the branch to take for i in countup(0, sonsLen(n) - 1): var it = n.sons[i] - if it == nil: illFormedAst(n) + if it == nil: illFormedAst(n, c.config) var idx = 1 case it.kind of nkElifBranch: - checkSonsLen(it, 2) + checkSonsLen(it, 2, c.config) if c.inGenericContext == 0: var e = semConstBoolExpr(c, it.sons[0]) - if e.kind != nkIntLit: internalError(e.info, "semRecordNodeAux") + if e.kind != nkIntLit: internalError(c.config, e.info, "semRecordNodeAux") elif e.intVal != 0 and branch == nil: branch = it.sons[1] else: it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0])) of nkElse: - checkSonsLen(it, 1) + checkSonsLen(it, 1, c.config) if branch == nil: branch = it.sons[0] idx = 0 - else: illFormedAst(n) + else: illFormedAst(n, c.config) if c.inGenericContext > 0: # use a new check intset here for each branch: var newCheck: IntSet @@ -609,33 +654,35 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, semRecordNodeAux(c, n.sons[i], check, pos, a, rectype) if a != father: addSon(father, a) of nkIdentDefs: - checkMinSonsLen(n, 3) + checkMinSonsLen(n, 3, c.config) var length = sonsLen(n) var a: PNode if father.kind != nkRecList and length>=4: a = newNodeI(nkRecList, n.info) else: a = ast.emptyNode if n.sons[length-1].kind != nkEmpty: - localError(n.sons[length-1].info, errInitHereNotAllowed) + localError(c.config, n.sons[length-1].info, errInitHereNotAllowed) var typ: PType if n.sons[length-2].kind == nkEmpty: - localError(n.info, errTypeExpected) + localError(c.config, n.info, errTypeExpected) typ = errorType(c) else: typ = semTypeNode(c, n.sons[length-2], nil) propagateToOwner(rectype, typ) - let rec = rectype.sym + var fieldOwner = if c.inGenericContext > 0: c.getCurrOwner + else: rectype.sym for i in countup(0, sonsLen(n)-3): var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported}) - suggestSym(n.sons[i].info, f, c.graph.usageSym) + suggestSym(c.config, n.sons[i].info, f, c.graph.usageSym) f.typ = typ f.position = pos - if (rec != nil) and ({sfImportc, sfExportc} * rec.flags != {}) and - (f.loc.r == nil): + if fieldOwner != nil and + {sfImportc, sfExportc} * fieldOwner.flags != {} and + f.loc.r == nil: f.loc.r = rope(f.name.s) - f.flags = f.flags + ({sfImportc, sfExportc} * rec.flags) + f.flags = f.flags + ({sfImportc, sfExportc} * fieldOwner.flags) inc(pos) if containsOrIncl(check, f.name.id): - localError(n.sons[i].info, errAttemptToRedefine, f.name.s) + localError(c.config, n.sons[i].info, "attempt to redefine: '" & f.name.s & "'") if a.kind == nkEmpty: addSon(father, newSymNode(f)) else: addSon(a, newSymNode(f)) styleCheckDef(f) @@ -645,35 +692,35 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, # inherited from generic/partial specialized parent second check. # There is no branch validity check here if containsOrIncl(check, n.sym.name.id): - localError(n.info, errAttemptToRedefine, n.sym.name.s) + localError(c.config, n.info, "attempt to redefine: '" & n.sym.name.s & "'") addSon(father, n) of nkEmpty: discard - else: illFormedAst(n) + else: illFormedAst(n, c.config) proc addInheritedFieldsAux(c: PContext, check: var IntSet, pos: var int, n: PNode) = case n.kind of nkRecCase: - if (n.sons[0].kind != nkSym): internalError(n.info, "addInheritedFieldsAux") + if (n.sons[0].kind != nkSym): internalError(c.config, n.info, "addInheritedFieldsAux") addInheritedFieldsAux(c, check, pos, n.sons[0]) for i in countup(1, sonsLen(n) - 1): case n.sons[i].kind of nkOfBranch, nkElse: addInheritedFieldsAux(c, check, pos, lastSon(n.sons[i])) - else: internalError(n.info, "addInheritedFieldsAux(record case branch)") + else: internalError(c.config, n.info, "addInheritedFieldsAux(record case branch)") of nkRecList: for i in countup(0, sonsLen(n) - 1): addInheritedFieldsAux(c, check, pos, n.sons[i]) of nkSym: incl(check, n.sym.name.id) inc(pos) - else: internalError(n.info, "addInheritedFieldsAux()") + else: internalError(c.config, 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, tyRef, tyPtr, tyAlias}: + while result.kind in {tyGenericInst, tyGenericBody, tyRef, tyPtr, tyAlias, tySink}: result = lastSon(result) proc addInheritedFields(c: PContext, check: var IntSet, pos: var int, @@ -690,12 +737,12 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = var pos = 0 var base, realBase: PType = nil # n.sons[0] contains the pragmas (if any). We process these later... - checkSonsLen(n, 3) + checkSonsLen(n, 3, c.config) if n.sons[1].kind != nkEmpty: realBase = semTypeNode(c, n.sons[1].sons[0], nil) base = skipTypesOrNil(realBase, skipPtrs) if base.isNil: - localError(n.info, errIllegalRecursionInTypeX, "object") + localError(c.config, n.info, "cannot inherit from a type that is not an object type") else: var concreteBase = skipGenericInvocation(base) if concreteBase.kind in {tyObject, tyGenericParam, @@ -708,10 +755,11 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = addInheritedFields(c, check, pos, concreteBase) else: if concreteBase.kind != tyError: - localError(n.sons[1].info, errInheritanceOnlyWithNonFinalObjects) + localError(c.config, n.sons[1].info, "inheritance only works with non-final objects; " & + "to enable inheritance write '" & typeToString(realBase) & " of RootObj'") base = nil realBase = nil - if n.kind != nkObjectTy: internalError(n.info, "semObjectNode") + if n.kind != nkObjectTy: internalError(c.config, n.info, "semObjectNode") result = newOrPrevType(tyObject, prev, c) rawAddSon(result, realBase) if result.n.isNil: @@ -749,8 +797,7 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = addDecl(c, param) else: # within a macro, every param has the type NimNode! - let nn = if getCompilerProc("NimNode") != nil: getSysSym"NimNode" - else: getSysSym"PNimrodNode" + let nn = getSysSym(c.graph, param.info, "NimNode") var a = copySym(param) a.typ = nn.typ addDecl(c, a) @@ -760,7 +807,7 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = let typedescId = getIdent"typedesc" template shouldHaveMeta(t) = - internalAssert tfHasMeta in t.flags + internalAssert c.config, tfHasMeta in t.flags # result.lastSon.flags.incl tfHasMeta proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, @@ -818,7 +865,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, if tfUnresolved in paramType.flags: return # already lifted let base = paramType.base.maybeLift if base.isMetaType and procKind == skMacro: - localError(info, errMacroBodyDependsOnGenericTypes, paramName) + localError(c.config, info, errMacroBodyDependsOnGenericTypes % paramName) result = addImplicitGeneric(c.newTypeWithSons(tyStatic, @[base])) result.flags.incl({tfHasStatic, tfUnresolved}) @@ -836,7 +883,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result = liftingWalk(paramType.sons[0], true) of tySequence, tySet, tyArray, tyOpenArray, - tyVar, tyPtr, tyRef, tyProc: + tyVar, tyLent, tyPtr, tyRef, tyProc: # XXX: this is a bit strange, but proc(s: seq) # produces tySequence(tyGenericParam, tyNone). # This also seems to be true when creating aliases @@ -850,7 +897,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, else: for i in 0 ..< paramType.len: if paramType.sons[i] == paramType: - globalError(info, errIllegalRecursionInTypeX, typeToString(paramType)) + globalError(c.config, info, errIllegalRecursionInTypeX % typeToString(paramType)) var lifted = liftingWalk(paramType.sons[i]) if lifted != nil: paramType.sons[i] = lifted @@ -919,7 +966,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result = addImplicitGeneric(copyType(paramType, getCurrOwner(c), false)) of tyGenericParam: - markUsed(info, paramType.sym, c.graph.usageSym) + markUsed(c.config, info, paramType.sym, c.graph.usageSym) styleCheckUse(info, paramType.sym) if tfWildcard in paramType.flags: paramType.flags.excl tfWildcard @@ -932,7 +979,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType = if n.kind == nkCurlyExpr: result = semTypeNode(c, n.sons[0], nil) - constraint = semNodeKindConstraints(n) + constraint = semNodeKindConstraints(n, c.config) else: result = semTypeNode(c, n, nil) @@ -951,7 +998,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, # for historical reasons (code grows) this is invoked for parameter # lists too and then 'isType' is false. var cl: IntSet - checkMinSonsLen(n, 1) + checkMinSonsLen(n, 1, c.config) result = newProcType(c, n.info, prev) if genericParams != nil and sonsLen(genericParams) == 0: cl = initIntSet() @@ -965,8 +1012,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, # skip this parameter here. It'll then be re-generated in another LL # pass over this instantiation: if a.kind == nkSym and sfFromGeneric in a.sym.flags: continue - illFormedAst(a) - checkMinSonsLen(a, 3) + illFormedAst(a, c.config) + checkMinSonsLen(a, 3, c.config) var typ: PType = nil def: PNode = nil @@ -989,10 +1036,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, if not containsGenericType(typ): def = fitNode(c, typ, def, def.info) if not hasType and not hasDefault: - if isType: localError(a.info, "':' expected") + if isType: localError(c.config, a.info, "':' expected") if kind in {skTemplate, skMacro}: typ = newTypeS(tyExpr, c) - elif skipTypes(typ, {tyGenericInst, tyAlias}).kind == tyVoid: + elif skipTypes(typ, {tyGenericInst, tyAlias, tySink}).kind == tyVoid: continue for j in countup(0, length-3): var arg = newSymG(skParam, a.sons[j], c) @@ -1000,7 +1047,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, let param = strTableGet(c.signatures, arg.name) if param != nil: typ = param.typ else: - localError(a.info, "typeless parameters are obsolete") + localError(c.config, a.info, "typeless parameters are obsolete") typ = errorType(c) let lifted = liftParamType(c, kind, genericParams, typ, arg.name.s, arg.info) @@ -1011,11 +1058,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, inc(counter) if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def) if containsOrIncl(check, arg.name.id): - localError(a.sons[j].info, errAttemptToRedefine, arg.name.s) + localError(c.config, a.sons[j].info, "attempt to redefine: '" & arg.name.s & "'") addSon(result.n, newSymNode(arg)) rawAddSon(result, finalType) addParamOrResult(c, arg, kind) - if gCmd == cmdPretty: styleCheckDef(a.sons[j].info, arg) + if c.config.cmd == cmdPretty: styleCheckDef(a.sons[j].info, arg) var r: PType if n.sons[0].kind != nkEmpty: @@ -1024,7 +1071,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, if r != nil: # turn explicit 'void' return type into 'nil' because the rest of the # compiler only checks for 'nil': - if skipTypes(r, {tyGenericInst, tyAlias}).kind != tyVoid: + if skipTypes(r, {tyGenericInst, tyAlias, tySink}).kind != tyVoid: # 'auto' as a return type does not imply a generic: if r.kind == tyAnything: # 'p(): auto' and 'p(): expr' are equivalent, but the rest of the @@ -1065,7 +1112,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, n.sym.typ.flags.excl tfWildcard proc semStmtListType(c: PContext, n: PNode, prev: PType): PType = - checkMinSonsLen(n, 1) + checkMinSonsLen(n, 1, c.config) var length = sonsLen(n) for i in countup(0, length - 2): n.sons[i] = semStmt(c, n.sons[i]) @@ -1078,7 +1125,7 @@ proc semStmtListType(c: PContext, n: PNode, prev: PType): PType = proc semBlockType(c: PContext, n: PNode, prev: PType): PType = inc(c.p.nestedBlockCounter) - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.config) openScope(c) if n.sons[0].kind notin {nkEmpty, nkSym}: addDecl(c, newSymS(skLabel, n.sons[0], c)) @@ -1100,20 +1147,20 @@ proc semObjectTypeForInheritedGenericInst(c: PContext, n: PNode, t: PType) = realBase = t.sons[0] base = skipTypesOrNil(realBase, skipPtrs) if base.isNil: - localError(n.info, errIllegalRecursionInTypeX, "object") + localError(c.config, n.info, errIllegalRecursionInTypeX % "object") else: let concreteBase = skipGenericInvocation(base) if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags: addInheritedFields(c, check, pos, concreteBase) else: if concreteBase.kind != tyError: - localError(n.info, errInheritanceOnlyWithNonFinalObjects) + localError(c.config, n.info, errInheritanceOnlyWithNonFinalObjects) var newf = newNodeI(nkRecList, n.info) semRecordNodeAux(c, t.n, check, pos, newf, t) proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = if s.typ == nil: - localError(n.info, "cannot instantiate the '$1' $2" % + localError(c.config, n.info, "cannot instantiate the '$1' $2" % [s.name.s, ($s.kind).substr(2).toLowerAscii]) return newOrPrevType(tyError, prev, c) @@ -1126,7 +1173,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = template addToResult(typ) = if typ.isNil: - internalAssert false + internalAssert c.config, false rawAddSon(result, typ) else: addSonSkipIntLit(result, typ) @@ -1138,7 +1185,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = elif t.kind != tyGenericBody: # we likely got code of the form TypeA[TypeB] where TypeA is # not generic. - localError(n.info, errNoGenericParamsAllowedForX, s.name.s) + localError(c.config, n.info, errNoGenericParamsAllowedForX % s.name.s) return newOrPrevType(tyError, prev, c) else: var m = newCandidate(c, t) @@ -1147,9 +1194,9 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = if m.state != csMatch: let err = "cannot instantiate " & typeToString(t) & "\n" & - "got: (" & describeArgs(c, n) & ")\n" & - "but expected: (" & describeArgs(c, t.n, 0) & ")" - localError(n.info, errGenerated, err) + "got: <" & describeArgs(c, n) & ">\n" & + "but expected: <" & describeArgs(c, t.n, 0) & ">" + localError(c.config, n.info, errGenerated, err) return newOrPrevType(tyError, prev, c) var isConcrete = true @@ -1167,7 +1214,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = if isConcrete: if s.ast == nil and s.typ.kind != tyCompositeTypeClass: # XXX: What kind of error is this? is it still relevant? - localError(n.info, errCannotInstantiateX, s.name.s) + localError(c.config, n.info, errCannotInstantiateX % s.name.s) result = newOrPrevType(tyError, prev, c) else: result = instGenericContainer(c, n.info, result, @@ -1177,7 +1224,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = # generic/partial specialized parent let tx = result.skipTypes(abstractPtrs, 50) if tx.isNil: - localError(n.info, "invalid recursion in type '$1'" % typeToString(result[0])) + localError(c.config, n.info, "invalid recursion in type '$1'" % typeToString(result[0])) return errorType(c) if tx != result and tx.kind == tyObject and tx.sons[0] != nil: semObjectTypeForInheritedGenericInst(c, n, tx) @@ -1206,7 +1253,7 @@ proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType = let alias = maybeAliasType(c, result, prev) if alias != nil: result = alias else: - localError(n.info, errTypeExpected, n.renderTree) + localError(c.config, n.info, "expected type, but got: " & n.renderTree) result = errorType(c) proc freshType(res, prev: PType): PType {.inline.} = @@ -1257,10 +1304,11 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType = dummyName = param dummyType = candidateTypeSlot - internalAssert dummyName.kind == nkIdent + internalAssert c.config, dummyName.kind == nkIdent var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar, - dummyName.ident, owner, owner.info) + dummyName.ident, owner, param.info) dummyParam.typ = dummyType + incl dummyParam.flags, sfUsed addDecl(c, dummyParam) result.n.sons[3] = semConceptBody(c, n[3]) @@ -1268,7 +1316,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType = proc semProcTypeWithScope(c: PContext, n: PNode, prev: PType, kind: TSymKind): PType = - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.config) openScope(c) result = semProcTypeNode(c, n.sons[0], nil, prev, kind, isType=true) # start with 'ccClosure', but of course pragmas can overwrite this: @@ -1278,7 +1326,7 @@ proc semProcTypeWithScope(c: PContext, n: PNode, s.typ = result if n.sons[1].kind != nkEmpty and n.sons[1].len > 0: pragma(c, s, n.sons[1], procTypePragmas) - when useEffectSystem: setEffectsForProcType(result, n.sons[1]) + when useEffectSystem: setEffectsForProcType(c.graph, result, n.sons[1]) closeScope(c) proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType = @@ -1299,19 +1347,19 @@ proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym = if n.kind == nkType: result = symFromType(n.typ, n.info) else: - localError(n.info, errTypeExpected) + localError(c.config, n.info, errTypeExpected) result = errorSym(c, n) proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = nil inc c.inTypeContext - if gCmd == cmdIdeTools: suggestExpr(c, n) + if c.config.cmd == cmdIdeTools: suggestExpr(c, n) case n.kind of nkEmpty: discard of nkTypeOfExpr: # for ``type(countup(1,3))``, see ``tests/ttoseq``. - checkSonsLen(n, 1) + checkSonsLen(n, 1, c.config) let typExpr = semExprWithType(c, n.sons[0], {efInTypeof}) fixupTypeOf(c, prev, typExpr) result = typExpr.typ @@ -1320,6 +1368,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev) else: result = semAnonTuple(c, n, prev) + of nkTupleConstr: result = semAnonTuple(c, n, prev) of nkCallKinds: let x = n[0] let ident = case x.kind @@ -1335,26 +1384,26 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = semRangeAux(c, n, prev) elif n[0].kind == nkNilLit and n.len == 2: result = semTypeNode(c, n.sons[1], prev) - if result.skipTypes({tyGenericInst, tyAlias}).kind in NilableTypes+GenericTypes: + if result.skipTypes({tyGenericInst, tyAlias, tySink}).kind in NilableTypes+GenericTypes: if tfNotNil in result.flags: result = freshType(result, prev) result.flags.excl(tfNotNil) else: - localError(n.info, errGenerated, "invalid type") + localError(c.config, n.info, errGenerated, "invalid type") elif n[0].kind notin nkIdentKinds: result = semTypeExpr(c, n, prev) else: - let op = considerQuotedIdent(n.sons[0]) + let op = considerQuotedIdent(c.config, n.sons[0]) if op.id in {ord(wAnd), ord(wOr)} or op.s == "|": - checkSonsLen(n, 3) + checkSonsLen(n, 3, c.config) var t1 = semTypeNode(c, n.sons[1], nil) t2 = semTypeNode(c, n.sons[2], nil) if t1 == nil: - localError(n.sons[1].info, errTypeExpected) + localError(c.config, n.sons[1].info, errTypeExpected) result = newOrPrevType(tyError, prev, c) elif t2 == nil: - localError(n.sons[2].info, errTypeExpected) + localError(c.config, n.sons[2].info, errTypeExpected) result = newOrPrevType(tyError, prev, c) else: result = if op.id == ord(wAnd): makeAndType(c, t1, t2) @@ -1363,34 +1412,39 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = case n.len of 3: result = semTypeNode(c, n.sons[1], prev) - if result.skipTypes({tyGenericInst, tyAlias}).kind in NilableTypes+GenericTypes+{tyForward} and + if result.skipTypes({tyGenericInst, tyAlias, tySink}).kind in NilableTypes+GenericTypes+{tyForward} and n.sons[2].kind == nkNilLit: result = freshType(result, prev) result.flags.incl(tfNotNil) + if notnil notin c.features: + localError(c.config, n.info, "enable the 'not nil' annotation with {.experimental: \"notnil\".}") else: - localError(n.info, errGenerated, "invalid type") + localError(c.config, n.info, errGenerated, "invalid type") of 2: let negated = semTypeNode(c, n.sons[1], prev) result = makeNotType(c, negated) else: - localError(n.info, errGenerated, "invalid type") + localError(c.config, n.info, errGenerated, "invalid type") elif op.id == ord(wPtr): result = semAnyRef(c, n, tyPtr, prev) elif op.id == ord(wRef): result = semAnyRef(c, n, tyRef, prev) elif op.id == ord(wType): - checkSonsLen(n, 2) + checkSonsLen(n, 2, c.config) let typExpr = semExprWithType(c, n.sons[1], {efInTypeof}) fixupTypeOf(c, prev, typExpr) result = typExpr.typ else: - result = semTypeExpr(c, n, prev) + if c.inGenericContext > 0 and n.kind == nkCall: + result = makeTypeFromExpr(c, n.copyTree) + else: + result = semTypeExpr(c, n, prev) of nkWhenStmt: var whenResult = semWhen(c, n, false) if whenResult.kind == nkStmtList: whenResult.kind = nkStmtListType result = semTypeNode(c, whenResult, prev) of nkBracketExpr: - checkMinSonsLen(n, 2) + checkMinSonsLen(n, 2, c.config) var head = n.sons[0] var s = if head.kind notin nkCallKinds: semTypeIdent(c, head) else: symFromExpectedTypeNode(c, semExpr(c, head)) @@ -1416,8 +1470,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of mVar: result = newOrPrevType(tyVar, prev, c) var base = semTypeNode(c, n.sons[1], nil) - if base.kind == tyVar: - localError(n.info, errVarVarTypeNotAllowed) + if base.kind in {tyVar, tyLent}: + localError(c.config, n.info, "type 'var var' is not allowed") base = base.sons[0] addSonSkipIntLit(result, base) of mRef: result = semAnyRef(c, n, tyRef, prev) @@ -1427,13 +1481,13 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkDotExpr: let typeExpr = semExpr(c, n) if typeExpr.typ.isNil: - localError(n.info, "object constructor needs an object type;" & + localError(c.config, n.info, "object constructor needs an object type;" & " for named arguments use '=' instead of ':'") result = errorType(c) elif typeExpr.typ.kind == tyFromExpr: result = typeExpr.typ elif typeExpr.typ.kind != tyTypeDesc: - localError(n.info, errTypeExpected) + localError(c.config, n.info, errTypeExpected) result = errorType(c) else: result = typeExpr.typ.base @@ -1449,10 +1503,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkIdent, nkAccQuoted: var s = semTypeIdent(c, n) if s.typ == nil: - if s.kind != skError: localError(n.info, errTypeExpected) + if s.kind != skError: localError(c.config, n.info, errTypeExpected) result = newOrPrevType(tyError, prev, c) elif s.kind == skParam and s.typ.kind == tyTypeDesc: - internalAssert s.typ.base.kind != tyNone and prev == nil + internalAssert c.config, s.typ.base.kind != tyNone and prev == nil result = s.typ.base elif prev == nil: result = s.typ @@ -1479,10 +1533,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = else: assignType(prev, t) result = prev - markUsed(n.info, n.sym, c.graph.usageSym) + markUsed(c.config, n.info, n.sym, c.graph.usageSym) styleCheckUse(n.info, n.sym) else: - if s.kind != skError: localError(n.info, errTypeExpected) + if s.kind != skError: localError(c.config, n.info, errTypeExpected) result = newOrPrevType(tyError, prev, c) of nkObjectTy: result = semObjectNode(c, n, prev) of nkTupleTy: result = semTuple(c, n, prev) @@ -1520,7 +1574,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkStmtListType: result = semStmtListType(c, n, prev) of nkBlockType: result = semBlockType(c, n, prev) else: - localError(n.info, errTypeExpected) + localError(c.config, n.info, errTypeExpected) result = newOrPrevType(tyError, prev, c) n.typ = result dec c.inTypeContext @@ -1573,10 +1627,10 @@ proc processMagicType(c: PContext, m: PSym) = of mChar: setMagicType(m, tyChar, 1) of mString: setMagicType(m, tyString, ptrSize) - rawAddSon(m.typ, getSysType(tyChar)) + rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar)) of mCstring: setMagicType(m, tyCString, ptrSize) - rawAddSon(m.typ, getSysType(tyChar)) + rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar)) of mPointer: setMagicType(m, tyPointer, ptrSize) of mEmptySet: setMagicType(m, tySet, 1) @@ -1618,7 +1672,12 @@ proc processMagicType(c: PContext, m: PSym) = of mPNimrodNode: incl m.typ.flags, tfTriggersCompileTime of mException: discard - else: localError(m.info, errTypeExpected) + of mBuiltinType: + case m.name.s + of "lent": setMagicType(m, tyLent, ptrSize) + of "sink": setMagicType(m, tySink, 0) + else: localError(c.config, m.info, errTypeExpected) + else: localError(c.config, m.info, errTypeExpected) proc semGenericConstraints(c: PContext, x: PType): PType = result = newTypeWithSons(c, tyGenericParam, @[x]) @@ -1626,11 +1685,11 @@ proc semGenericConstraints(c: PContext, x: PType): PType = proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = result = copyNode(n) if n.kind != nkGenericParams: - illFormedAst(n) + illFormedAst(n, c.config) return for i in countup(0, sonsLen(n)-1): var a = n.sons[i] - if a.kind != nkIdentDefs: illFormedAst(n) + if a.kind != nkIdentDefs: illFormedAst(n, c.config) let L = a.len var def = a[^1] let constraint = a[^2] @@ -1676,7 +1735,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = if paramName.safeLen == 2: if not nimEnableCovariance or paramName[0].ident.s == "in": if father == nil or sfImportc notin father.sym.flags: - localError(paramName.info, errInOutFlagNotExtern, paramName[0].ident.s) + localError(c.config, paramName.info, errInOutFlagNotExtern % $paramName[0]) covarianceFlag = if paramName[0].ident.s == "in": tfContravariant else: tfCovariant if father != nil: father.flags.incl tfCovariant diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index a42092ae0..61d92bb19 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -14,21 +14,21 @@ import ast, astalgo, msgs, types, magicsys, semdata, renderer, options const tfInstClearedFlags = {tfHasMeta, tfUnresolved} -proc checkPartialConstructedType(info: TLineInfo, t: PType) = +proc checkPartialConstructedType(conf: ConfigRef; info: TLineInfo, t: PType) = if tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject: - localError(info, errInvalidPragmaX, "acyclic") - elif t.kind == tyVar and t.sons[0].kind == tyVar: - localError(info, errVarVarTypeNotAllowed) + localError(conf, info, "invalid pragma: acyclic") + elif t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}: + localError(conf, info, "type 'var var' is not allowed") -proc checkConstructedType*(info: TLineInfo, typ: PType) = +proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) = var t = typ.skipTypes({tyDistinct}) if t.kind in tyTypeClasses: discard elif tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject: - localError(info, errInvalidPragmaX, "acyclic") - elif t.kind == tyVar and t.sons[0].kind == tyVar: - localError(info, errVarVarTypeNotAllowed) + localError(conf, info, "invalid pragma: acyclic") + elif t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}: + localError(conf, info, "type 'var var' is not allowed") elif computeSize(t) == szIllegalRecursion: - localError(info, errIllegalRecursionInTypeX, typeToString(t)) + localError(conf, info, "illegal recursion in type '" & typeToString(t) & "'") when false: if t.kind == tyObject and t.sons[0] != nil: if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags: @@ -36,9 +36,8 @@ proc checkConstructedType*(info: TLineInfo, typ: PType) = proc searchInstTypes*(key: PType): PType = let genericTyp = key.sons[0] - internalAssert genericTyp.kind == tyGenericBody and - key.sons[0] == genericTyp and - genericTyp.sym != nil + if not (genericTyp.kind == tyGenericBody and + key.sons[0] == genericTyp and genericTyp.sym != nil): return if genericTyp.sym.typeInstCache == nil: return @@ -195,19 +194,19 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode = var branch: PNode = nil # the branch to take for i in countup(0, sonsLen(n) - 1): var it = n.sons[i] - if it == nil: illFormedAst(n) + if it == nil: illFormedAst(n, cl.c.config) case it.kind of nkElifBranch: - checkSonsLen(it, 2) + checkSonsLen(it, 2, cl.c.config) var cond = prepareNode(cl, it.sons[0]) var e = cl.c.semConstExpr(cl.c, cond) if e.kind != nkIntLit: - internalError(e.info, "ReplaceTypeVarsN: when condition not a bool") + internalError(cl.c.config, e.info, "ReplaceTypeVarsN: when condition not a bool") if e.intVal != 0 and branch == nil: branch = it.sons[1] of nkElse: - checkSonsLen(it, 1) + checkSonsLen(it, 1, cl.c.config) if branch == nil: branch = it.sons[0] - else: illFormedAst(n) + else: illFormedAst(n, cl.c.config) if branch != nil: result = replaceTypeVarsN(cl, branch) else: @@ -244,14 +243,14 @@ proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType = result = cl.typeMap.lookup(t) if result == nil: if cl.allowMetaTypes or tfRetType in t.flags: return - localError(t.sym.info, errCannotInstantiateX, typeToString(t)) + localError(cl.c.config, t.sym.info, "cannot instantiate: '" & typeToString(t) & "'") result = errorType(cl.c) # In order to prevent endless recursions, we must remember # this bad lookup and replace it with errorType everywhere. # These code paths are only active in "nim check" cl.typeMap.put(t, result) elif result.kind == tyGenericParam and not cl.allowMetaTypes: - internalError(cl.info, "substitution with generic parameter") + internalError(cl.c.config, cl.info, "substitution with generic parameter") proc instCopyType*(cl: var TReplTypeVars, t: PType): PType = # XXX: relying on allowMetaTypes is a kludge @@ -278,7 +277,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = # is difficult to handle: const eqFlags = eqTypeFlags + {tfGcSafe} var body = t.sons[0] - if body.kind != tyGenericBody: internalError(cl.info, "no generic body") + if body.kind != tyGenericBody: internalError(cl.c.config, cl.info, "no generic body") var header: PType = t # search for some instantiation here: if cl.allowMetaTypes: @@ -351,7 +350,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = # handleGenericInvocation will handle the alias-to-alias-to-alias case if newbody.isGenericAlias: newbody = newbody.skipGenericAlias rawAddSon(result, newbody) - checkPartialConstructedType(cl.info, newbody) + checkPartialConstructedType(cl.c.config, cl.info, newbody) let dc = newbody.deepCopy if cl.allowMetaTypes == false: if dc != nil and sfFromGeneric notin newbody.deepCopy.flags: @@ -368,7 +367,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = assert newbody.kind in {tyRef, tyPtr} assert newbody.lastSon.typeInst == nil newbody.lastSon.typeInst = result - if newDestructors: + if destructor in cl.c.features: cl.c.typesWithOps.add((newbody, result)) else: typeBound(cl.c, newbody, result, assignment, cl.info) @@ -417,7 +416,7 @@ proc propagateFieldFlags(t: PType, n: PNode) = # The type must be fully instantiated! if n.isNil: return - internalAssert n.kind != nkRecWhen + #internalAssert n.kind != nkRecWhen case n.kind of nkSym: propagateToOwner(t, n.sym.typ) @@ -454,12 +453,16 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result.kind = tyUserTypeClassInst of tyGenericBody: - localError(cl.info, errCannotInstantiateX, typeToString(t)) + localError(cl.c.config, cl.info, "cannot instantiate: '" & typeToString(t) & "'") result = errorType(cl.c) #result = replaceTypeVarsT(cl, lastSon(t)) of tyFromExpr: if cl.allowMetaTypes: return + # This assert is triggered when a tyFromExpr was created in a cyclic + # way. You should break the cycle at the point of creation by introducing + # a call such as: `n.typ = makeTypeFromExpr(c, n.copyTree)` + # Otherwise, the cycle will be fatal for the prepareNode call below assert t.n.typ != t var n = prepareNode(cl, t.n) if n.kind != nkEmpty: @@ -518,7 +521,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = var r = replaceTypeVarsT(cl, result.sons[i]) if result.kind == tyObject: # carefully coded to not skip the precious tyGenericInst: - let r2 = r.skipTypes({tyAlias}) + let r2 = r.skipTypes({tyAlias, tySink}) if r2.kind in {tyPtr, tyRef}: r = skipTypes(r2, {tyPtr, tyRef}) result.sons[i] = r @@ -529,7 +532,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = case result.kind of tyArray: let idx = result.sons[0] - internalAssert idx.kind != tyStatic + internalAssert cl.c.config, idx.kind != tyStatic of tyObject, tyTuple: propagateFieldFlags(result, result.n) @@ -541,7 +544,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = else: discard proc instAllTypeBoundOp*(c: PContext, info: TLineInfo) = - if not newDestructors: return + if destructor notin c.features: return var i = 0 while i < c.typesWithOps.len: let (newty, oldty) = c.typesWithOps[i] diff --git a/compiler/service.nim b/compiler/service.nim index ac04b7860..f1a988ae5 100644 --- a/compiler/service.nim +++ b/compiler/service.nim @@ -11,7 +11,7 @@ import times, commands, options, msgs, nimconf, - extccomp, strutils, os, platform, parseopt, idents + extccomp, strutils, os, platform, parseopt, idents, configuration when useCaas: import net @@ -26,7 +26,7 @@ var # in caas mode, the list of defines and options will be given at start-up? # it's enough to check that the previous compilation command is the same? -proc processCmdLine*(pass: TCmdLinePass, cmd: string) = +proc processCmdLine*(pass: TCmdLinePass, cmd: string; config: ConfigRef) = var p = parseopt.initOptParser(cmd) var argsCount = 0 while true: @@ -36,23 +36,23 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) = of cmdLongoption, cmdShortOption: if p.key == " ": p.key = "-" - if processArgument(pass, p, argsCount): break + if processArgument(pass, p, argsCount, config): break else: - processSwitch(pass, p) + processSwitch(pass, p, config) of cmdArgument: - if processArgument(pass, p, argsCount): break + if processArgument(pass, p, argsCount, config): break if pass == passCmd2: - if optRun notin gGlobalOptions and arguments != "" and options.command.normalize != "run": - rawMessage(errArgsNeedRunOption, []) + if optRun notin config.globalOptions and config.arguments.len > 0 and config.command.normalize != "run": + rawMessage(config, errGenerated, errArgsNeedRunOption) -proc serve*(cache: IdentCache; action: proc (cache: IdentCache){.nimcall.}) = +proc serve*(cache: IdentCache; action: proc (cache: IdentCache){.nimcall.}; config: ConfigRef) = template execute(cmd) = curCaasCmd = cmd - processCmdLine(passCmd2, cmd) + processCmdLine(passCmd2, cmd, config) action(cache) - gErrorCounter = 0 + config.errorCounter = 0 - let typ = getConfigVar("server.type") + let typ = getConfigVar(config, "server.type") case typ of "stdin": while true: @@ -65,9 +65,9 @@ proc serve*(cache: IdentCache; action: proc (cache: IdentCache){.nimcall.}) = of "tcp", "": when useCaas: var server = newSocket() - let p = getConfigVar("server.port") + let p = getConfigVar(config, "server.port") let port = if p.len > 0: parseInt(p).Port else: 6000.Port - server.bindAddr(port, getConfigVar("server.address")) + server.bindAddr(port, getConfigVar(config, "server.address")) var inp = "".TaintedString server.listen() var stdoutSocket = newSocket() diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 5d6b5978d..46b83c386 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -9,7 +9,7 @@ ## Computes hash values for routine (proc, method etc) signatures. -import ast, md5 +import ast, md5, tables, ropes from hashes import Hash from astalgo import debug from types import typeToString, preferDesc @@ -155,7 +155,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = else: c.hashSym(t.sym) return - of tyAlias, tyGenericInst, tyUserTypeClasses: + of tyAlias, tySink, tyGenericInst, tyUserTypeClasses: c.hashType t.lastSon, flags return else: @@ -326,3 +326,32 @@ proc hashOwner*(s: PSym): SigHash = c &= m.name.s md5Final c, result.Md5Digest + +proc idOrSig*(s: PSym, currentModule: string, + sigCollisions: var CountTable[SigHash]): Rope = + if s.kind in routineKinds and s.typ != nil: + # signatures for exported routines are reliable enough to + # produce a unique name and this means produced C++ is more stable wrt + # Nim changes: + let sig = hashProc(s) + result = rope($sig) + #let m = if s.typ.callConv != ccInline: findPendingModule(m, s) else: m + let counter = sigCollisions.getOrDefault(sig) + #if sigs == "_jckmNePK3i2MFnWwZlp6Lg" and s.name.s == "contains": + # echo "counter ", counter, " ", s.id + if counter != 0: + result.add "_" & rope(counter+1) + # this minor hack is necessary to make tests/collections/thashes compile. + # The inlined hash function's original module is ambiguous so we end up + # generating duplicate names otherwise: + if s.typ.callConv == ccInline: + result.add rope(currentModule) + sigCollisions.inc(sig) + else: + let sig = hashNonProc(s) + result = rope($sig) + let counter = sigCollisions.getOrDefault(sig) + if counter != 0: + result.add "_" & rope(counter+1) + sigCollisions.inc(sig) + diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 3d0b0ed3d..41cac2a4a 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -24,8 +24,9 @@ type CandidateError* = object sym*: PSym - unmatchedVarParam*: int + unmatchedVarParam*, firstMismatch*: int diagnostics*: seq[string] + enabled*: bool CandidateErrors* = seq[CandidateError] @@ -60,14 +61,18 @@ type # matching. they will be reset if the matching # is not successful. may replace the bindings # table in the future. - diagnostics*: seq[string] # when this is not nil, the matching process + diagnostics*: seq[string] # \ + # when diagnosticsEnabled, the matching process # will collect extra diagnostics that will be # displayed to the user. # triggered when overload resolution fails # or when the explain pragma is used. may be # triggered with an idetools command in the # future. - inheritancePenalty: int # to prefer closest father object type + inheritancePenalty: int # to prefer closest father object type + firstMismatch*: int # position of the first type mismatch for + # better error messages + diagnosticsEnabled*: bool TTypeRelFlag* = enum trDontBind @@ -94,7 +99,7 @@ type const isNilConversion = isConvertible # maybe 'isIntConv' fits better? -proc markUsed*(info: TLineInfo, s: PSym; usageSym: var PSym) +proc markUsed*(conf: ConfigRef; info: TLineInfo, s: PSym; usageSym: var PSym) template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone @@ -122,7 +127,8 @@ proc put(c: var TCandidate, key, val: PType) {.inline.} = idTablePut(c.bindings, key, val.skipIntLit) proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, - binding: PNode, calleeScope = -1, diagnostics = false) = + binding: PNode, calleeScope = -1, + diagnosticsEnabled = false) = initCandidateAux(ctx, c, callee.typ) c.calleeSym = callee if callee.kind in skProcKinds and calleeScope == -1: @@ -137,7 +143,8 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, c.calleeScope = 1 else: c.calleeScope = calleeScope - c.diagnostics = if diagnostics: @[] else: nil + c.diagnostics = if diagnosticsEnabled: @[] else: nil + c.diagnosticsEnabled = diagnosticsEnabled c.magic = c.calleeSym.magic initIdTable(c.bindings) if binding != nil and callee.kind in routineKinds: @@ -145,13 +152,13 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1): var formalTypeParam = typeParams.sons[i-1].typ var bound = binding[i].typ - internalAssert bound != nil - if formalTypeParam.kind == tyTypeDesc: - if bound.kind != tyTypeDesc: - bound = makeTypeDesc(ctx, bound) - else: - bound = bound.skipTypes({tyTypeDesc}) - put(c, formalTypeParam, bound) + if bound != nil: + if formalTypeParam.kind == tyTypeDesc: + if bound.kind != tyTypeDesc: + bound = makeTypeDesc(ctx, bound) + else: + bound = bound.skipTypes({tyTypeDesc}) + put(c, formalTypeParam, bound) proc newCandidate*(ctx: PContext, callee: PSym, binding: PNode, calleeScope = -1): TCandidate = @@ -180,7 +187,8 @@ proc sumGeneric(t: PType): int = while true: case t.kind of tyGenericInst, tyArray, tyRef, tyPtr, tyDistinct, - tyOpenArray, tyVarargs, tySet, tyRange, tySequence, tyGenericBody: + tyOpenArray, tyVarargs, tySet, tyRange, tySequence, tyGenericBody, + tyLent: t = t.lastSon inc result of tyOr: @@ -207,7 +215,7 @@ proc sumGeneric(t: PType): int = of tyStatic: return t.sons[0].sumGeneric + 1 of tyGenericParam, tyExpr, tyStmt: break - of tyAlias: t = t.lastSon + of tyAlias, tySink: t = t.lastSon of tyBool, tyChar, tyEnum, tyObject, tyPointer, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128, tyUInt..tyUInt64, tyCompositeTypeClass: @@ -354,8 +362,8 @@ proc concreteType(c: TCandidate, t: PType): PType = # proc sort[T](cmp: proc(a, b: T): int = cmp) if result.kind != tyGenericParam: break of tyGenericInvocation: - internalError("cannot resolve type: " & typeToString(t)) result = t + doAssert(false, "cannot resolve type: " & typeToString(t)) else: result = t # Note: empty is valid here @@ -464,7 +472,7 @@ proc skipToObject(t: PType; skipped: var SkippedPtr): PType = inc ptrs skipped = skippedPtr r = r.lastSon - of tyGenericBody, tyGenericInst, tyAlias: + of tyGenericBody, tyGenericInst, tyAlias, tySink: r = r.lastSon else: break @@ -511,8 +519,8 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation = if f.n != nil and a.n != nil: for i in countup(0, sonsLen(f.n) - 1): # check field names: - if f.n.sons[i].kind != nkSym: internalError(f.n.info, "recordRel") - elif a.n.sons[i].kind != nkSym: internalError(a.n.info, "recordRel") + if f.n.sons[i].kind != nkSym: return isNone + elif a.n.sons[i].kind != nkSym: return isNone else: var x = f.n.sons[i].sym var y = a.n.sons[i].sym @@ -524,7 +532,7 @@ proc allowsNil(f: PType): TTypeRelation {.inline.} = result = if tfNotNil notin f.flags: isSubtype else: isNone proc inconsistentVarTypes(f, a: PType): bool {.inline.} = - result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar) + result = f.kind != a.kind and (f.kind in {tyVar, tyLent} or a.kind in {tyVar, tyLent}) proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = ## For example we have: @@ -604,7 +612,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags: return isNone elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {} and - optThreadAnalysis in gGlobalOptions: + optThreadAnalysis in c.c.config.globalOptions: # noSideEffect implies ``tfThread``! return isNone elif f.flags * {tfIterator} != a.flags * {tfIterator}: @@ -653,7 +661,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = matchedConceptContext.prev = prevMatchedConcept matchedConceptContext.depth = prevMatchedConcept.depth + 1 if prevMatchedConcept.depth > 4: - localError(body.info, $body & " too nested for type matching") + localError(m.c.graph.config, body.info, $body & " too nested for type matching") return nil openScope(c) @@ -678,7 +686,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = if alreadyBound != nil: typ = alreadyBound template paramSym(kind): untyped = - newSym(kind, typeParamName, typeClass.sym, typeClass.sym.info) + newSym(kind, typeParamName, typeClass.sym, typeClass.sym.info, {}) block addTypeParam: for prev in typeParams: @@ -714,7 +722,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = diagnostics: seq[string] errorPrefix: string flags: TExprFlags = {} - collectDiagnostics = m.diagnostics != nil or + collectDiagnostics = m.diagnosticsEnabled or sfExplain in typeClass.sym.flags if collectDiagnostics: @@ -733,7 +741,9 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = if collectDiagnostics: writelnHook = oldWriteHook - for msg in diagnostics: m.diagnostics.safeAdd msg + for msg in diagnostics: + m.diagnostics.safeAdd msg + m.diagnosticsEnabled = true if checkedBody == nil: return nil @@ -750,19 +760,20 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = result.n = checkedBody -proc shouldSkipDistinct(rules: PNode, callIdent: PIdent): bool = +proc shouldSkipDistinct(m: TCandidate; rules: PNode, callIdent: PIdent): bool = + # XXX This is bad as 'considerQuotedIdent' can produce an error! if rules.kind == nkWith: for r in rules: - if r.considerQuotedIdent == callIdent: return true + if considerQuotedIdent(m.c.graph.config, r) == callIdent: return true return false else: for r in rules: - if r.considerQuotedIdent == callIdent: return false + if considerQuotedIdent(m.c.graph.config, r) == callIdent: return false return true -proc maybeSkipDistinct(t: PType, callee: PSym): PType = +proc maybeSkipDistinct(m: TCandidate; t: PType, callee: PSym): PType = if t != nil and t.kind == tyDistinct and t.n != nil and - shouldSkipDistinct(t.n, callee.name): + shouldSkipDistinct(m, t.n, callee.name): result = t.base else: result = t @@ -858,11 +869,11 @@ proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool = return false -proc failureToInferStaticParam(n: PNode) = +proc failureToInferStaticParam(conf: ConfigRef; n: PNode) = let staticParam = n.findUnresolvedStatic let name = if staticParam != nil: staticParam.sym.name.s else: "unknown" - localError(n.info, errCannotInferStaticParam, name) + localError(conf, n.info, "cannot infer the value of the static param '" & name & "'") proc inferStaticsInRange(c: var TCandidate, inferred, concrete: PType): TTypeRelation = @@ -876,7 +887,7 @@ proc inferStaticsInRange(c: var TCandidate, if inferStaticParam(c, exp, rhs): return isGeneric else: - failureToInferStaticParam exp + failureToInferStaticParam(c.c.graph.config, exp) if lowerBound.kind == nkIntLit: if upperBound.kind == nkIntLit: @@ -889,7 +900,7 @@ proc inferStaticsInRange(c: var TCandidate, doInferStatic(lowerBound, upperBound.intVal + 1 - lengthOrd(concrete)) template subtypeCheck() = - if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyVar}: + if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyVar, tyLent}: result = isNone proc isCovariantPtr(c: var TCandidate, f, a: PType): bool = @@ -897,7 +908,7 @@ proc isCovariantPtr(c: var TCandidate, f, a: PType): bool = assert f.kind == a.kind template baseTypesCheck(lhs, rhs: PType): bool = - lhs.kind notin {tyPtr, tyRef, tyVar} and + lhs.kind notin {tyPtr, tyRef, tyVar, tyLent} and typeRel(c, lhs, rhs, {trNoCovariance}) == isSubtype case f.kind @@ -912,6 +923,26 @@ proc isCovariantPtr(c: var TCandidate, f, a: PType): bool = else: return false +when false: + proc maxNumericType(prev, candidate: PType): PType = + let c = candidate.skipTypes({tyRange}) + template greater(s) = + if c.kind in s: result = c + case prev.kind + of tyInt: greater({tyInt64}) + of tyInt8: greater({tyInt, tyInt16, tyInt32, tyInt64}) + of tyInt16: greater({tyInt, tyInt32, tyInt64}) + of tyInt32: greater({tyInt64}) + + of tyUInt: greater({tyUInt64}) + of tyUInt8: greater({tyUInt, tyUInt16, tyUInt32, tyUInt64}) + of tyUInt16: greater({tyUInt, tyUInt32, tyUInt64}) + of tyUInt32: greater({tyUInt64}) + + of tyFloat32: greater({tyFloat64, tyFloat128}) + of tyFloat64: greater({tyFloat128}) + else: discard + proc typeRelImpl(c: var TCandidate, f, aOrig: PType, flags: TTypeRelFlags = {}): TTypeRelation = # typeRel can be used to establish various relationships between types: @@ -969,7 +1000,8 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, of tyStatic: candidate = computedType else: - localError(f.n.info, errTypeExpected) + # XXX What is this non-sense? Error reporting in signature matching? + discard "localError(f.n.info, errTypeExpected)" else: discard @@ -983,17 +1015,17 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, template doBind: bool = trDontBind notin flags # var and static arguments match regular modifier-free types - var a = aOrig.skipTypes({tyStatic, tyVar}).maybeSkipDistinct(c.calleeSym) + var a = maybeSkipDistinct(c, aOrig.skipTypes({tyStatic, tyVar, tyLent}), c.calleeSym) # XXX: Theoretically, maybeSkipDistinct could be called before we even # start the param matching process. This could be done in `prepareOperand` # for example, but unfortunately `prepareOperand` is not called in certain # situation when nkDotExpr are rotated to nkDotCalls - if aOrig.kind == tyAlias: + if aOrig.kind in {tyAlias, tySink}: return typeRel(c, f, lastSon(aOrig)) if a.kind == tyGenericInst and - skipTypes(f, {tyVar}).kind notin { + skipTypes(f, {tyVar, tyLent}).kind notin { tyGenericBody, tyGenericInvocation, tyGenericInst, tyGenericParam} + tyTypeClasses: return typeRel(c, f, lastSon(a)) @@ -1105,8 +1137,8 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, of tyFloat32: result = handleFloatRange(f, a) of tyFloat64: result = handleFloatRange(f, a) of tyFloat128: result = handleFloatRange(f, a) - of tyVar: - if aOrig.kind == tyVar: result = typeRel(c, f.base, aOrig.base) + of tyVar, tyLent: + if aOrig.kind == f.kind: result = typeRel(c, f.base, aOrig.base) else: result = typeRel(c, f.base, aOrig, flags + {trNoCovariance}) subtypeCheck() of tyArray: @@ -1122,8 +1154,14 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, else: fRange = prev let ff = f.sons[1].skipTypes({tyTypeDesc}) - let aa = a.sons[1].skipTypes({tyTypeDesc}) - result = typeRel(c, ff, aa) + # This typeDesc rule is wrong, see bug #7331 + let aa = a.sons[1] #.skipTypes({tyTypeDesc}) + + if f.sons[0].kind != tyGenericParam and aa.kind == tyEmpty: + result = isGeneric + else: + result = typeRel(c, ff, aa) + if result < isGeneric: if nimEnableCovariance and trNoCovariance notin flags and @@ -1214,7 +1252,9 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, if result < isGeneric: result = isNone elif a.kind == tyGenericParam: result = isGeneric - of tyForward: internalError("forward type in typeRel()") + of tyForward: + #internalError("forward type in typeRel()") + result = isNone of tyNil: if a.kind == f.kind: result = isEqual of tyTuple: @@ -1311,7 +1351,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, of tyEmpty, tyVoid: if a.kind == f.kind: result = isEqual - of tyAlias: + of tyAlias, tySink: result = typeRel(c, lastSon(f), a) of tyGenericInst: @@ -1357,8 +1397,13 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, var aAsObject = roota.lastSon - if fKind in {tyRef, tyPtr} and aAsObject.kind == fKind: - aAsObject = aAsObject.base + if fKind in {tyRef, tyPtr}: + if aAsObject.kind == tyObject: + # bug #7600, tyObject cannot be passed + # as argument to tyRef/tyPtr + return isNone + elif aAsObject.kind == fKind: + aAsObject = aAsObject.base if aAsObject.kind == tyObject: let baseType = aAsObject.base @@ -1399,7 +1444,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, (sonsLen(x) - 1 == sonsLen(f)): for i in countup(1, sonsLen(f) - 1): if x.sons[i].kind == tyGenericParam: - internalError("wrong instantiated type!") + internalError(c.c.graph.config, "wrong instantiated type!") elif typeRel(c, f.sons[i], x.sons[i]) <= isSubtype: # Workaround for regression #4589 if f.sons[i].kind != tyTypeDesc: return @@ -1431,7 +1476,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, if x == nil: discard "maybe fine (for eg. a==tyNil)" elif x.kind in {tyGenericInvocation, tyGenericParam}: - internalError("wrong instantiated type!") + internalError(c.c.graph.config, "wrong instantiated type!") else: put(c, f.sons[i], x) @@ -1497,7 +1542,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, considerPreviousT: let targetKind = f.sons[0].kind let effectiveArgType = a.skipTypes({tyRange, tyGenericInst, - tyBuiltInTypeClass, tyAlias}) + tyBuiltInTypeClass, tyAlias, tySink}) let typeClassMatches = targetKind == effectiveArgType.kind and not effectiveArgType.isEmptyContainer if typeClassMatches or @@ -1554,7 +1599,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, if f.sonsLen == 0: result = isGeneric else: - internalAssert a.sons != nil and a.sons.len > 0 + internalAssert c.c.graph.config, a.sons != nil and a.sons.len > 0 c.typedescMatched = true var aa = a while aa.kind in {tyTypeDesc, tyGenericParam} and aa.len > 0: @@ -1601,9 +1646,18 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, elif x.kind == tyGenericParam: result = isGeneric else: + # Special type binding rule for numeric types. + # See section "Generic type inference for numeric types" of the + # manual for further details: + when false: + let rebinding = maxNumericType(x.skipTypes({tyRange}), a) + if rebinding != nil: + put(c, f, rebinding) + result = isGeneric + else: + discard result = typeRel(c, x, a) # check if it fits if result > isGeneric: result = isGeneric - of tyStatic: let prev = PType(idTableGet(c.bindings, f)) if prev == nil: @@ -1687,13 +1741,13 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n): result = isNone else: - localError(f.n.info, errTypeExpected) + localError(c.c.graph.config, f.n.info, "type expected") result = isNone of tyNone: if a.kind == tyNone: result = isEqual else: - internalError " unknown type kind " & $f.kind + internalError c.c.graph.config, " unknown type kind " & $f.kind proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation = var m: TCandidate @@ -1706,7 +1760,7 @@ proc getInstantiatedType(c: PContext, arg: PNode, m: TCandidate, if result == nil: result = generateTypeInstance(c, m.bindings, arg, f) if result == nil: - internalError(arg.info, "getInstantiatedType") + internalError(c.graph.config, arg.info, "getInstantiatedType") result = errorType(c) proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate, @@ -1719,7 +1773,7 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate, result.typ = errorType(c) else: result.typ = f - if result.typ == nil: internalError(arg.info, "implicitConv") + if result.typ == nil: internalError(c.graph.config, arg.info, "implicitConv") addSon(result, ast.emptyNode) addSon(result, arg) @@ -1740,7 +1794,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, dest = generateTypeInstance(c, m.bindings, arg, dest) let fdest = typeRel(m, f, dest) if fdest in {isEqual, isGeneric}: - markUsed(arg.info, c.converters[i], c.graph.usageSym) + markUsed(c.config, arg.info, c.converters[i], c.graph.usageSym) var s = newSymNode(c.converters[i]) s.typ = c.converters[i].typ s.info = arg.info @@ -1832,8 +1886,11 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, var r = typeRel(m, f, a) + # This special typing rule for macros and templates is not documented + # anywhere and breaks symmetry. It's hard to get rid of though, my + # custom seqs example fails to compile without this: 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, but we cannot (!) move this # directly into typeRel using return-like templates incMatches(m, r) @@ -1841,7 +1898,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, return arg elif f.kind == tyTypeDesc: return arg - elif f.kind == tyStatic: + elif f.kind == tyStatic and arg.typ.n != nil: return arg.typ.n else: return argSemantized # argOrig @@ -1907,7 +1964,8 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, inc(m.genericMatches) if arg.typ == nil: result = arg - elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple: + elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or + m.inheritancePenalty > 0: result = implicitConv(nkHiddenSubConv, f, arg, m, c) elif arg.typ.isEmptyContainer: result = arg.copyTree @@ -1977,8 +2035,9 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, y.calleeSym = m.calleeSym z.calleeSym = m.calleeSym var best = -1 - for i in countup(0, sonsLen(arg) - 1): - if arg.sons[i].sym.kind in {skProc, skFunc, skMethod, skConverter, skIterator}: + for i in 0 ..< arg.len: + if arg.sons[i].sym.kind in {skProc, skFunc, skMethod, skConverter, + skIterator, skMacro, skTemplate}: copyCandidate(z, m) z.callee = arg.sons[i].typ if tfUnresolved in z.callee.flags: continue @@ -2007,24 +2066,24 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, x = z elif cmp == 0: y = z # z is as good as x + if x.state == csEmpty: result = nil elif y.state == csMatch and cmpCandidates(x, y) == 0: if x.state != csMatch: - internalError(arg.info, "x.state is not csMatch") + internalError(m.c.graph.config, arg.info, "x.state is not csMatch") # ambiguous: more than one symbol fits! # See tsymchoice_for_expr as an example. 'f.kind == tyExpr' should match # anyway: - if f.kind == tyExpr: result = arg + if f.kind in {tyExpr, tyStmt}: result = arg else: result = nil else: # only one valid interpretation found: - markUsed(arg.info, arg.sons[best].sym, m.c.graph.usageSym) + markUsed(m.c.config, arg.info, arg.sons[best].sym, m.c.graph.usageSym) styleCheckUse(arg.info, arg.sons[best].sym) result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best], argOrig) - proc setSon(father: PNode, at: int, son: PNode) = let oldLen = father.len if oldLen <= at: @@ -2060,15 +2119,16 @@ proc prepareOperand(c: PContext; a: PNode): PNode = result = a considerGenSyms(c, result) -proc prepareNamedParam(a: PNode) = +proc prepareNamedParam(a: PNode; conf: ConfigRef) = if a.sons[0].kind != nkIdent: var info = a.sons[0].info - a.sons[0] = newIdentNode(considerQuotedIdent(a.sons[0]), info) + a.sons[0] = newIdentNode(considerQuotedIdent(conf, a.sons[0]), info) proc arrayConstr(c: PContext, n: PNode): PType = result = newTypeS(tyArray, c) rawAddSon(result, makeRangeType(c, 0, 0, n.info)) - addSonSkipIntLit(result, skipTypes(n.typ, {tyGenericInst, tyVar, tyOrdinal})) + addSonSkipIntLit(result, skipTypes(n.typ, + {tyGenericInst, tyVar, tyLent, tyOrdinal})) proc arrayConstr(c: PContext, info: TLineInfo): PType = result = newTypeS(tyArray, c) @@ -2115,7 +2175,8 @@ proc matchesAux(c: PContext, n, nOrig: PNode, var formal: PSym = if formalLen > 1: m.callee.n.sons[1].sym else: nil while a < n.len: - if a >= formalLen-1 and formal != nil and formal.typ.isVarargsUntyped: + if a >= formalLen-1 and f < formalLen and m.callee.n[f].typ.isVarargsUntyped: + formal = m.callee.n.sons[f].sym incl(marker, formal.position) if container.isNil: container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info)) @@ -2126,9 +2187,9 @@ proc matchesAux(c: PContext, n, nOrig: PNode, elif n.sons[a].kind == nkExprEqExpr: # named param # check if m.callee has such a param: - prepareNamedParam(n.sons[a]) + prepareNamedParam(n.sons[a], c.config) if n.sons[a].sons[0].kind != nkIdent: - localError(n.sons[a].info, errNamedParamHasToBeIdent) + localError(c.config, n.sons[a].info, "named parameter has to be an identifier") m.state = csNoMatch return formal = getSymFromList(m.callee.n, n.sons[a].sons[0].ident, 1) @@ -2171,8 +2232,9 @@ proc matchesAux(c: PContext, n, nOrig: PNode, # we have no formal here to snoop at: n.sons[a] = prepareOperand(c, n.sons[a]) if skipTypes(n.sons[a].typ, abstractVar-{tyTypeDesc}).kind==tyString: - addSon(m.call, implicitConv(nkHiddenStdConv, getSysType(tyCString), - copyTree(n.sons[a]), m, c)) + addSon(m.call, implicitConv(nkHiddenStdConv, + getSysType(c.graph, n.sons[a].info, tyCString), + copyTree(n.sons[a]), m, c)) else: addSon(m.call, copyTree(n.sons[a])) elif formal != nil and formal.typ.kind == tyVarargs: @@ -2195,7 +2257,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, return else: if m.callee.n.sons[f].kind != nkSym: - internalError(n.sons[a].info, "matches") + internalError(c.config, n.sons[a].info, "matches") return formal = m.callee.n.sons[f].sym if containsOrIncl(marker, formal.position) and container.isNil: @@ -2218,6 +2280,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, n.sons[a], nOrig.sons[a]) if arg == nil: m.state = csNoMatch + m.firstMismatch = f return if m.baseTypeMatch: #assert(container == nil) @@ -2272,6 +2335,7 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = else: # no default value m.state = csNoMatch + m.firstMismatch = f break else: # use default value: @@ -2301,7 +2365,7 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo; var m: TCandidate initCandidate(c, m, dc.typ) if col >= dc.typ.len: - localError(info, errGenerated, "cannot instantiate '" & dc.name.s & "'") + localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'") return nil var f = dc.typ.sons[col] @@ -2310,7 +2374,7 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo; else: if f.kind == tyVar: f = f.lastSon if typeRel(m, f, t) == isNone: - localError(info, errGenerated, "cannot instantiate '" & dc.name.s & "'") + localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'") else: result = c.semGenerateInstance(c, dc, m.bindings, info) if op == attachedDeepCopy: diff --git a/compiler/suggest.nim b/compiler/suggest.nim index f9210cc93..23aecfa71 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -32,7 +32,8 @@ # included from sigmatch.nim -import algorithm, prefixmatches +import algorithm, prefixmatches, configuration +from wordrecg import wDeprecated when defined(nimsuggest): import passes, tables # importer @@ -105,7 +106,7 @@ proc cmpSuggestions(a, b: Suggest): int = # independent of hashing order: result = cmp(a.name.s, b.name.s) -proc symToSuggest(s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo; +proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo; quality: range[0..100]; prefix: PrefixMatch; inTypeContext: bool; scope: int): Suggest = new(result) @@ -124,7 +125,7 @@ proc symToSuggest(s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo; if u.fileIndex == info.fileIndex: inc c result.localUsages = c result.symkind = s.kind - if optIdeTerse notin gGlobalOptions: + if optIdeTerse notin conf.globalOptions: result.qualifiedPath = @[] if not isLocal and s.kind != skModule: let ow = s.owner @@ -191,8 +192,8 @@ proc suggestResult(s: Suggest) = else: suggestWriteln($s) -proc produceOutput(a: var Suggestions) = - if gIdeCmd in {ideSug, ideCon}: +proc produceOutput(a: var Suggestions; conf: ConfigRef) = + if conf.ideCmd in {ideSug, ideCon}: a.sort cmpSuggestions when defined(debug): # debug code @@ -236,11 +237,11 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} = proc suggestField(c: PContext, s: PSym; f: PNode; info: TLineInfo; outputs: var Suggestions) = var pm: PrefixMatch if filterSym(s, f, pm) and fieldVisible(c, s): - outputs.add(symToSuggest(s, isLocal=true, ideSug, info, 100, pm, c.inTypeContext > 0, 0)) + outputs.add(symToSuggest(c.config, s, isLocal=true, ideSug, info, 100, pm, c.inTypeContext > 0, 0)) proc getQuality(s: PSym): range[0..100] = if s.typ != nil and s.typ.len > 1: - var exp = s.typ.sons[1].skipTypes({tyGenericInst, tyVar, tyAlias}) + var exp = s.typ.sons[1].skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink}) if exp.kind == tyVarargs: exp = elemType(exp) if exp.kind in {tyExpr, tyStmt, tyGenericParam, tyAnything}: return 50 return 100 @@ -255,7 +256,7 @@ template wholeSymTab(cond, section: untyped) = let it {.inject.} = item var pm {.inject.}: PrefixMatch if cond: - outputs.add(symToSuggest(it, isLocal = isLocal, section, info, getQuality(it), + outputs.add(symToSuggest(c.config, it, isLocal = isLocal, section, info, getQuality(it), pm, c.inTypeContext > 0, scopeN)) proc suggestSymList(c: PContext, list, f: PNode; info: TLineInfo, outputs: var Suggestions) = @@ -309,7 +310,7 @@ proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} = 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, tyAlias}) + var exp = s.typ.sons[1].skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink}) 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) @@ -329,7 +330,7 @@ proc suggestEverything(c: PContext, n, f: PNode, outputs: var Suggestions) = for it in items(scope.symbols): var pm: PrefixMatch if filterSym(it, f, pm): - outputs.add(symToSuggest(it, isLocal = isLocal, ideSug, n.info, 0, pm, + outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug, n.info, 0, pm, c.inTypeContext > 0, scopeN)) #if scope == c.topLevelScope and f.isNil: break @@ -341,18 +342,18 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) when defined(nimsuggest): if n.kind == nkSym and n.sym.kind == skError and suggestVersion == 0: # consider 'foo.|' where 'foo' is some not imported module. - let fullPath = findModule(n.sym.name.s, n.info.toFullPath) + let fullPath = findModule(c.config, n.sym.name.s, n.info.toFullPath) if fullPath.len == 0: # error: no known module name: typ = nil else: - let m = gImportModule(c.graph, c.module, fullpath.fileInfoIdx, c.cache) + let m = gImportModule(c.graph, c.module, fileInfoIdx(c.config, fullpath), c.cache) if m == nil: typ = nil else: for it in items(n.sym.tab): if filterSym(it, field, pm): - outputs.add(symToSuggest(it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -100)) - outputs.add(symToSuggest(m, isLocal=false, ideMod, n.info, 100, PrefixMatch.None, + outputs.add(symToSuggest(c.config, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -100)) + outputs.add(symToSuggest(c.config, m, isLocal=false, ideMod, n.info, 100, PrefixMatch.None, c.inTypeContext > 0, -99)) if typ == nil: @@ -362,11 +363,11 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) # all symbols accessible, because we are in the current module: for it in items(c.topLevelScope.symbols): if filterSym(it, field, pm): - outputs.add(symToSuggest(it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99)) + outputs.add(symToSuggest(c.config, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99)) else: for it in items(n.sym.tab): if filterSym(it, field, pm): - outputs.add(symToSuggest(it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99)) + outputs.add(symToSuggest(c.config, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99)) else: # fallback: suggestEverything(c, n, field, outputs) @@ -378,8 +379,8 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) t = t.sons[0] suggestOperations(c, n, field, typ, outputs) else: - let orig = typ # skipTypes(typ, {tyGenericInst, tyAlias}) - typ = skipTypes(typ, {tyGenericInst, tyVar, tyPtr, tyRef, tyAlias}) + let orig = typ # skipTypes(typ, {tyGenericInst, tyAlias, tySink}) + typ = skipTypes(typ, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink}) if typ.kind == tyObject: var t = typ while true: @@ -414,7 +415,7 @@ when defined(nimsuggest): # Since TLineInfo defined a == operator that doesn't include the column, # we map TLineInfo to a unique int here for this lookup table: proc infoToInt(info: TLineInfo): int64 = - info.fileIndex + info.line.int64 shl 32 + info.col.int64 shl 48 + info.fileIndex.int64 + info.line.int64 shl 32 + info.col.int64 shl 48 proc addNoDup(s: PSym; info: TLineInfo) = # ensure nothing gets too slow: @@ -425,29 +426,29 @@ when defined(nimsuggest): s.allUsages.add(info) var - lastLineInfo*: TLineInfo + lastLineInfo*: TLineInfo # XXX global here -proc findUsages(info: TLineInfo; s: PSym; usageSym: var PSym) = +proc findUsages(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) = if suggestVersion == 1: if usageSym == nil and isTracked(info, s.name.s.len): usageSym = s - suggestResult(symToSuggest(s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0)) + suggestResult(symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0)) elif s == usageSym: if lastLineInfo != info: - suggestResult(symToSuggest(s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0)) + suggestResult(symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0)) lastLineInfo = info when defined(nimsuggest): - proc listUsages*(s: PSym) = + proc listUsages*(conf: ConfigRef; s: PSym) = #echo "usages ", len(s.allUsages) for info in s.allUsages: let x = if info == s.info and info.col == s.info.col: ideDef else: ideUse - suggestResult(symToSuggest(s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0)) + suggestResult(symToSuggest(conf, s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0)) -proc findDefinition(info: TLineInfo; s: PSym) = +proc findDefinition(conf: ConfigRef; info: TLineInfo; s: PSym) = if s.isNil: return if isTracked(info, s.name.s.len): - suggestResult(symToSuggest(s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0)) + suggestResult(symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0)) suggestQuit() proc ensureIdx[T](x: var T, y: int) = @@ -456,7 +457,7 @@ proc ensureIdx[T](x: var T, y: int) = proc ensureSeq[T](x: var seq[T]) = if x == nil: newSeq(x, 0) -proc suggestSym*(info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} = +proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} = ## misnamed: should be 'symDeclared' when defined(nimsuggest): if suggestVersion == 0: @@ -465,33 +466,44 @@ proc suggestSym*(info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.in else: s.addNoDup(info) - if gIdeCmd == ideUse: - findUsages(info, s, usageSym) - elif gIdeCmd == ideDef: - findDefinition(info, s) - elif gIdeCmd == ideDus and s != nil: + if conf.ideCmd == ideUse: + findUsages(conf, info, s, usageSym) + elif conf.ideCmd == ideDef: + findDefinition(conf, info, s) + elif conf.ideCmd == ideDus and s != nil: if isTracked(info, s.name.s.len): - suggestResult(symToSuggest(s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0)) - findUsages(info, s, usageSym) - elif gIdeCmd == ideHighlight and info.fileIndex == gTrackPos.fileIndex: - suggestResult(symToSuggest(s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0)) - elif gIdeCmd == ideOutline and info.fileIndex == gTrackPos.fileIndex and + suggestResult(symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0)) + findUsages(conf, info, s, usageSym) + elif conf.ideCmd == ideHighlight and info.fileIndex == gTrackPos.fileIndex: + suggestResult(symToSuggest(conf, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0)) + elif conf.ideCmd == ideOutline and info.fileIndex == gTrackPos.fileIndex and isDecl: - suggestResult(symToSuggest(s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0)) - -proc markUsed(info: TLineInfo; s: PSym; usageSym: var PSym) = + suggestResult(symToSuggest(conf, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0)) + +proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) = + if s.kind in routineKinds: + let n = s.ast[pragmasPos] + if n.kind != nkEmpty: + for it in n: + if whichPragma(it) == wDeprecated and it.safeLen == 2 and + it[1].kind in {nkStrLit..nkTripleStrLit}: + message(conf, info, warnDeprecated, it[1].strVal & "; " & s.name.s) + return + message(conf, info, warnDeprecated, s.name.s) + +proc markUsed(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) = incl(s.flags, sfUsed) if s.kind == skEnumField and s.owner != nil: incl(s.owner.flags, sfUsed) if {sfDeprecated, sfError} * s.flags != {}: - if sfDeprecated in s.flags: message(info, warnDeprecated, s.name.s) - if sfError in s.flags: localError(info, errWrongSymbolX, s.name.s) + if sfDeprecated in s.flags: warnAboutDeprecated(conf, info, s) + if sfError in s.flags: localError(conf, info, "usage of '$1' is a user-defined error" % s.name.s) when defined(nimsuggest): - suggestSym(info, s, usageSym, false) + suggestSym(conf, info, s, usageSym, false) -proc useSym*(sym: PSym; usageSym: var PSym): PNode = +proc useSym*(conf: ConfigRef; sym: PSym; usageSym: var PSym): PNode = result = newSymNode(sym) - markUsed(result.info, sym, usageSym) + markUsed(conf, result.info, sym, usageSym) proc safeSemExpr*(c: PContext, n: PNode): PNode = # use only for idetools support! @@ -522,9 +534,9 @@ proc suggestExprNoCheck*(c: PContext, n: PNode) = if c.compilesContextId > 0: return inc(c.compilesContextId) var outputs: Suggestions = @[] - if gIdeCmd == ideSug: + if c.config.ideCmd == ideSug: sugExpr(c, n, outputs) - elif gIdeCmd == ideCon: + elif c.config.ideCmd == ideCon: if n.kind in nkCallKinds: var a = copyNode(n) var x = safeSemExpr(c, n.sons[0]) @@ -538,8 +550,8 @@ proc suggestExprNoCheck*(c: PContext, n: PNode) = suggestCall(c, a, n, outputs) dec(c.compilesContextId) - if outputs.len > 0 and gIdeCmd in {ideSug, ideCon, ideDef}: - produceOutput(outputs) + if outputs.len > 0 and c.config.ideCmd in {ideSug, ideCon, ideDef}: + produceOutput(outputs, c.config) suggestQuit() proc suggestExpr*(c: PContext, n: PNode) = @@ -558,11 +570,11 @@ proc suggestStmt*(c: PContext, n: PNode) = proc suggestEnum*(c: PContext; n: PNode; t: PType) = var outputs: Suggestions = @[] suggestSymList(c, t.n, nil, n.info, outputs) - produceOutput(outputs) + produceOutput(outputs, c.config) if outputs.len > 0: suggestQuit() proc suggestSentinel*(c: PContext) = - if gIdeCmd != ideSug or c.module.position != gTrackPos.fileIndex: return + if c.config.ideCmd != ideSug or c.module.position != gTrackPos.fileIndex.int32: return if c.compilesContextId > 0: return inc(c.compilesContextId) var outputs: Suggestions = @[] @@ -575,7 +587,7 @@ proc suggestSentinel*(c: PContext) = for it in items(scope.symbols): var pm: PrefixMatch if filterSymNoOpr(it, nil, pm): - outputs.add(symToSuggest(it, isLocal = isLocal, ideSug, newLineInfo(gTrackPos.fileIndex, -1, -1), 0, PrefixMatch.None, false, scopeN)) + outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug, newLineInfo(gTrackPos.fileIndex, -1, -1), 0, PrefixMatch.None, false, scopeN)) dec(c.compilesContextId) - produceOutput(outputs) + produceOutput(outputs, c.config) diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index 4745b1ac7..4bc153e46 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -11,17 +11,17 @@ import strutils, llstream, ast, astalgo, idents, lexer, options, msgs, parser, - pbraces, filters, filter_tmpl, renderer + filters, filter_tmpl, renderer, configuration type TFilterKind* = enum filtNone, filtTemplate, filtReplace, filtStrip TParserKind* = enum - skinStandard, skinStrongSpaces, skinBraces, skinEndX + skinStandard, skinStrongSpaces, skinEndX const parserNames*: array[TParserKind, string] = ["standard", "strongspaces", - "braces", "endx"] + "endx"] filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace", "strip"] @@ -30,39 +30,38 @@ type skin*: TParserKind parser*: TParser +template config(p: TParsers): ConfigRef = p.parser.lex.config + proc parseAll*(p: var TParsers): PNode = case p.skin of skinStandard, skinStrongSpaces: result = parser.parseAll(p.parser) - of skinBraces: - result = pbraces.parseAll(p.parser) of skinEndX: - internalError("parser to implement") + internalError(p.config, "parser to implement") result = ast.emptyNode proc parseTopLevelStmt*(p: var TParsers): PNode = case p.skin of skinStandard, skinStrongSpaces: result = parser.parseTopLevelStmt(p.parser) - of skinBraces: - result = pbraces.parseTopLevelStmt(p.parser) of skinEndX: - internalError("parser to implement") + internalError(p.config, "parser to implement") result = ast.emptyNode proc utf8Bom(s: string): int = - if s[0] == '\xEF' and s[1] == '\xBB' and s[2] == '\xBF': + if s.len >= 3 and s[0] == '\xEF' and s[1] == '\xBB' and s[2] == '\xBF': result = 3 else: result = 0 proc containsShebang(s: string, i: int): bool = - if s[i] == '#' and s[i+1] == '!': + if i+1 < s.len and s[i] == '#' and s[i+1] == '!': var j = i + 2 - while s[j] in Whitespace: inc(j) + while j < s.len and s[j] in Whitespace: inc(j) result = s[j] == '/' -proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache): PNode = +proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache; + config: ConfigRef): PNode = result = ast.emptyNode var s = llStreamOpen(filename, fmRead) if s != nil: @@ -74,11 +73,11 @@ proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache): PNo discard llStreamReadLine(s, line) i = 0 inc linenumber - if line[i] == '#' and line[i+1] == '?': + if i+1 < line.len and line[i] == '#' and line[i+1] == '?': inc(i, 2) - while line[i] in Whitespace: inc(i) + while i < line.len and line[i] in Whitespace: inc(i) var q: TParser - parser.openParser(q, filename, llStreamOpen(substr(line, i)), cache) + parser.openParser(q, filename, llStreamOpen(substr(line, i)), cache, config) result = parser.parseAll(q) parser.closeParser(q) llStreamClose(s) @@ -89,42 +88,44 @@ proc getFilter(ident: PIdent): TFilterKind = return i result = filtNone -proc getParser(ident: PIdent): TParserKind = +proc getParser(conf: ConfigRef; n: PNode; ident: PIdent): TParserKind = for i in countup(low(TParserKind), high(TParserKind)): if cmpIgnoreStyle(ident.s, parserNames[i]) == 0: return i - rawMessage(errInvalidDirectiveX, ident.s) + localError(conf, n.info, "unknown parser: " & ident.s) -proc getCallee(n: PNode): PIdent = +proc getCallee(conf: ConfigRef; n: PNode): PIdent = if n.kind in nkCallKinds and n.sons[0].kind == nkIdent: result = n.sons[0].ident elif n.kind == nkIdent: result = n.ident else: - rawMessage(errXNotAllowedHere, renderTree(n)) + localError(conf, n.info, "invalid filter: " & renderTree(n)) proc applyFilter(p: var TParsers, n: PNode, filename: string, stdin: PLLStream): PLLStream = - var ident = getCallee(n) + var ident = getCallee(p.config, n) var f = getFilter(ident) case f of filtNone: - p.skin = getParser(ident) + p.skin = getParser(p.config, n, ident) result = stdin of filtTemplate: - result = filterTmpl(stdin, filename, n) + result = filterTmpl(stdin, filename, n, p.config) of filtStrip: - result = filterStrip(stdin, filename, n) + result = filterStrip(p.config, stdin, filename, n) of filtReplace: - result = filterReplace(stdin, filename, n) + result = filterReplace(p.config, stdin, filename, n) if f != filtNone: - if hintCodeBegin in gNotes: - rawMessage(hintCodeBegin, []) - msgWriteln(result.s) - rawMessage(hintCodeEnd, []) + assert p.config != nil + if hintCodeBegin in p.config.notes: + rawMessage(p.config, hintCodeBegin, []) + msgWriteln(p.config, result.s) + rawMessage(p.config, hintCodeEnd, []) proc evalPipe(p: var TParsers, n: PNode, filename: string, start: PLLStream): PLLStream = + assert p.config != nil result = start if n.kind == nkEmpty: return if n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "|": @@ -138,31 +139,33 @@ proc evalPipe(p: var TParsers, n: PNode, filename: string, else: result = applyFilter(p, n, filename, result) -proc openParsers*(p: var TParsers, fileIdx: int32, inputstream: PLLStream; - cache: IdentCache) = +proc openParsers*(p: var TParsers, fileIdx: FileIndex, inputstream: PLLStream; + cache: IdentCache; config: ConfigRef) = + assert config != nil var s: PLLStream p.skin = skinStandard let filename = fileIdx.toFullPathConsiderDirty - var pipe = parsePipe(filename, inputstream, cache) + var pipe = parsePipe(filename, inputstream, cache, config) + p.config() = config if pipe != nil: s = evalPipe(p, pipe, filename, inputstream) else: s = inputstream case p.skin - of skinStandard, skinBraces, skinEndX: - parser.openParser(p.parser, fileIdx, s, cache, false) + of skinStandard, skinEndX: + parser.openParser(p.parser, fileIdx, s, cache, config, false) of skinStrongSpaces: - parser.openParser(p.parser, fileIdx, s, cache, true) + parser.openParser(p.parser, fileIdx, s, cache, config, true) proc closeParsers*(p: var TParsers) = parser.closeParser(p.parser) -proc parseFile*(fileIdx: int32; cache: IdentCache): PNode {.procvar.} = +proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode {.procvar.} = var p: TParsers f: File let filename = fileIdx.toFullPathConsiderDirty if not open(f, filename): - rawMessage(errCannotOpenFile, filename) + rawMessage(config, errGenerated, "cannot open file: " & filename) return - openParsers(p, fileIdx, llStreamOpen(f), cache) + openParsers(p, fileIdx, llStreamOpen(f), cache, config) result = parseAll(p) closeParsers(p) diff --git a/compiler/transf.nim b/compiler/transf.nim index f8f7f8746..c2add13ff 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -19,9 +19,10 @@ # * transforms 'defer' into a 'try finally' statement import - intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os, + intsets, strutils, options, ast, astalgo, trees, treetab, msgs, lookups, idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread, - lambdalifting, sempass2, lowerings, lookups, destroyer, liftlocals + lambdalifting, sempass2, lowerings, destroyer, liftlocals, closureiters, + modulegraphs type PTransNode* = distinct PNode @@ -44,6 +45,7 @@ type nestedProcs: int # > 0 if we are in a nested proc contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' deferDetected, tooEarly, needsDestroyPass: bool + graph: ModuleGraph PTransf = ref TTransfContext proc newTransNode(a: PNode): PTransNode {.inline.} = @@ -84,7 +86,7 @@ proc pushTransCon(c: PTransf, t: PTransCon) = c.transCon = t proc popTransCon(c: PTransf) = - if (c.transCon == nil): internalError("popTransCon") + if (c.transCon == nil): internalError(c.graph.config, "popTransCon") c.transCon = c.transCon.next proc getCurrOwner(c: PTransf): PSym = @@ -93,11 +95,11 @@ proc getCurrOwner(c: PTransf): PSym = proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode = let r = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c), info) - r.typ = typ #skipTypes(typ, {tyGenericInst, tyAlias}) + r.typ = typ #skipTypes(typ, {tyGenericInst, tyAlias, tySink}) incl(r.flags, sfFromGeneric) let owner = getCurrOwner(c) if owner.isIterator and not c.tooEarly: - result = freshVarForClosureIter(r, owner) + result = freshVarForClosureIter(c.graph, r, owner) else: result = newSymNode(r) @@ -118,10 +120,10 @@ proc transformSymAux(c: PTransf, n: PNode): PNode = if s.typ != nil and s.typ.callConv == ccClosure: if s.kind == skIterator: if c.tooEarly: return n - else: return liftIterSym(n, getCurrOwner(c)) + else: return liftIterSym(c.graph, n, getCurrOwner(c)) elif s.kind in {skProc, skFunc, skConverter, skMethod} and not c.tooEarly: # top level .closure procs are still somewhat supported for 'Nake': - return makeClosure(s, nil, n.info) + return makeClosure(c.graph, s, nil, n.info) #elif n.sym.kind in {skVar, skLet} and n.sym.typ.callConv == ccClosure: # echo n.info, " come heer for ", c.tooEarly # if not c.tooEarly: @@ -130,7 +132,7 @@ proc transformSymAux(c: PTransf, n: PNode): PNode = if sfBorrow in s.flags and s.kind in routineKinds: # simply exchange the symbol: b = s.getBody - if b.kind != nkSym: internalError(n.info, "wrong AST for borrowed symbol") + if b.kind != nkSym: internalError(c.graph.config, n.info, "wrong AST for borrowed symbol") b = newSymNode(b.sym, n.info) else: b = n @@ -151,7 +153,7 @@ proc transformSym(c: PTransf, n: PNode): PTransNode = proc freshVar(c: PTransf; v: PSym): PNode = let owner = getCurrOwner(c) if owner.isIterator and not c.tooEarly: - result = freshVarForClosureIter(v, owner) + result = freshVarForClosureIter(c.graph, v, owner) else: var newVar = copySym(v) incl(newVar.flags, sfFromGeneric) @@ -166,11 +168,11 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode = result[i] = PTransNode(it) elif it.kind == nkIdentDefs: if it.sons[0].kind == nkSym: - internalAssert(it.len == 3) + internalAssert(c.graph.config, it.len == 3) let x = freshVar(c, it.sons[0].sym) idNodeTablePut(c.transCon.mapping, it.sons[0].sym, x) var defs = newTransNode(nkIdentDefs, it.info, 3) - if importantComments(): + if importantComments(c.graph.config): # keep documentation information: PNode(defs).comment = it.comment defs[0] = x.PTransNode @@ -184,7 +186,7 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode = result[i] = transform(c, it) else: if it.kind != nkVarTuple: - internalError(it.info, "transformVarSection: not nkVarTuple") + internalError(c.graph.config, it.info, "transformVarSection: not nkVarTuple") var L = sonsLen(it) var defs = newTransNode(it.kind, it.info, L) for j in countup(0, L-3): @@ -203,9 +205,9 @@ proc transformConstSection(c: PTransf, v: PNode): PTransNode = if it.kind == nkCommentStmt: result[i] = PTransNode(it) else: - if it.kind != nkConstDef: internalError(it.info, "transformConstSection") + if it.kind != nkConstDef: internalError(c.graph.config, it.info, "transformConstSection") if it.sons[0].kind != nkSym: - internalError(it.info, "transformConstSection") + internalError(c.graph.config, it.info, "transformConstSection") result[i] = PTransNode(it) @@ -301,7 +303,7 @@ proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) = # XXX: BUG: what if `n` is an expression with side-effects? for i in countup(0, sonsLen(c.transCon.forStmt) - 3): add(father, newAsgnStmt(c, c.transCon.forStmt.sons[i], - transform(c, newTupleAccess(n, i)))) + transform(c, newTupleAccess(c.graph, n, i)))) proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode = case n.kind @@ -331,10 +333,10 @@ proc transformYield(c: PTransf, n: PNode): PTransNode = # c.transCon.forStmt.len == 3 means that there is one for loop variable # and thus no tuple unpacking: if e.typ.isNil: return result # can happen in nimsuggest for unknown reasons - if skipTypes(e.typ, {tyGenericInst, tyAlias}).kind == tyTuple and + if skipTypes(e.typ, {tyGenericInst, tyAlias, tySink}).kind == tyTuple and c.transCon.forStmt.len != 3: e = skipConv(e) - if e.kind == nkPar: + if e.kind in {nkPar, nkTupleConstr}: for i in countup(0, sonsLen(e) - 1): var v = e.sons[i] if v.kind == nkExprColonExpr: v = v.sons[1] @@ -356,7 +358,7 @@ proc transformYield(c: PTransf, n: PNode): PTransNode = proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode = result = transformSons(c, n) - if gCmd == cmdCompileToCpp or sfCompileToCpp in c.module.flags: return + if c.graph.config.cmd == cmdCompileToCpp or sfCompileToCpp in c.module.flags: return var n = result.PNode case n.sons[0].kind of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: @@ -382,21 +384,21 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode = if n.typ.skipTypes(abstractVar).kind != tyOpenArray: PNode(result).typ = n.typ -proc generateThunk(prc: PNode, dest: PType): PNode = +proc generateThunk(c: PTransf; prc: PNode, dest: PType): PNode = ## Converts 'prc' into '(thunk, nil)' so that it's compatible with ## a closure. # we cannot generate a proper thunk here for GC-safety reasons # (see internal documentation): - if gCmd == cmdCompileToJS: return prc + if c.graph.config.cmd == cmdCompileToJS: return prc result = newNodeIT(nkClosure, prc.info, dest) var conv = newNodeIT(nkHiddenSubConv, prc.info, dest) conv.add(emptyNode) conv.add(prc) if prc.kind == nkClosure: - internalError(prc.info, "closure to closure created") + internalError(c.graph.config, prc.info, "closure to closure created") result.add(conv) - result.add(newNodeIT(nkNilLit, prc.info, getSysType(tyNil))) + result.add(newNodeIT(nkNilLit, prc.info, getSysType(c.graph, prc.info, tyNil))) proc transformConv(c: PTransf, n: PNode): PTransNode = # numeric types need range checks: @@ -481,7 +483,7 @@ proc transformConv(c: PTransf, n: PNode): PTransNode = of tyProc: result = transformSons(c, n) if dest.callConv == ccClosure and source.callConv == ccDefault: - result = generateThunk(result[1].PNode, dest).PTransNode + result = generateThunk(c, result[1].PNode, dest).PTransNode else: result = transformSons(c, n) @@ -500,20 +502,20 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto = case arg.kind of nkEmpty..nkNilLit: result = paDirectMapping - of nkPar, nkCurly, nkBracket: + of nkPar, nkTupleConstr, nkCurly, nkBracket: result = paFastAsgn for i in countup(0, sonsLen(arg) - 1): if putArgInto(arg.sons[i], formal) != paDirectMapping: return result = paDirectMapping else: - if skipTypes(formal, abstractInst).kind == tyVar: result = paVarAsgn + if skipTypes(formal, abstractInst).kind in {tyVar, tyLent}: result = paVarAsgn else: result = paFastAsgn proc findWrongOwners(c: PTransf, n: PNode) = if n.kind == nkVarSection: let x = n.sons[0].sons[0] if x.kind == nkSym and x.sym.owner != getCurrOwner(c): - internalError(x.info, "bah " & x.sym.name.s & " " & + internalError(c.graph.config, x.info, "bah " & x.sym.name.s & " " & x.sym.owner.name.s & " " & getCurrOwner(c).name.s) else: for i in 0 ..< safeLen(n): findWrongOwners(c, n.sons[i]) @@ -521,7 +523,7 @@ proc findWrongOwners(c: PTransf, n: PNode) = proc transformFor(c: PTransf, n: PNode): PTransNode = # generate access statements for the parameters (unless they are constant) # put mapping from formal parameters to actual parameters - if n.kind != nkForStmt: internalError(n.info, "transformFor") + if n.kind != nkForStmt: internalError(c.graph.config, n.info, "transformFor") var length = sonsLen(n) var call = n.sons[length - 2] @@ -539,7 +541,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode = n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode if not c.tooEarly: n.sons[length-2] = transform(c, n.sons[length-2]).PNode - result[1] = lambdalifting.liftForLoop(n, getCurrOwner(c)).PTransNode + result[1] = lambdalifting.liftForLoop(c.graph, n, getCurrOwner(c)).PTransNode else: result[1] = newNode(nkEmpty).PTransNode discard c.breakSyms.pop @@ -689,7 +691,7 @@ proc transformCall(c: PTransf, n: PNode): PTransNode = while (j < sonsLen(n)): let b = transform(c, n.sons[j]).PNode if not isConstExpr(b): break - a = evalOp(op.magic, n, a, b, nil) + a = evalOp(op.magic, n, a, b, nil, c.graph) inc(j) add(result, a.PTransNode) if len(result) == 2: result = result[1] @@ -708,18 +710,18 @@ proc transformCall(c: PTransf, n: PNode): PTransNode = let t = lastSon(s.sons[0].sym.ast) if t.kind != nkSym or sfDispatcher notin t.sym.flags: methodDef(s.sons[0].sym, false) - result = methodCall(s).PTransNode + result = methodCall(s, c.graph.config).PTransNode else: result = s.PTransNode proc transformExceptBranch(c: PTransf, n: PNode): PTransNode = result = transformSons(c, n) - if n[0].isInfixAs(): + if n[0].isInfixAs() and not isImportedException(n[0][1].typ, c.graph.config): let excTypeNode = n[0][1] - let actions = newTransNode(nkStmtList, n[1].info, 2) + let actions = newTransNode(nkStmtListExpr, n[1], 2) # Generating `let exc = (excType)(getCurrentException())` # -> getCurrentException() - let excCall = PTransNode(callCodegenProc("getCurrentException", ast.emptyNode)) + let excCall = PTransNode(callCodegenProc(c.graph, "getCurrentException", ast.emptyNode)) # -> (excType) let convNode = newTransNode(nkHiddenSubConv, n[1].info, 2) convNode[0] = PTransNode(ast.emptyNode) @@ -745,13 +747,13 @@ proc transformExceptBranch(c: PTransf, n: PNode): PTransNode = proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} = # symbols that expand to a complex constant (array, etc.) should not be # inlined, unless it's the empty array: - result = orig.kind == nkSym and cnst.kind in {nkCurly, nkPar, nkBracket} and + result = orig.kind == nkSym and cnst.kind in {nkCurly, nkPar, nkTupleConstr, nkBracket} and cnst.len != 0 -proc commonOptimizations*(c: PSym, n: PNode): PNode = +proc commonOptimizations*(g: ModuleGraph; c: PSym, n: PNode): PNode = result = n for i in 0 ..< n.safeLen: - result.sons[i] = commonOptimizations(c, n.sons[i]) + result.sons[i] = commonOptimizations(g, c, n.sons[i]) var op = getMergeOp(n) if (op != nil) and (op.magic != mNone) and (sonsLen(n) >= 3): result = newNodeIT(nkCall, n.info, n.typ) @@ -766,12 +768,12 @@ proc commonOptimizations*(c: PSym, n: PNode): PNode = while j < sonsLen(args): let b = args.sons[j] if not isConstExpr(b): break - a = evalOp(op.magic, result, a, b, nil) + a = evalOp(op.magic, result, a, b, nil, g) inc(j) add(result, a) if len(result) == 2: result = result[1] else: - var cnst = getConstExpr(c, n) + var cnst = getConstExpr(c, n, g) # we inline constants if they are not complex constants: if cnst != nil and not dontInlineConstant(n, cnst): result = cnst @@ -888,7 +890,7 @@ proc transform(c: PTransf, n: PNode): PTransNode = let L = n.len-1 result[L] = transform(c, n.sons[L]) # XXX comment handling really sucks: - if importantComments(): + if importantComments(c.graph.config): PNode(result).comment = n.comment of nkClosure: # it can happen that for-loop-inlining produced a fresh @@ -905,7 +907,7 @@ proc transform(c: PTransf, n: PNode): PTransNode = when false: if oldDeferAnchor != nil: c.deferAnchor = oldDeferAnchor - var cnst = getConstExpr(c.module, PNode(result)) + var cnst = getConstExpr(c.module, PNode(result), c.graph) # we inline constants if they are not complex constants: if cnst != nil and not dontInlineConstant(n, cnst): result = PTransNode(cnst) # do not miss an optimization @@ -920,11 +922,12 @@ proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode = popTransCon(c) incl(result.flags, nfTransf) -proc openTransf(module: PSym, filename: string): PTransf = +proc openTransf(g: ModuleGraph; module: PSym, filename: string): PTransf = new(result) result.contSyms = @[] result.breakSyms = @[] result.module = module + result.graph = g proc flattenStmts(n: PNode) = var goOn = true @@ -967,46 +970,50 @@ template liftDefer(c, root) = if c.deferDetected: liftDeferAux(root) -proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode = +proc transformBody*(g: ModuleGraph; module: PSym, n: PNode, prc: PSym): PNode = if nfTransf in n.flags or prc.kind in {skTemplate}: result = n else: - var c = openTransf(module, "") - result = liftLambdas(prc, n, c.tooEarly) + var c = openTransf(g, module, "") + result = liftLambdas(g, prc, n, c.tooEarly) #result = n result = processTransf(c, result, prc) liftDefer(c, result) #result = liftLambdas(prc, result) - when useEffectSystem: trackProc(prc, result) - result = liftLocalsIfRequested(prc, result) - if c.needsDestroyPass and newDestructors: - result = injectDestructorCalls(prc, result) + when useEffectSystem: trackProc(g, prc, result) + result = liftLocalsIfRequested(prc, result, g.config) + if c.needsDestroyPass: #and newDestructors: + result = injectDestructorCalls(g, prc, result) + + if prc.isIterator and oldIterTransf notin g.config.features: + result = g.transformClosureIterator(prc, result) + incl(result.flags, nfTransf) #if prc.name.s == "testbody": # echo renderTree(result) -proc transformStmt*(module: PSym, n: PNode): PNode = +proc transformStmt*(g: ModuleGraph; module: PSym, n: PNode): PNode = if nfTransf in n.flags: result = n else: - var c = openTransf(module, "") + var c = openTransf(g, module, "") result = processTransf(c, n, module) liftDefer(c, result) #result = liftLambdasForTopLevel(module, result) - when useEffectSystem: trackTopLevelStmt(module, result) + when useEffectSystem: trackTopLevelStmt(g, module, result) #if n.info ?? "temp.nim": # echo renderTree(result, {renderIds}) - if c.needsDestroyPass and newDestructors: - result = injectDestructorCalls(module, result) + if c.needsDestroyPass: + result = injectDestructorCalls(g, module, result) incl(result.flags, nfTransf) -proc transformExpr*(module: PSym, n: PNode): PNode = +proc transformExpr*(g: ModuleGraph; module: PSym, n: PNode): PNode = if nfTransf in n.flags: result = n else: - var c = openTransf(module, "") + var c = openTransf(g, module, "") result = processTransf(c, n, module) liftDefer(c, result) - if c.needsDestroyPass and newDestructors: - result = injectDestructorCalls(module, result) + if c.needsDestroyPass: + result = injectDestructorCalls(g, module, result) incl(result.flags, nfTransf) diff --git a/compiler/trees.nim b/compiler/trees.nim index 7efefdc2e..fb523de9d 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -97,12 +97,12 @@ proc isDeepConstExpr*(n: PNode): bool = result = true of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv: result = isDeepConstExpr(n.sons[1]) - of nkCurly, nkBracket, nkPar, nkObjConstr, nkClosure, nkRange: + of nkCurly, nkBracket, nkPar, nkTupleConstr, nkObjConstr, nkClosure, nkRange: for i in ord(n.kind == nkObjConstr) ..< n.len: if not isDeepConstExpr(n.sons[i]): return false if n.typ.isNil: result = true else: - let t = n.typ.skipTypes({tyGenericInst, tyDistinct, tyAlias}) + let t = n.typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink}) if t.kind in {tyRef, tyPtr}: return false if t.kind != tyObject or not isCaseObj(t.n): result = true @@ -118,7 +118,7 @@ proc isRange*(n: PNode): bool {.inline.} = result = true proc whichPragma*(n: PNode): TSpecialWord = - let key = if n.kind == nkExprColonExpr: n.sons[0] else: n + let key = if n.kind in nkPragmaCallKinds and n.len > 0: n.sons[0] else: n if key.kind == nkIdent: result = whichKeyword(key.ident) proc unnestStmts(n, result: PNode) = diff --git a/compiler/types.nim b/compiler/types.nim index 495a1977d..1fab842cc 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -10,7 +10,7 @@ # this module contains routines for accessing and iterating over types import - intsets, ast, astalgo, trees, msgs, strutils, platform, renderer + intsets, ast, astalgo, trees, msgs, strutils, platform, renderer, options type TPreferedDesc* = enum @@ -51,17 +51,17 @@ const # TODO: Remove tyTypeDesc from each abstractX and (where necessary) # replace with typedescX abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal, - tyTypeDesc, tyAlias, tyInferred} + tyTypeDesc, tyAlias, tyInferred, tySink, tyLent} abstractVar* = {tyVar, tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, - tyAlias, tyInferred} + tyAlias, tyInferred, tySink, tyLent} abstractRange* = {tyGenericInst, tyRange, tyDistinct, tyOrdinal, tyTypeDesc, - tyAlias, tyInferred} + tyAlias, tyInferred, tySink} abstractVarRange* = {tyGenericInst, tyRange, tyVar, tyDistinct, tyOrdinal, - tyTypeDesc, tyAlias, tyInferred} + tyTypeDesc, tyAlias, tyInferred, tySink} abstractInst* = {tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, - tyInferred} + tyInferred, tySink} skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyTypeDesc, tyAlias, - tyInferred} + tyInferred, tySink, tyLent} # typedescX is used if we're sure tyTypeDesc should be included (or skipped) typedescPtrs* = abstractPtrs + {tyTypeDesc} typedescInst* = abstractInst + {tyTypeDesc} @@ -92,8 +92,9 @@ proc getOrdValue*(n: PNode): BiggestInt = of nkNilLit: result = 0 of nkHiddenStdConv: result = getOrdValue(n.sons[1]) else: - localError(n.info, errOrdinalTypeExpected) - result = 0 + #localError(n.info, errOrdinalTypeExpected) + # XXX check usages of getOrdValue + result = high(BiggestInt) proc isIntLit*(t: PType): bool {.inline.} = result = t.kind == tyInt and t.n != nil and t.n.kind == nkIntLit @@ -105,14 +106,14 @@ proc getProcHeader*(sym: PSym; prefer: TPreferedDesc = preferName): string = result = sym.owner.name.s & '.' & sym.name.s & '(' var n = sym.typ.n for i in countup(1, sonsLen(n) - 1): - var p = n.sons[i] + let p = n.sons[i] if p.kind == nkSym: add(result, p.sym.name.s) add(result, ": ") add(result, typeToString(p.sym.typ, prefer)) if i != sonsLen(n)-1: add(result, ", ") else: - internalError("getProcHeader") + result.add renderTree(p) add(result, ')') if n.sons[0].typ != nil: result.add(": " & typeToString(n.sons[0].typ, prefer)) @@ -195,10 +196,10 @@ proc searchTypeNodeForAux(n: PNode, p: TTypePredicate, of nkOfBranch, nkElse: result = searchTypeNodeForAux(lastSon(n.sons[i]), p, marker) if result: return - else: internalError("searchTypeNodeForAux(record case branch)") + else: discard of nkSym: result = searchTypeForAux(n.sym.typ, p, marker) - else: internalError(n.info, "searchTypeNodeForAux()") + else: discard proc searchTypeForAux(t: PType, predicate: TTypePredicate, marker: var IntSet): bool = @@ -244,7 +245,7 @@ proc analyseObjectWithTypeFieldAux(t: PType, if t == nil: return case t.kind of tyObject: - if (t.n != nil): + if t.n != nil: if searchTypeNodeForAux(t.n, isObjectWithTypeFieldPredicate, marker): return frEmbedded for i in countup(0, sonsLen(t) - 1): @@ -388,8 +389,8 @@ const "int", "int8", "int16", "int32", "int64", "float", "float32", "float64", "float128", "uint", "uint8", "uint16", "uint32", "uint64", - "unused0", "unused1", - "unused2", "varargs[$1]", "unused", "Error Type", + "opt", "sink", + "lent", "varargs[$1]", "unused", "Error Type", "BuiltInTypeClass", "UserTypeClass", "UserTypeClassInst", "CompositeTypeClass", "inferred", "and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor", @@ -453,16 +454,17 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = if t.sons[0].kind == tyNone: result = "typedesc" else: result = "type " & typeToString(t.sons[0]) of tyStatic: - internalAssert t.len > 0 if prefer == preferGenericArg and t.n != nil: result = t.n.renderTree else: - result = "static[" & typeToString(t.sons[0]) & "]" + result = "static[" & (if t.len > 0: typeToString(t.sons[0]) else: "") & "]" if t.n != nil: result.add "(" & renderTree(t.n) & ")" of tyUserTypeClass: - internalAssert t.sym != nil and t.sym.owner != nil - if t.isResolvedUserTypeClass: return typeToString(t.lastSon) - return t.sym.owner.name.s + if t.sym != nil and t.sym.owner != nil: + if t.isResolvedUserTypeClass: return typeToString(t.lastSon) + return t.sym.owner.name.s + else: + result = "<invalid tyUserTypeClass>" of tyBuiltInTypeClass: result = case t.base.kind: of tyVar: "var" @@ -496,7 +498,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = of tyNot: result = "not " & typeToString(t.sons[0]) of tyExpr: - internalAssert t.len == 0 + #internalAssert t.len == 0 result = "untyped" of tyFromExpr: result = renderTree(t.n) @@ -539,7 +541,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = add(result, typeToString(t.sons[i])) if i < sonsLen(t) - 1: add(result, ", ") add(result, ')') - of tyPtr, tyRef, tyVar: + of tyPtr, tyRef, tyVar, tyLent: result = typeToStr[t.kind] if t.len >= 2: setLen(result, result.len-1) @@ -567,7 +569,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = add(result, typeToString(t.sons[i])) if i < sonsLen(t) - 1: add(result, ", ") add(result, ')') - if t.sons[0] != nil: add(result, ": " & typeToString(t.sons[0])) + if t.len > 0 and t.sons[0] != nil: add(result, ": " & typeToString(t.sons[0])) var prag = if t.callConv == ccDefault: "" else: CallingConvToStr[t.callConv] if tfNoSideEffect in t.flags: addSep(prag) @@ -581,6 +583,8 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = if len(prag) != 0: add(result, "{." & prag & ".}") of tyVarargs: result = typeToStr[t.kind] % typeToString(t.sons[0]) + of tySink: + result = "sink " & typeToString(t.sons[0]) else: result = typeToStr[t.kind] result.addTypeFlags(t) @@ -610,16 +614,16 @@ proc firstOrd*(t: PType): BiggestInt = else: assert(t.n.sons[0].kind == nkSym) result = t.n.sons[0].sym.position - of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias: + of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred: result = firstOrd(lastSon(t)) of tyOrdinal: if t.len > 0: result = firstOrd(lastSon(t)) - else: internalError("invalid kind for first(" & $t.kind & ')') + else: internalError(newPartialConfigRef(), "invalid kind for firstOrd(" & $t.kind & ')') else: - internalError("invalid kind for first(" & $t.kind & ')') + internalError(newPartialConfigRef(), "invalid kind for firstOrd(" & $t.kind & ')') result = 0 -proc lastOrd*(t: PType): BiggestInt = +proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt = case t.kind of tyBool: result = 1 of tyChar: result = 255 @@ -638,22 +642,25 @@ proc lastOrd*(t: PType): BiggestInt = of tyInt64: result = 0x7FFFFFFFFFFFFFFF'i64 of tyUInt: if platform.intSize == 4: result = 0xFFFFFFFF + elif fixedUnsigned: result = 0xFFFFFFFFFFFFFFFF'i64 else: result = 0x7FFFFFFFFFFFFFFF'i64 of tyUInt8: result = 0xFF of tyUInt16: result = 0xFFFF of tyUInt32: result = 0xFFFFFFFF - of tyUInt64: result = 0x7FFFFFFFFFFFFFFF'i64 + of tyUInt64: + if fixedUnsigned: result = 0xFFFFFFFFFFFFFFFF'i64 + else: result = 0x7FFFFFFFFFFFFFFF'i64 of tyEnum: assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym) result = t.n.sons[sonsLen(t.n) - 1].sym.position - of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias: + of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred: result = lastOrd(lastSon(t)) of tyProxy: result = 0 of tyOrdinal: if t.len > 0: result = lastOrd(lastSon(t)) - else: internalError("invalid kind for last(" & $t.kind & ')') + else: internalError(newPartialConfigRef(), "invalid kind for lastOrd(" & $t.kind & ')') else: - internalError("invalid kind for last(" & $t.kind & ')') + internalError(newPartialConfigRef(), "invalid kind for lastOrd(" & $t.kind & ')') result = 0 proc lengthOrd*(t: PType): BiggestInt = @@ -743,7 +750,7 @@ proc equalParam(a, b: PSym): TParamsEquality = proc sameConstraints(a, b: PNode): bool = if isNil(a) and isNil(b): return true - internalAssert a.len == b.len + if a.len != b.len: return false for i in 1 ..< a.len: if not exprStructuralEquivalent(a[i].sym.constraint, b[i].sym.constraint): @@ -772,8 +779,8 @@ proc equalParams(a, b: PNode): TParamsEquality = return paramsNotEqual # paramsIncompatible; # continue traversal! If not equal, we can return immediately; else # it stays incompatible - if not sameTypeOrNil(a.sons[0].typ, b.sons[0].typ, {ExactTypeDescValues}): - if (a.sons[0].typ == nil) or (b.sons[0].typ == nil): + if not sameTypeOrNil(a.typ, b.typ, {ExactTypeDescValues}): + if (a.typ == nil) or (b.typ == nil): result = paramsNotEqual # one proc has a result, the other not is OK else: result = paramsIncompatible # overloading by different @@ -802,7 +809,8 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool = var y = b.n.sons[i].sym result = x.name.id == y.name.id if not result: break - else: internalError(a.n.info, "sameTuple") + else: + return false elif a.n != b.n and (a.n == nil or b.n == nil) and IgnoreTupleFields notin c.flags: result = false @@ -965,10 +973,10 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = result = sameFlags(a, b) of tyGenericParam: result = sameChildrenAux(a, b, c) and sameFlags(a, b) - if result and ExactGenericParams in c.flags: + if result and {ExactGenericParams, ExactTypeDescValues} * c.flags != {}: result = a.sym.position == b.sym.position of tyGenericInvocation, tyGenericBody, tySequence, - tyOpenArray, tySet, tyRef, tyPtr, tyVar, + tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyLent, tySink, tyArray, tyProc, tyVarargs, tyOrdinal, tyTypeClasses, tyOpt: cycleCheck() if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n @@ -992,7 +1000,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = cycleCheck() result = sameTypeAux(a.lastSon, b.lastSon, c) of tyNone: result = false - of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("sameFlags") + of tyUnused, tyOptAsRef: result = false proc sameBackendType*(x, y: PType): bool = var c = initSameTypeClosure() @@ -1051,16 +1059,21 @@ proc commonSuperclass*(a, b: PType): PType = x = x.sons[0] var y = b while y != nil: + var t = y # bug #7818, save type before skip y = skipTypes(y, skipPtrs) - if ancestors.contains(y.id): return y + if ancestors.contains(y.id): + # bug #7818, defer the previous skipTypes + if t.kind != tyGenericInst: t = y + return t y = y.sons[0] type - TTypeAllowedFlag = enum + TTypeAllowedFlag* = enum taField, - taHeap + taHeap, + taConcept - TTypeAllowedFlags = set[TTypeAllowedFlag] + TTypeAllowedFlags* = set[TTypeAllowedFlag] proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, flags: TTypeAllowedFlags = {}): PType @@ -1101,11 +1114,11 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, if containsOrIncl(marker, typ.id): return var t = skipTypes(typ, abstractInst-{tyTypeDesc}) case t.kind - of tyVar: + of tyVar, tyLent: if kind in {skProc, skFunc, skConst}: return t var t2 = skipTypes(t.sons[0], abstractInst-{tyTypeDesc}) case t2.kind - of tyVar: + of tyVar, tyLent: if taHeap notin flags: result = t2 # ``var var`` is illegal on the heap of tyOpenArray: if kind != skParam: result = t @@ -1128,7 +1141,12 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, of tyVoid: if taField notin flags: result = t of tyTypeClasses: - if not (tfGenericTypeParam in t.flags or taField notin flags): result = t + if tfGenericTypeParam in t.flags or taConcept in flags: #or taField notin flags: + discard + elif t.isResolvedUserTypeClass: + result = typeAllowedAux(marker, t.lastSon, kind, flags) + elif kind notin {skParam, skResult}: + result = t of tyGenericBody, tyGenericParam, tyGenericInvocation, tyNone, tyForward, tyFromExpr: result = t @@ -1143,15 +1161,19 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, of tyRange: if skipTypes(t.sons[0], abstractInst-{tyTypeDesc}).kind notin {tyChar, tyEnum, tyInt..tyFloat128, tyUInt8..tyUInt32}: result = t - of tyOpenArray, tyVarargs: + of tyOpenArray, tyVarargs, tySink: if kind != skParam: result = t else: result = typeAllowedAux(marker, t.sons[0], skVar, flags) of tySequence, tyOpt: if t.sons[0].kind != tyEmpty: result = typeAllowedAux(marker, t.sons[0], skVar, flags+{taHeap}) + elif kind in {skVar, skLet}: + result = t.sons[0] of tyArray: if t.sons[1].kind != tyEmpty: result = typeAllowedAux(marker, t.sons[1], skVar, flags) + elif kind in {skVar, skLet}: + result = t.sons[1] of tyRef: if kind == skConst: result = t else: result = typeAllowedAux(marker, t.lastSon, skVar, flags+{taHeap}) @@ -1170,17 +1192,19 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, if result != nil: break if result.isNil and t.n != nil: result = typeAllowedNode(marker, t.n, kind, flags) - of tyProxy, tyEmpty: + of tyEmpty: + if kind in {skVar, skLet}: result = t + of tyProxy: # for now same as error node; we say it's a valid type as it should # prevent cascading errors: result = nil - of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("typeAllowedAux") + of tyUnused, tyOptAsRef: result = t -proc typeAllowed*(t: PType, kind: TSymKind): PType = +proc typeAllowed*(t: PType, kind: TSymKind; flags: TTypeAllowedFlags = {}): PType = # returns 'nil' on success and otherwise the part of the type that is # wrong! var marker = initIntSet() - result = typeAllowedAux(marker, t, kind, {}) + result = typeAllowedAux(marker, t, kind, flags) proc align(address, alignment: BiggestInt): BiggestInt = result = (address + (alignment - 1)) and not (alignment - 1) @@ -1263,7 +1287,8 @@ proc computeRecSizeAux(n: PNode, a, currOffset: var BiggestInt): BiggestInt = if res < 0: return res maxSize = max(maxSize, res) maxAlign = max(maxAlign, b) - else: internalError("computeRecSizeAux(record case branch)") + else: + return szIllegalRecursion currOffset = align(currOffset, maxAlign) + maxSize result = align(result, maxAlign) + maxSize a = maxAlign @@ -1322,7 +1347,10 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = if typ.callConv == ccClosure: result = 2 * ptrSize else: result = ptrSize a = ptrSize - of tyNil, tyCString, tyString, tySequence, tyPtr, tyRef, tyVar, tyOpenArray: + of tyString, tyNil: + result = ptrSize + a = result + of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent, tyOpenArray: let base = typ.lastSon if base == typ or (base.kind == tyTuple and base.size==szIllegalRecursion): result = szIllegalRecursion @@ -1421,7 +1449,8 @@ proc getReturnType*(s: PSym): PType = proc getSize*(typ: PType): BiggestInt = result = computeSize(typ) - if result < 0: internalError("getSize: " & $typ.kind) + #if result < 0: internalError("getSize: " & $typ.kind) + # XXX review all usages of 'getSize' proc containsGenericTypeIter(t: PType, closure: RootRef): bool = case t.kind @@ -1449,8 +1478,7 @@ proc baseOfDistinct*(t: PType): PType = while it.kind in {tyPtr, tyRef}: parent = it it = it.lastSon - if it.kind == tyDistinct: - internalAssert parent != nil + if it.kind == tyDistinct and parent != nil: parent.sons[0] = it.sons[0] proc safeInheritanceDiff*(a, b: PType): int = @@ -1482,8 +1510,9 @@ type proc compatibleEffects*(formal, actual: PType): EffectsCompat = # for proc type compatibility checking: assert formal.kind == tyProc and actual.kind == tyProc - internalAssert formal.n.sons[0].kind == nkEffectList - internalAssert actual.n.sons[0].kind == nkEffectList + if formal.n.sons[0].kind != nkEffectList or + actual.n.sons[0].kind != nkEffectList: + return efTagsUnknown var spec = formal.n.sons[0] if spec.len != 0: @@ -1608,14 +1637,14 @@ proc skipHiddenSubConv*(n: PNode): PNode = else: result = n -proc typeMismatch*(info: TLineInfo, formal, actual: PType) = +proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType) = if formal.kind != tyError and actual.kind != tyError: let named = typeToString(formal) let desc = typeToString(formal, preferDesc) let x = if named == desc: named else: named & " = " & desc - var msg = msgKindToString(errTypeMismatch) & - typeToString(actual) & ") " & - msgKindToString(errButExpectedX) % [x] + var msg = "type mismatch: got <" & + typeToString(actual) & "> " & + "but expected '" & x & "'" if formal.kind == tyProc and actual.kind == tyProc: case compatibleEffects(formal, actual) @@ -1630,4 +1659,4 @@ proc typeMismatch*(info: TLineInfo, formal, actual: PType) = msg.add "\n.tag effect is 'any tag allowed'" of efLockLevelsDiffer: msg.add "\nlock levels differ" - localError(info, errGenerated, msg) + localError(conf, info, msg) diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim index 4a9b8d1a9..4d75d5d05 100644 --- a/compiler/typesrenderer.nim +++ b/compiler/typesrenderer.nim @@ -17,7 +17,6 @@ proc renderPlainSymbolName*(n: PNode): string = ## Use this on documentation name nodes to extract the *raw* symbol name, ## without decorations, parameters, or anything. That can be used as the base ## for the HTML hyperlinks. - result = "" case n.kind of nkPostfix, nkAccQuoted: result = renderPlainSymbolName(n[n.len-1]) @@ -28,7 +27,8 @@ proc renderPlainSymbolName*(n: PNode): string = of nkPragmaExpr: result = renderPlainSymbolName(n[0]) else: - internalError(n.info, "renderPlainSymbolName() with " & $n.kind) + result = "" + #internalError(n.info, "renderPlainSymbolName() with " & $n.kind) assert(not result.isNil) proc renderType(n: PNode): string = @@ -105,7 +105,8 @@ proc renderParamTypes(found: var seq[string], n: PNode) = for i in 0 ..< typePos: found.add(typeStr) else: - internalError(n.info, "renderParamTypes(found,n) with " & $n.kind) + found.add($n) + #internalError(n.info, "renderParamTypes(found,n) with " & $n.kind) proc renderParamTypes*(n: PNode, sep = defaultParamSeparator): string = ## Returns the types contained in `n` joined by `sep`. diff --git a/compiler/vm.nim b/compiler/vm.nim index 5c9a982ab..cbd304caa 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -19,7 +19,7 @@ import ast except getstr import strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, parser, vmdeps, idents, trees, renderer, options, transf, parseutils, - vmmarshal, gorgeimpl + vmmarshal, gorgeimpl, configuration from semfold import leValueConv, ordinalValToString from evaltempl import evalTemplate @@ -61,7 +61,7 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) = while x != nil: inc calls x = x.next - msgWriteln($calls & " calls omitted\n") + msgWriteln(c.config, $calls & " calls omitted\n") return stackTraceAux(c, x.next, x.comesFrom, recursionLimit-1) var info = c.debug[pc] @@ -78,19 +78,19 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) = if x.prc != nil: for k in 1..max(1, 25-s.len): add(s, ' ') add(s, x.prc.name.s) - msgWriteln(s) + msgWriteln(c.config, s) proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, - msg: TMsgKind, arg = "", n: PNode = nil) = - msgWriteln("stack trace: (most recent call last)") + msg: string, n: PNode = nil) = + msgWriteln(c.config, "stack trace: (most recent call last)") stackTraceAux(c, tos, pc) # XXX test if we want 'globalError' for every mode let lineInfo = if n == nil: c.debug[pc] else: n.info - if c.mode == emRepl: globalError(lineInfo, msg, arg) - else: localError(lineInfo, msg, arg) + if c.mode == emRepl: globalError(c.config, lineInfo, msg) + else: localError(c.config, lineInfo, msg) proc bailOut(c: PCtx; tos: PStackFrame) = - stackTrace(c, tos, c.exceptionInstr, errUnhandledExceptionX, + stackTrace(c, tos, c.exceptionInstr, "unhandled exception: " & c.currentExceptionA.sons[3].skipColon.strVal) when not defined(nimComputedGoto): @@ -210,8 +210,14 @@ proc putIntoNode(n: var PNode; x: TFullReg) = of rkInt: n.intVal = x.intVal of rkFloat: n.floatVal = x.floatVal of rkNode: - if nfIsRef in x.node.flags: n = x.node - else: n[] = x.node[] + if nfIsRef in x.node.flags: + n = x.node + else: + let destIsRef = nfIsRef in n.flags + n[] = x.node[] + # Ref-ness must be kept for the destination + if destIsRef: + n.flags.incl nfIsRef of rkRegisterAddr: putIntoNode(n, x.regAddr[]) of rkNodeAddr: n[] = x.nodeAddr[][] @@ -308,7 +314,7 @@ proc cleanUpOnReturn(c: PCtx; f: PStackFrame): int = return pc return -1 -proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = +proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = if desttyp.kind == tyString: if dest.kind != rkNode: myreset(dest) @@ -323,7 +329,7 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = dest.node.strVal = if f.ast.isNil: f.name.s else: f.ast.strVal else: for i in 0..<n.len: - if n.sons[i].kind != nkSym: internalError("opConv for enum") + if n.sons[i].kind != nkSym: internalError(c.config, "opConv for enum") let f = n.sons[i].sym if f.position == x: dest.node.strVal = if f.ast.isNil: f.name.s else: f.ast.strVal @@ -353,7 +359,7 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = of tyChar: dest.node.strVal = $chr(src.intVal) else: - internalError("cannot convert to string " & desttyp.typeToString) + internalError(c.config, "cannot convert to string " & desttyp.typeToString) else: case skipTypes(desttyp, abstractRange).kind of tyInt..tyInt64: @@ -402,9 +408,9 @@ template handleJmpBack() {.dirty.} = if allowInfiniteLoops in c.features: c.loopIterations = MaxLoopIterations else: - msgWriteln("stack trace: (most recent call last)") + msgWriteln(c.config, "stack trace: (most recent call last)") stackTraceAux(c, tos, pc) - globalError(c.debug[pc], errTooManyIterations) + globalError(c.config, c.debug[pc], errTooManyIterations) dec(c.loopIterations) proc recSetFlagIsRef(arg: PNode) = @@ -418,14 +424,14 @@ proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) = let typ = node.typ.skipTypes(abstractInst+{tyRange}-{tyTypeDesc}) let typeEntry = typ.sons[0].skipTypes(abstractInst+{tyRange}-{tyTypeDesc}) let typeKind = case typeEntry.kind - of tyUInt..tyUInt64: nkUIntLit - of tyRange, tyEnum, tyBool, tyChar, tyInt..tyInt64: nkIntLit - of tyFloat..tyFloat128: nkFloatLit - of tyString: nkStrLit - of tyObject: nkObjConstr - of tySequence: nkNilLit - of tyProc, tyTuple: nkPar - else: nkEmpty + of tyUInt..tyUInt64: nkUIntLit + of tyRange, tyEnum, tyBool, tyChar, tyInt..tyInt64: nkIntLit + of tyFloat..tyFloat128: nkFloatLit + of tyString: nkStrLit + of tyObject: nkObjConstr + of tySequence: nkNilLit + of tyProc, tyTuple: nkTupleConstr + else: nkEmpty let oldLen = node.len setLen(node.sons, newLen) @@ -434,6 +440,17 @@ proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) = for i in oldLen ..< newLen: node.sons[i] = newNodeI(typeKind, info) +const + errIndexOutOfBounds = "index out of bounds" + errNilAccess = "attempt to access a nil address" + errOverOrUnderflow = "over- or underflow" + errConstantDivisionByZero = "division by zero" + errIllegalConvFromXtoY = "illegal conversion from '$1' to '$2'" + errTooManyIterations = "interpretation requires too many iterations; " & + "if you are sure this is not a bug in your code edit " & + "compiler/vmdef.MaxLoopIterations and rebuild the compiler" + errFieldXNotFound = "node lacks field: " + proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var pc = start var tos = tos @@ -447,7 +464,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = #if c.traceActive: when traceCode: echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra, " rb ", instr.regB, " rc ", instr.regC - # message(c.debug[pc], warnUser, "Trace") + # message(c.config, c.debug[pc], warnUser, "Trace") case instr.opcode of opcEof: return regs[ra] @@ -580,7 +597,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if regs[rb].kind == rkNode: regs[ra].nodeAddr = addr(regs[rb].node) else: - stackTrace(c, tos, pc, errGenerated, "limited VM support for 'addr'") + stackTrace(c, tos, pc, "limited VM support for 'addr'") of opcLdDeref: # a = b[] let ra = instr.regA @@ -623,7 +640,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = stackTrace(c, tos, pc, errOverOrUnderflow) of opcAddImmInt: decodeBImm(rkInt) - #message(c.debug[pc], warnUser, "came here") + #message(c.config, c.debug[pc], warnUser, "came here") #debug regs[rb].node let bVal = regs[rb].intVal @@ -779,9 +796,21 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].intVal = ord(regs[rb].intVal <% regs[rc].intVal) of opcEqRef: decodeBC(rkInt) - regs[ra].intVal = ord((regs[rb].node.kind == nkNilLit and - regs[rc].node.kind == nkNilLit) or - regs[rb].node == regs[rc].node) + if regs[rb].kind == rkNodeAddr: + if regs[rc].kind == rkNodeAddr: + regs[ra].intVal = ord(regs[rb].nodeAddr == regs[rc].nodeAddr) + else: + assert regs[rc].kind == rkNode + # we know these cannot be equal + regs[ra].intVal = ord(false) + elif regs[rc].kind == rkNodeAddr: + assert regs[rb].kind == rkNode + # we know these cannot be equal + regs[ra].intVal = ord(false) + else: + regs[ra].intVal = ord((regs[rb].node.kind == nkNilLit and + regs[rc].node.kind == nkNilLit) or + regs[rb].node == regs[rc].node) of opcEqNimrodNode: decodeBC(rkInt) regs[ra].intVal = @@ -880,17 +909,17 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node = if a.sym.ast.isNil: newNode(nkNilLit) else: copyTree(a.sym.ast) else: - stackTrace(c, tos, pc, errFieldXNotFound, "symbol") + stackTrace(c, tos, pc, "node is not a symbol") of opcEcho: let rb = instr.regB if rb == 1: - msgWriteln(regs[ra].node.strVal, {msgStdout}) + msgWriteln(c.config, regs[ra].node.strVal, {msgStdout}) else: var outp = "" for i in ra..ra+rb-1: #if regs[i].kind != rkNode: debug regs[i] outp.add(regs[i].node.strVal) - msgWriteln(outp, {msgStdout}) + msgWriteln(c.config, outp, {msgStdout}) of opcContainsSet: decodeBC(rkInt) regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].regToNode)) @@ -919,15 +948,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let rc = instr.regC if not (leValueConv(regs[rb].regToNode, regs[ra].regToNode) and leValueConv(regs[ra].regToNode, regs[rc].regToNode)): - stackTrace(c, tos, pc, errGenerated, - msgKindToString(errIllegalConvFromXtoY) % [ - $regs[ra].regToNode, "[" & $regs[rb].regToNode & ".." & $regs[rc].regToNode & "]"]) + stackTrace(c, tos, pc, + errIllegalConvFromXtoY % [ + $regs[ra].regToNode, "[" & $regs[rb].regToNode & ".." & $regs[rc].regToNode & "]"]) of opcIndCall, opcIndCallAsgn: # dest = call regStart, n; where regStart = fn, arg1, ... let rb = instr.regB let rc = instr.regC let bb = regs[rb].node - let isClosure = bb.kind == nkPar + let isClosure = bb.kind == nkTupleConstr let prc = if not isClosure: bb.sym else: bb.sons[0].sym if prc.offset < -1: # it's a callback: @@ -937,20 +966,20 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = currentLineInfo: c.debug[pc])) elif sfImportc in prc.flags: if allowFFI notin c.features: - globalError(c.debug[pc], errGenerated, "VM not allowed to do FFI") + globalError(c.config, c.debug[pc], "VM not allowed to do FFI") # we pass 'tos.slots' instead of 'regs' so that the compiler can keep # 'regs' in a register: when hasFFI: let prcValue = c.globals.sons[prc.position-1] if prcValue.kind == nkEmpty: - globalError(c.debug[pc], errGenerated, "canot run " & prc.name.s) + globalError(c.config, c.debug[pc], "canot run " & prc.name.s) let newValue = callForeignFunction(prcValue, prc.typ, tos.slots, rb+1, rc-1, c.debug[pc]) if newValue.kind != nkEmpty: assert instr.opcode == opcIndCallAsgn putIntoReg(regs[ra], newValue) else: - globalError(c.debug[pc], errGenerated, "VM not built with FFI support") + globalError(c.config, c.debug[pc], "VM not built with FFI support") elif prc.kind != skTemplate: let newPc = compile(c, prc) # tricky: a recursion is also a jump back, so we use the same @@ -960,7 +989,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos) newSeq(newFrame.slots, prc.offset+ord(isClosure)) if not isEmptyType(prc.typ.sons[0]) or prc.kind == skMacro: - putIntoReg(newFrame.slots[0], getNullValue(prc.typ.sons[0], prc.info)) + putIntoReg(newFrame.slots[0], getNullValue(prc.typ.sons[0], prc.info, c.config)) for i in 1 .. rc-1: newFrame.slots[i] = regs[rb+i] if isClosure: @@ -982,7 +1011,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let node = regs[rb+i].regToNode node.info = c.debug[pc] macroCall.add(node) - var a = evalTemplate(macroCall, prc, genSymOwner) + var a = evalTemplate(macroCall, prc, genSymOwner, c.config) if a.kind == nkStmtList and a.len == 1: a = a[0] a.recSetFlagIsRef ensureKind(rkNode) @@ -1067,7 +1096,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNew: ensureKind(rkNode) let typ = c.types[instr.regBx - wordExcess] - regs[ra].node = getNullValue(typ, c.debug[pc]) + regs[ra].node = getNullValue(typ, c.debug[pc], c.config) regs[ra].node.flags.incl nfIsRef of opcNewSeq: let typ = c.types[instr.regBx - wordExcess] @@ -1079,7 +1108,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.typ = typ newSeq(regs[ra].node.sons, count) for i in 0 ..< count: - regs[ra].node.sons[i] = getNullValue(typ.sons[0], c.debug[pc]) + regs[ra].node.sons[i] = getNullValue(typ.sons[0], c.debug[pc], c.config) of opcNewStr: decodeB(rkNode) regs[ra].node = newNodeI(nkStrLit, c.debug[pc]) @@ -1091,7 +1120,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcLdNull: ensureKind(rkNode) let typ = c.types[instr.regBx - wordExcess] - regs[ra].node = getNullValue(typ, c.debug[pc]) + regs[ra].node = getNullValue(typ, c.debug[pc], c.config) # opcLdNull really is the gist of the VM's problems: should it load # a fresh null to regs[ra].node or to regs[ra].node[]? This really # depends on whether regs[ra] represents the variable itself or wether @@ -1138,7 +1167,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.strVal = renderTree(regs[rb].regToNode, {renderNoComments, renderDocComments}) of opcQuit: if c.mode in {emRepl, emStaticExpr, emStaticStmt}: - message(c.debug[pc], hintQuitCalled) + message(c.config, c.debug[pc], hintQuitCalled) msgQuit(int8(getOrdValue(regs[ra].regToNode))) else: return TFullReg(kind: rkNone) @@ -1164,14 +1193,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess) else: c.setLenSeq(regs[ra].node, newLen, c.debug[pc]) of opcReset: - internalError(c.debug[pc], "too implement") + internalError(c.config, c.debug[pc], "too implement") of opcNarrowS: decodeB(rkInt) let min = -(1.BiggestInt shl (rb-1)) let max = (1.BiggestInt shl (rb-1))-1 if regs[ra].intVal < min or regs[ra].intVal > max: - stackTrace(c, tos, pc, errGenerated, - msgKindToString(errUnhandledExceptionX) % "value out of range") + stackTrace(c, tos, pc, "unhandled exception: value out of range") of opcNarrowU: decodeB(rkInt) regs[ra].intVal = regs[ra].intVal and ((1'i64 shl rb)-1) @@ -1205,7 +1233,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if u.kind notin {nkEmpty..nkNilLit}: u.add(regs[rc].node) else: - stackTrace(c, tos, pc, errGenerated, "cannot add to node kind: " & $u.kind) + stackTrace(c, tos, pc, "cannot add to node kind: " & $u.kind) regs[ra].node = u of opcNAddMultiple: decodeBC(rkNode) @@ -1215,38 +1243,46 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = # 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) + stackTrace(c, tos, pc, "cannot add to node kind: " & $u.kind) regs[ra].node = u of opcNKind: decodeB(rkInt) regs[ra].intVal = ord(regs[rb].node.kind) c.comesFromHeuristic = regs[rb].node.info + of opcNSymKind: + decodeB(rkInt) + let a = regs[rb].node + if a.kind == nkSym: + regs[ra].intVal = ord(a.sym.kind) + else: + stackTrace(c, tos, pc, "node is not a symbol") + c.comesFromHeuristic = regs[rb].node.info of opcNIntVal: decodeB(rkInt) let a = regs[rb].node case a.kind of nkCharLit..nkUInt64Lit: regs[ra].intVal = a.intVal - else: stackTrace(c, tos, pc, errFieldXNotFound, "intVal") + else: stackTrace(c, tos, pc, errFieldXNotFound & "intVal") of opcNFloatVal: decodeB(rkFloat) let a = regs[rb].node case a.kind of nkFloatLit..nkFloat64Lit: regs[ra].floatVal = a.floatVal - else: stackTrace(c, tos, pc, errFieldXNotFound, "floatVal") + else: stackTrace(c, tos, pc, errFieldXNotFound & "floatVal") of opcNSymbol: decodeB(rkNode) let a = regs[rb].node if a.kind == nkSym: regs[ra].node = copyNode(a) else: - stackTrace(c, tos, pc, errFieldXNotFound, "symbol") + stackTrace(c, tos, pc, errFieldXNotFound & "symbol") of opcNIdent: decodeB(rkNode) let a = regs[rb].node if a.kind == nkIdent: regs[ra].node = copyNode(a) else: - stackTrace(c, tos, pc, errFieldXNotFound, "ident") + stackTrace(c, tos, pc, errFieldXNotFound & "ident") of opcNGetType: let rb = instr.regB let rc = instr.regC @@ -1257,40 +1293,48 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = 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") + stackTrace(c, tos, pc, "node has no type") of 1: # 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") + # stackTrace(c, tos, pc, "node has no type") of 2: # getTypeInst opcode: ensureKind(rkNode) if regs[rb].kind == rkNode and regs[rb].node.typ != nil: regs[ra].node = opMapTypeInstToAst(regs[rb].node.typ, c.debug[pc]) else: - stackTrace(c, tos, pc, errGenerated, "node has no type") + stackTrace(c, tos, pc, "node has no type") else: # getTypeImpl opcode: ensureKind(rkNode) if regs[rb].kind == rkNode and regs[rb].node.typ != nil: regs[ra].node = opMapTypeImplToAst(regs[rb].node.typ, c.debug[pc]) else: - stackTrace(c, tos, pc, errGenerated, "node has no type") + stackTrace(c, tos, pc, "node has no type") of opcNStrVal: decodeB(rkNode) createStr regs[ra] let a = regs[rb].node - if a.kind in {nkStrLit..nkTripleStrLit}: regs[ra].node.strVal = a.strVal - elif a.kind == nkCommentStmt: regs[ra].node.strVal = a.comment - else: stackTrace(c, tos, pc, errFieldXNotFound, "strVal") + case a.kind + of {nkStrLit..nkTripleStrLit}: + regs[ra].node.strVal = a.strVal + of nkCommentStmt: + regs[ra].node.strVal = a.comment + of nkIdent: + regs[ra].node.strVal = a.ident.s + of nkSym: + regs[ra].node.strVal = a.sym.name.s + else: + stackTrace(c, tos, pc, errFieldXNotFound & "strVal") of opcSlurp: decodeB(rkNode) createStr regs[ra] regs[ra].node.strVal = opSlurp(regs[rb].node.strVal, c.debug[pc], - c.module) + c.module, c.config) of opcGorge: decodeBC(rkNode) inc pc @@ -1299,39 +1343,40 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = createStr regs[ra] regs[ra].node.strVal = opGorge(regs[rb].node.strVal, regs[rc].node.strVal, regs[rd].node.strVal, - c.debug[pc])[0] + c.debug[pc], c.config)[0] of opcNError: decodeB(rkNode) let a = regs[ra].node let b = regs[rb].node - stackTrace(c, tos, pc, errUser, a.strVal, if b.kind == nkNilLit: nil else: b) + stackTrace(c, tos, pc, a.strVal, if b.kind == nkNilLit: nil else: b) of opcNWarning: - message(c.debug[pc], warnUser, regs[ra].node.strVal) + message(c.config, c.debug[pc], warnUser, regs[ra].node.strVal) of opcNHint: - message(c.debug[pc], hintUser, regs[ra].node.strVal) + message(c.config, c.debug[pc], hintUser, regs[ra].node.strVal) of opcParseExprToAst: decodeB(rkNode) # c.debug[pc].line.int - countLines(regs[rb].strVal) ? var error: string - let ast = parseString(regs[rb].node.strVal, c.cache, c.debug[pc].toFullPath, - c.debug[pc].line.int, - proc (info: TLineInfo; msg: TMsgKind; arg: string) = - if error.isNil and msg <= msgs.errMax: - error = formatMsg(info, msg, arg)) + let ast = parseString(regs[rb].node.strVal, c.cache, c.config, + c.debug[pc].toFullPath, c.debug[pc].line.int, + proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) = + if error.isNil and msg <= errMax: + error = formatMsg(conf, info, msg, arg)) if not error.isNil: c.errorFlag = error elif sonsLen(ast) != 1: - c.errorFlag = formatMsg(c.debug[pc], errExprExpected, "multiple statements") + c.errorFlag = formatMsg(c.config, c.debug[pc], errGenerated, + "expected expression, but got multiple statements") else: regs[ra].node = ast.sons[0] of opcParseStmtToAst: decodeB(rkNode) var error: string - let ast = parseString(regs[rb].node.strVal, c.cache, c.debug[pc].toFullPath, - c.debug[pc].line.int, - proc (info: TLineInfo; msg: TMsgKind; arg: string) = - if error.isNil and msg <= msgs.errMax: - error = formatMsg(info, msg, arg)) + let ast = parseString(regs[rb].node.strVal, c.cache, c.config, + c.debug[pc].toFullPath, c.debug[pc].line.int, + proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) = + if error.isNil and msg <= errMax: + error = formatMsg(conf, info, msg, arg)) if not error.isNil: c.errorFlag = error else: @@ -1343,7 +1388,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcCallSite: ensureKind(rkNode) if c.callsite != nil: regs[ra].node = c.callsite - else: stackTrace(c, tos, pc, errFieldXNotFound, "callsite") + else: stackTrace(c, tos, pc, errFieldXNotFound & "callsite") of opcNGetFile: decodeB(rkNode) let n = regs[rb].node @@ -1353,7 +1398,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNGetLine: decodeB(rkNode) let n = regs[rb].node - regs[ra].node = newIntNode(nkIntLit, n.info.line) + regs[ra].node = newIntNode(nkIntLit, n.info.line.int) regs[ra].node.info = n.info regs[ra].node.typ = n.typ of opcNGetColumn: @@ -1364,31 +1409,54 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.typ = n.typ of opcEqIdent: decodeBC(rkInt) - if regs[rb].node.kind == nkIdent and regs[rc].node.kind == nkIdent: - regs[ra].intVal = ord(regs[rb].node.ident.id == regs[rc].node.ident.id) + # aliases for shorter and easier to understand code below + let aNode = regs[rb].node + let bNode = regs[rc].node + # these are cstring to prevent string copy, and cmpIgnoreStyle from + # takes cstring arguments + var aStrVal: cstring = nil + var bStrVal: cstring = nil + # extract strVal from argument ``a`` + case aNode.kind + of {nkStrLit..nkTripleStrLit}: + aStrVal = aNode.strVal.cstring + of nkIdent: + aStrVal = aNode.ident.s.cstring + of nkSym: + aStrVal = aNode.sym.name.s.cstring + of nkOpenSymChoice, nkClosedSymChoice: + aStrVal = aNode[0].sym.name.s.cstring else: - regs[ra].intVal = 0 + discard + # extract strVal from argument ``b`` + case bNode.kind + of {nkStrLit..nkTripleStrLit}: + bStrVal = bNode.strVal.cstring + of nkIdent: + bStrVal = bNode.ident.s.cstring + of nkSym: + bStrVal = bNode.sym.name.s.cstring + of nkOpenSymChoice, nkClosedSymChoice: + bStrVal = bNode[0].sym.name.s.cstring + else: + discard + # set result + regs[ra].intVal = + if aStrVal != nil and bStrVal != nil: + ord(idents.cmpIgnoreStyle(aStrVal,bStrVal,high(int)) == 0) + else: + 0 + of opcStrToIdent: decodeB(rkNode) if regs[rb].node.kind notin {nkStrLit..nkTripleStrLit}: - stackTrace(c, tos, pc, errFieldXNotFound, "strVal") + stackTrace(c, tos, pc, errFieldXNotFound & "strVal") else: regs[ra].node = newNodeI(nkIdent, c.debug[pc]) regs[ra].node.ident = getIdent(regs[rb].node.strVal) - of opcIdentToStr: - decodeB(rkNode) - let a = regs[rb].node - createStr regs[ra] - regs[ra].node.info = c.debug[pc] - if a.kind == nkSym: - regs[ra].node.strVal = a.sym.name.s - elif a.kind == nkIdent: - regs[ra].node.strVal = a.ident.s - else: - stackTrace(c, tos, pc, errFieldXNotFound, "ident") of opcSetType: if regs[ra].kind != rkNode: - internalError(c.debug[pc], "cannot set type") + internalError(c.config, c.debug[pc], "cannot set type") regs[ra].node.typ = c.types[instr.regBx - wordExcess] of opcConv: let rb = instr.regB @@ -1397,9 +1465,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = inc pc let srctyp = c.types[c.code[pc].regBx - wordExcess] - if opConv(regs[ra], regs[rb], desttyp, srctyp): - stackTrace(c, tos, pc, errGenerated, - msgKindToString(errIllegalConvFromXtoY) % [ + if opConv(c, regs[ra], regs[rb], desttyp, srctyp): + stackTrace(c, tos, pc, + errIllegalConvFromXtoY % [ typeToString(srctyp), typeToString(desttyp)]) of opcCast: let rb = instr.regB @@ -1412,7 +1480,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let dest = fficast(regs[rb], desttyp) asgnRef(regs[ra], dest) else: - globalError(c.debug[pc], "cannot evaluate cast") + globalError(c.config, c.debug[pc], "cannot evaluate cast") of opcNSetIntVal: decodeB(rkNode) var dest = regs[ra].node @@ -1420,7 +1488,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[rb].kind in {rkInt}: dest.intVal = regs[rb].intVal else: - stackTrace(c, tos, pc, errFieldXNotFound, "intVal") + stackTrace(c, tos, pc, errFieldXNotFound & "intVal") of opcNSetFloatVal: decodeB(rkNode) var dest = regs[ra].node @@ -1428,26 +1496,26 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[rb].kind in {rkFloat}: dest.floatVal = regs[rb].floatVal else: - stackTrace(c, tos, pc, errFieldXNotFound, "floatVal") + stackTrace(c, tos, pc, errFieldXNotFound & "floatVal") of opcNSetSymbol: decodeB(rkNode) var dest = regs[ra].node if dest.kind == nkSym and regs[rb].node.kind == nkSym: dest.sym = regs[rb].node.sym else: - stackTrace(c, tos, pc, errFieldXNotFound, "symbol") + stackTrace(c, tos, pc, errFieldXNotFound & "symbol") of opcNSetIdent: decodeB(rkNode) var dest = regs[ra].node if dest.kind == nkIdent and regs[rb].node.kind == nkIdent: dest.ident = regs[rb].node.ident else: - stackTrace(c, tos, pc, errFieldXNotFound, "ident") + stackTrace(c, tos, pc, errFieldXNotFound & "ident") of opcNSetType: decodeB(rkNode) let b = regs[rb].node - internalAssert b.kind == nkSym and b.sym.kind == skType - internalAssert regs[ra].node != nil + internalAssert c.config, b.kind == nkSym and b.sym.kind == skType + internalAssert c.config, regs[ra].node != nil regs[ra].node.typ = b.sym.typ of opcNSetStrVal: decodeB(rkNode) @@ -1458,25 +1526,28 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = elif dest.kind == nkCommentStmt and regs[rb].kind in {rkNode}: dest.comment = regs[rb].node.strVal else: - stackTrace(c, tos, pc, errFieldXNotFound, "strVal") + stackTrace(c, tos, pc, errFieldXNotFound & "strVal") of opcNNewNimNode: decodeBC(rkNode) var k = regs[rb].intVal if k < 0 or k > ord(high(TNodeKind)): - internalError(c.debug[pc], + internalError(c.config, c.debug[pc], "request to create a NimNode of invalid kind") let cc = regs[rc].node - regs[ra].node = newNodeI(TNodeKind(int(k)), + let x = newNodeI(TNodeKind(int(k)), if cc.kind != nkNilLit: cc.info - elif c.comesFromHeuristic.line > -1: + elif c.comesFromHeuristic.line != 0'u16: c.comesFromHeuristic elif c.callsite != nil and c.callsite.safeLen > 1: c.callsite[1].info else: c.debug[pc]) - regs[ra].node.flags.incl nfIsRef + x.flags.incl nfIsRef + # prevent crashes in the compiler resulting from wrong macros: + if x.kind == nkIdent: x.ident = c.cache.emptyIdent + regs[ra].node = x of opcNCopyNimNode: decodeB(rkNode) regs[ra].node = copyNode(regs[rb].node) @@ -1494,7 +1565,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let name = if regs[rc].node.strVal.len == 0: ":tmp" else: regs[rc].node.strVal if k < 0 or k > ord(high(TSymKind)): - internalError(c.debug[pc], "request to create symbol of invalid kind") + internalError(c.config, c.debug[pc], "request to create symbol of invalid kind") var sym = newSym(k.TSymKind, name.getIdent, c.module.owner, c.debug[pc]) incl(sym.flags, sfGenSym) regs[ra].node = newSymNode(sym) @@ -1503,7 +1574,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = # type trait operation decodeB(rkNode) var typ = regs[rb].node.typ - internalAssert typ != nil + internalAssert c.config, typ != nil while typ.kind == tyTypeDesc and typ.len > 0: typ = typ.sons[0] createStr regs[ra] regs[ra].node.strVal = typ.typeToString(preferExported) @@ -1512,14 +1583,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let rb = instr.regB inc pc let typ = c.types[c.code[pc].regBx - wordExcess] - putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ)) + putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ, c.config)) 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) + storeAny(regs[ra].node.strVal, typ, regs[rb].regToNode, c.config) of opcToNarrowInt: decodeBC(rkInt) let mask = (1'i64 shl rc) - 1 # 0xFF @@ -1541,7 +1612,7 @@ proc execute(c: PCtx, start: int): PNode = proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode = if sym.kind in routineKinds: if sym.typ.len-1 != args.len: - localError(sym.info, + localError(c.config, sym.info, "NimScript: expected $# arguments, but got $#" % [ $(sym.typ.len-1), $args.len]) else: @@ -1553,18 +1624,18 @@ proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode = # setup parameters: if not isEmptyType(sym.typ.sons[0]) or sym.kind == skMacro: - putIntoReg(tos.slots[0], getNullValue(sym.typ.sons[0], sym.info)) + putIntoReg(tos.slots[0], getNullValue(sym.typ.sons[0], sym.info, c.config)) # XXX We could perform some type checking here. for i in 1..<sym.typ.len: putIntoReg(tos.slots[i], args[i-1]) result = rawExecute(c, start, tos).regToNode else: - localError(sym.info, + localError(c.config, sym.info, "NimScript: attempt to call non-routine: " & sym.name.s) proc evalStmt*(c: PCtx, n: PNode) = - let n = transformExpr(c.module, n) + let n = transformExpr(c.graph, c.module, n) let start = genStmt(c, n) # execute new instructions; this redundant opcEof check saves us lots # of allocations in 'execute': @@ -1572,13 +1643,13 @@ proc evalStmt*(c: PCtx, n: PNode) = discard execute(c, start) proc evalExpr*(c: PCtx, n: PNode): PNode = - let n = transformExpr(c.module, n) + let n = transformExpr(c.graph, c.module, n) let start = genExpr(c, n) assert c.code[start].opcode != opcEof result = execute(c, start) proc getGlobalValue*(c: PCtx; s: PSym): PNode = - internalAssert s.kind in {skLet, skVar} and sfGlobal in s.flags + internalAssert c.config, s.kind in {skLet, skVar} and sfGlobal in s.flags result = c.globals.sons[s.position-1] include vmops @@ -1589,9 +1660,9 @@ include vmops var globalCtx*: PCtx -proc setupGlobalCtx(module: PSym; cache: IdentCache) = +proc setupGlobalCtx(module: PSym; cache: IdentCache; graph: ModuleGraph) = if globalCtx.isNil: - globalCtx = newCtx(module, cache) + globalCtx = newCtx(module, cache, graph) registerAdditionalOps(globalCtx) else: refresh(globalCtx, module) @@ -1602,31 +1673,31 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = #pushStackFrame(c, newStackFrame()) # XXX produce a new 'globals' environment here: - setupGlobalCtx(module, cache) + setupGlobalCtx(module, cache, graph) result = globalCtx when hasFFI: globalCtx.features = {allowFFI, allowCast} -var oldErrorCount: int - proc myProcess(c: PPassContext, n: PNode): PNode = + let c = PCtx(c) # don't eval errornous code: - if oldErrorCount == msgs.gErrorCounter: - evalStmt(PCtx(c), n) + if c.oldErrorCount == c.config.errorCounter: + evalStmt(c, n) result = emptyNode else: result = n - oldErrorCount = msgs.gErrorCounter + c.oldErrorCount = c.config.errorCounter proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode = myProcess(c, n) const evalPass* = makePass(myOpen, nil, myProcess, myClose) -proc evalConstExprAux(module: PSym; cache: IdentCache; prc: PSym, n: PNode, +proc evalConstExprAux(module: PSym; cache: IdentCache; + g: ModuleGraph; prc: PSym, n: PNode, mode: TEvalMode): PNode = - let n = transformExpr(module, n) - setupGlobalCtx(module, cache) + let n = transformExpr(g, module, n) + setupGlobalCtx(module, cache, g) var c = globalCtx let oldMode = c.mode defer: c.mode = oldMode @@ -1639,19 +1710,19 @@ proc evalConstExprAux(module: PSym; cache: IdentCache; prc: PSym, n: PNode, newSeq(tos.slots, c.prc.maxSlots) #for i in 0 ..< c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty) result = rawExecute(c, start, tos).regToNode - if result.info.line < 0: result.info = n.info + if result.info.col < 0: result.info = n.info -proc evalConstExpr*(module: PSym; cache: IdentCache, e: PNode): PNode = - result = evalConstExprAux(module, cache, nil, e, emConst) +proc evalConstExpr*(module: PSym; cache: IdentCache, g: ModuleGraph; e: PNode): PNode = + result = evalConstExprAux(module, cache, g, nil, e, emConst) -proc evalStaticExpr*(module: PSym; cache: IdentCache, e: PNode, prc: PSym): PNode = - result = evalConstExprAux(module, cache, prc, e, emStaticExpr) +proc evalStaticExpr*(module: PSym; cache: IdentCache, g: ModuleGraph; e: PNode, prc: PSym): PNode = + result = evalConstExprAux(module, cache, g, prc, e, emStaticExpr) -proc evalStaticStmt*(module: PSym; cache: IdentCache, e: PNode, prc: PSym) = - discard evalConstExprAux(module, cache, prc, e, emStaticStmt) +proc evalStaticStmt*(module: PSym; cache: IdentCache, g: ModuleGraph; e: PNode, prc: PSym) = + discard evalConstExprAux(module, cache, g, prc, e, emStaticStmt) -proc setupCompileTimeVar*(module: PSym; cache: IdentCache, n: PNode) = - discard evalConstExprAux(module, cache, nil, n, emStaticStmt) +proc setupCompileTimeVar*(module: PSym; cache: IdentCache, g: ModuleGraph; n: PNode) = + discard evalConstExprAux(module, cache, g, nil, n, emStaticStmt) proc setupMacroParam(x: PNode, typ: PType): TFullReg = case typ.kind @@ -1675,24 +1746,26 @@ iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) = let posInCall = macroSym.typ.len + i yield (genericParam, call[posInCall]) +# to prevent endless recursion in macro instantiation +const evalMacroLimit = 1000 var evalMacroCounter: int -proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode, - sym: PSym): PNode = +proc evalMacroCall*(module: PSym; cache: IdentCache; g: ModuleGraph; + n, nOrig: PNode, sym: PSym): PNode = # XXX globalError() is ugly here, but I don't know a better solution for now inc(evalMacroCounter) - if evalMacroCounter > 100: - globalError(n.info, errTemplateInstantiationTooNested) + if evalMacroCounter > evalMacroLimit: + globalError(g.config, n.info, "macro instantiation too nested") # immediate macros can bypass any type and arity checking so we check the # arity here too: if sym.typ.len > n.safeLen and sym.typ.len > 1: - globalError(n.info, "in call '$#' got $#, but expected $# argument(s)" % [ + globalError(g.config, n.info, "in call '$#' got $#, but expected $# argument(s)" % [ n.renderTree, $(n.safeLen-1), $(sym.typ.len-1)]) - setupGlobalCtx(module, cache) + setupGlobalCtx(module, cache, g) var c = globalCtx - c.comesFromHeuristic.line = -1 + c.comesFromHeuristic.line = 0'u16 c.callsite = nOrig let start = genProc(c, sym) @@ -1724,16 +1797,16 @@ proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode, else: dec(evalMacroCounter) c.callsite = nil - localError(n.info, "expected " & $gp.len & + localError(c.config, n.info, "expected " & $gp.len & " generic parameter(s)") elif gp[i].sym.typ.kind in {tyStatic, tyTypeDesc}: dec(evalMacroCounter) c.callsite = nil - globalError(n.info, "static[T] or typedesc nor supported for .immediate macros") + globalError(c.config, n.info, "static[T] or typedesc nor supported for .immediate macros") # temporary storage: #for i in L ..< maxSlots: tos.slots[i] = newNode(nkEmpty) 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(c.config, n.info, "macro produced a cyclic tree") dec(evalMacroCounter) c.callsite = nil diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 5395d4bad..e10a62006 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -10,13 +10,13 @@ ## This module contains the type definitions for the new evaluation engine. ## An instruction is 1-3 int32s in memory, it is a register based VM. -import ast, passes, msgs, idents, intsets +import ast, passes, msgs, idents, intsets, options, modulegraphs const byteExcess* = 128 # we use excess-K for immediates wordExcess* = 32768 - MaxLoopIterations* = 1500_000 # max iterations of all loops + MaxLoopIterations* = 1_000_000_000 # max iterations of all loops type @@ -79,6 +79,7 @@ type opcNAdd, opcNAddMultiple, opcNKind, + opcNSymKind, opcNIntVal, opcNFloatVal, opcNSymbol, @@ -101,7 +102,6 @@ type opcNGetLine, opcNGetColumn, opcNGetFile, opcEqIdent, opcStrToIdent, - opcIdentToStr, opcGetImpl, opcEcho, @@ -206,17 +206,20 @@ type callbacks*: seq[tuple[key: string, value: VmCallback]] errorFlag*: string cache*: IdentCache + config*: ConfigRef + graph*: ModuleGraph + oldErrorCount*: int TPosition* = distinct int PEvalContext* = PCtx -proc newCtx*(module: PSym; cache: IdentCache): PCtx = +proc newCtx*(module: PSym; cache: IdentCache; g: ModuleGraph): PCtx = PCtx(code: @[], debug: @[], globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[], prc: PProc(blocks: @[]), module: module, loopIterations: MaxLoopIterations, comesFromHeuristic: unknownLineInfo(), callbacks: @[], errorFlag: "", - cache: cache) + cache: cache, config: g.config, graph: g) proc refresh*(c: PCtx, module: PSym) = c.module = module diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index fb277272b..2c92348a6 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -9,18 +9,18 @@ import ast, types, msgs, os, streams, options, idents -proc opSlurp*(file: string, info: TLineInfo, module: PSym): string = +proc opSlurp*(file: string, info: TLineInfo, module: PSym; conf: ConfigRef): string = try: var filename = parentDir(info.toFullPath) / file if not fileExists(filename): - filename = file.findFile + filename = findFile(conf, file) result = readFile(filename) # we produce a fake include statement for every slurped filename, so that # the module dependencies are accurate: appendToModule(module, newNode(nkIncludeStmt, info, @[ newStrNode(nkStrLit, filename)])) except IOError: - localError(info, errCannotOpenFile, file) + localError(conf, info, "cannot open file: " & file) result = "" proc atomicTypeX(name: string; m: TMagic; t: PType; info: TLineInfo): PNode = @@ -186,7 +186,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo; if inst: # only named tuples have a node, unnamed tuples don't if t.n.isNil: - result = newNodeX(nkPar) + result = newNodeX(nkTupleConstr) for subType in t.sons: result.add mapTypeToAst(subType, info) else: @@ -208,7 +208,14 @@ proc mapTypeToAstX(t: PType; info: TLineInfo; result.add mapTypeToAst(t.sons[0], info) else: result = mapTypeToBracket("ref", mRef, t, info) - of tyVar: result = mapTypeToBracket("var", mVar, t, info) + of tyVar: + if inst: + result = newNodeX(nkVarTy) + result.add mapTypeToAst(t.sons[0], info) + else: + result = mapTypeToBracket("var", mVar, t, info) + of tyLent: result = mapTypeToBracket("lent", mBuiltinType, t, info) + of tySink: result = mapTypeToBracket("sink", mBuiltinType, t, info) of tySequence: result = mapTypeToBracket("seq", mSeq, t, info) of tyOpt: result = mapTypeToBracket("opt", mOpt, t, info) of tyProc: @@ -264,7 +271,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo; of tyOr: result = mapTypeToBracket("or", mOr, t, info) of tyNot: result = mapTypeToBracket("not", mNot, t, info) of tyAnything: result = atomicType("anything", mNone) - of tyInferred: internalAssert false + of tyInferred: assert false of tyStatic, tyFromExpr: if inst: if t.n != nil: result = t.n.copyTree @@ -274,7 +281,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo; result.add atomicType("static", mNone) if t.n != nil: result.add t.n.copyTree - of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("mapTypeToAstX") + of tyUnused, tyOptAsRef: assert(false, "mapTypeToAstX") proc opMapTypeToAst*(t: PType; info: TLineInfo): PNode = result = mapTypeToAstX(t, info, false, true) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 17878b656..8a3c7e2e6 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -120,7 +120,7 @@ proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) = c.code.add(ins) c.debug.add(n.info) else: - localError(n.info, errGenerated, + localError(c.config, n.info, "VM: immediate value does not fit into an int8") proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) = @@ -137,7 +137,7 @@ proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) = c.code.add(ins) c.debug.add(n.info) else: - localError(n.info, errGenerated, + localError(c.config, n.info, "VM: immediate value does not fit into an int16") proc xjmp(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0): TPosition = @@ -151,7 +151,7 @@ proc genLabel(c: PCtx): TPosition = proc jmpBack(c: PCtx, n: PNode, p = TPosition(0)) = let dist = p.int - c.code.len - internalAssert(-0x7fff < dist and dist < 0x7fff) + internalAssert(c.config, -0x7fff < dist and dist < 0x7fff) gABx(c, n, opcJmpBack, 0, dist) proc patch(c: PCtx, p: TPosition) = @@ -159,7 +159,7 @@ proc patch(c: PCtx, p: TPosition) = let p = p.int let diff = c.code.len - p #c.jumpTargets.incl(c.code.len) - internalAssert(-0x7fff < diff and diff < 0x7fff) + internalAssert(c.config, -0x7fff < diff and diff < 0x7fff) let oldInstr = c.code[p] # opcode and regA stay the same: c.code[p] = ((oldInstr.uint32 and 0xffff'u32).uint32 or @@ -201,7 +201,7 @@ proc getTemp(cc: PCtx; tt: PType): TRegister = c.slots[i] = (inUse: true, kind: k) return TRegister(i) if c.maxSlots >= high(TRegister): - globalError(cc.bestEffort, "VM problem: too many registers required") + globalError(cc.config, cc.bestEffort, "VM problem: too many registers required") result = TRegister(c.maxSlots) c.slots[c.maxSlots] = (inUse: true, kind: k) inc c.maxSlots @@ -223,7 +223,7 @@ proc getTempRange(cc: PCtx; n: int; kind: TSlotKind): TRegister = for k in result .. result+n-1: c.slots[k] = (inUse: true, kind: kind) return if c.maxSlots+n >= high(TRegister): - globalError(cc.bestEffort, "VM problem: too many registers required") + globalError(cc.config, cc.bestEffort, "VM problem: too many registers required") result = TRegister(c.maxSlots) inc c.maxSlots, n for k in result .. result+n-1: c.slots[k] = (inUse: true, kind: kind) @@ -251,7 +251,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) proc gen(c: PCtx; n: PNode; dest: TRegister; flags: TGenFlags = {}) = var d: TDest = dest gen(c, n, d, flags) - internalAssert d == dest + #internalAssert c.config, d == dest # issue #7407 proc gen(c: PCtx; n: PNode; flags: TGenFlags = {}) = var tmp: TDest = -1 @@ -261,7 +261,7 @@ proc gen(c: PCtx; n: PNode; flags: TGenFlags = {}) = proc genx(c: PCtx; n: PNode; flags: TGenFlags = {}): TRegister = var tmp: TDest = -1 gen(c, n, tmp, flags) - #internalAssert tmp >= 0 # 'nim check' does not like this internalAssert. + #internalAssert c.config, tmp >= 0 # 'nim check' does not like this internalAssert. if tmp >= 0: result = TRegister(tmp) @@ -320,7 +320,7 @@ proc genBreak(c: PCtx; n: PNode) = if c.prc.blocks[i].label == n.sons[0].sym: c.prc.blocks[i].fixups.add L1 return - globalError(n.info, errGenerated, "VM problem: cannot find 'break' target") + globalError(c.config, n.info, "VM problem: cannot find 'break' target") else: c.prc.blocks[c.prc.blocks.high].fixups.add L1 @@ -378,7 +378,7 @@ proc rawGenLiteral(c: PCtx; n: PNode): int = #assert(n.kind != nkCall) n.flags.incl nfAllConst c.constants.add n.canonValue - internalAssert result < 0x7fff + internalAssert c.config, result < 0x7fff proc sameConstant*(a, b: PNode): bool = result = false @@ -405,10 +405,10 @@ proc genLiteral(c: PCtx; n: PNode): int = if sameConstant(c.constants[i], n): return i result = rawGenLiteral(c, n) -proc unused(n: PNode; x: TDest) {.inline.} = +proc unused(c: PCtx; n: PNode; x: TDest) {.inline.} = if x >= 0: #debug(n) - globalError(n.info, "not unused") + globalError(c.config, n.info, "not unused") proc genCase(c: PCtx; n: PNode; dest: var TDest) = # if (!expr1) goto L1; @@ -424,7 +424,7 @@ proc genCase(c: PCtx; n: PNode; dest: var TDest) = if not isEmptyType(n.typ): if dest < 0: dest = getTemp(c, n.typ) else: - unused(n, dest) + unused(c, n, dest) var endings: seq[TPosition] = @[] withTemp(tmp, n.sons[0].typ): c.gen(n.sons[0], tmp) @@ -451,7 +451,7 @@ proc genType(c: PCtx; typ: PType): int = if sameType(t, typ): return i result = c.types.len c.types.add(typ) - internalAssert(result <= 0x7fff) + internalAssert(c.config, result <= 0x7fff) proc genTry(c: PCtx; n: PNode; dest: var TDest) = if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ) @@ -525,7 +525,7 @@ proc genCall(c: PCtx; n: PNode; dest: var TDest) = var r: TRegister = x+i c.gen(n.sons[i], r) if i >= fntyp.len: - internalAssert tfVarargs in fntyp.flags + internalAssert c.config, tfVarargs in fntyp.flags c.gABx(n, opcSetType, r, c.genType(n.sons[i].typ)) if dest < 0: c.gABC(n, opcIndCall, 0, x, n.len) @@ -540,12 +540,12 @@ proc needsAsgnPatch(n: PNode): bool = n.kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr, nkDerefExpr, nkHiddenDeref} or (n.kind == nkSym and n.sym.isGlobal) -proc genField(n: PNode): TRegister = +proc genField(c: PCtx; n: PNode): TRegister = if n.kind != nkSym or n.sym.kind != skField: - globalError(n.info, "no field symbol") + globalError(c.config, n.info, "no field symbol") let s = n.sym if s.position > high(result): - globalError(n.info, + globalError(c.config, n.info, "too large offset! cannot generate code for: " & s.name.s) result = s.position @@ -572,7 +572,7 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = # XXX field checks here let left = if le.kind == nkDotExpr: le else: le.sons[0] let dest = c.genx(left.sons[0], {gfAddrOf, gfFieldAccess}) - let idx = genField(left.sons[1]) + let idx = genField(c, left.sons[1]) c.gABC(left, opcWrObj, dest, idx, value) c.freeTemp(dest) of nkDerefExpr, nkHiddenDeref: @@ -779,7 +779,7 @@ proc genIntCast(c: PCtx; n: PNode; dest: var TDest) = let tmp3 = c.getTemp(n.sons[1].typ) if dest < 0: dest = c.getTemp(n[0].typ) proc mkIntLit(ival: int): int = - result = genLiteral(c, newIntTypeNode(nkIntLit, ival, getSysType(tyInt))) + result = genLiteral(c, newIntTypeNode(nkIntLit, ival, getSysType(c.graph, n.info, tyInt))) if src.kind in unsignedIntegers and dst.kind in signedIntegers: # cast unsigned to signed integer of same size # signedVal = (unsignedVal xor offset) -% offset @@ -790,7 +790,7 @@ proc genIntCast(c: PCtx; n: PNode; dest: var TDest) = elif src.kind in signedIntegers and dst.kind in unsignedIntegers: # cast signed to unsigned integer of same size # unsignedVal = (offset +% signedVal +% 1) and offset - let offset = (1 shl (src_size * 8)) - 1 + let offset = (1 shl (src_size * 8)) - 1 c.gABx(n, opcLdConst, tmp2, mkIntLit(offset)) c.gABx(n, opcLdConst, dest, mkIntLit(offset+1)) c.gABC(n, opcAddu, tmp3, tmp, dest) @@ -802,7 +802,7 @@ proc genIntCast(c: PCtx; n: PNode; dest: var TDest) = c.freeTemp(tmp2) c.freeTemp(tmp3) else: - globalError(n.info, errGenerated, "VM is only allowed to 'cast' between integers of same size") + globalError(c.config, n.info, "VM is only allowed to 'cast' between integers of same size") proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = case m @@ -818,7 +818,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mSucc, mAddI: c.genAddSubInt(n, dest, opcAddInt) of mInc, mDec: - unused(n, dest) + unused(c, n, dest) let opc = if m == mInc: opcAddInt else: opcSubInt let d = c.genx(n.sons[1]) if n.sons[2].isInt8Lit: @@ -832,10 +832,10 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.freeTemp(d) of mOrd, mChr, mArrToSeq: c.gen(n.sons[1], dest) of mNew, mNewFinalize: - unused(n, dest) + unused(c, n, dest) c.genNew(n) of mNewSeq: - unused(n, dest) + unused(c, n, dest) c.genNewSeq(n) of mNewSeqOfCap: c.genNewSeqOfCap(n, dest) of mNewString: @@ -844,7 +844,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mNewStringOfCap: # we ignore the 'cap' argument and translate it as 'newString(0)'. # eval n.sons[1] for possible side effects: - var tmp = c.genx(n.sons[1]) + c.freeTemp(c.genx(n.sons[1])) + var tmp = c.getTemp(n.sons[1].typ) c.gABx(n, opcLdImmInt, tmp, 0) if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opcNewStr, dest, tmp) @@ -855,7 +856,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mLengthStr, mXLenStr: genUnaryABI(c, n, dest, opcLenStr) of mIncl, mExcl: - unused(n, dest) + unused(c, n, dest) var d = c.genx(n.sons[1]) var tmp = c.genx(n.sons[2]) c.genSetType(n.sons[1], d) @@ -952,19 +953,19 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mInSet: genBinarySet(c, n, dest, opcContainsSet) of mRepr: genUnaryABC(c, n, dest, opcRepr) of mExit: - unused(n, dest) + unused(c, n, dest) var tmp = c.genx(n.sons[1]) c.gABC(n, opcQuit, tmp) c.freeTemp(tmp) of mSetLengthStr, mSetLengthSeq: - unused(n, dest) + unused(c, n, dest) var d = c.genx(n.sons[1]) var tmp = c.genx(n.sons[2]) c.gABC(n, if m == mSetLengthStr: opcSetLenStr else: opcSetLenSeq, d, tmp) c.genAsgnPatch(n.sons[1], d) c.freeTemp(tmp) of mSwap: - unused(n, dest) + unused(c, n, dest) c.gen(lowerSwap(n, if c.prc == nil: c.module else: c.prc.sym)) of mIsNil: genUnaryABC(c, n, dest, opcIsNil) of mCopyStr: @@ -996,7 +997,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = # skip 'nkHiddenAddr': let d2AsNode = n.sons[2].sons[0] if needsAsgnPatch(d2AsNode): - d2 = c.getTemp(getSysType(tyFloat)) + d2 = c.getTemp(getSysType(c.graph, n.info, tyFloat)) else: d2 = c.genx(d2AsNode) var @@ -1009,13 +1010,13 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.genAsgnPatch(d2AsNode, d2) c.freeTemp(d2) of mReset: - unused(n, dest) + unused(c, n, dest) var d = c.genx(n.sons[1]) c.gABC(n, opcReset, d) of mOf, mIs: if dest < 0: dest = c.getTemp(n.typ) var tmp = c.genx(n.sons[1]) - var idx = c.getTemp(getSysType(tyInt)) + var idx = c.getTemp(getSysType(c.graph, n.info, tyInt)) var typ = n.sons[2].typ if m == mOf: typ = typ.skipTypes(abstractPtrs-{tyTypeDesc}) c.gABx(n, opcLdImmInt, idx, c.genType(typ)) @@ -1023,7 +1024,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.freeTemp(tmp) c.freeTemp(idx) of mSizeOf: - globalError(n.info, errCannotInterpretNodeX, renderTree(n)) + globalError(c.config, n.info, "cannot run in the VM: " & renderTree(n)) of mHigh: if dest < 0: dest = c.getTemp(n.typ) let tmp = c.genx(n.sons[1]) @@ -1034,23 +1035,23 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.gABI(n, opcLenSeq, dest, tmp, 1) c.freeTemp(tmp) of mEcho: - unused(n, dest) + unused(c, n, dest) let n = n[1].skipConv let x = c.getTempRange(n.len, slotTempUnknown) - internalAssert n.kind == nkBracket + internalAssert c.config, n.kind == nkBracket for i in 0..<n.len: var r: TRegister = x+i c.gen(n.sons[i], r) c.gABC(n, opcEcho, x, n.len) c.freeTempRange(x, n.len) of mAppendStrCh: - unused(n, dest) + unused(c, n, dest) genBinaryStmtVar(c, n, opcAddStrCh) of mAppendStrStr: - unused(n, dest) + unused(c, n, dest) genBinaryStmtVar(c, n, opcAddStrStr) of mAppendSeqElem: - unused(n, dest) + unused(c, n, dest) genBinaryStmtVar(c, n, opcAddSeqElem) of mParseExprToAst: genUnaryABC(c, n, dest, opcParseExprToAst) @@ -1068,7 +1069,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mGetImpl: genUnaryABC(c, n, dest, opcGetImpl) of mNChild: genBinaryABC(c, n, dest, opcNChild) of mNSetChild, mNDel: - unused(n, dest) + unused(c, n, dest) var tmp1 = c.genx(n.sons[1]) tmp2 = c.genx(n.sons[2]) @@ -1080,6 +1081,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mNAdd: genBinaryABC(c, n, dest, opcNAdd) of mNAddMultiple: genBinaryABC(c, n, dest, opcNAddMultiple) of mNKind: genUnaryABC(c, n, dest, opcNKind) + of mNSymKind: genUnaryABC(c, n, dest, opcNSymKind) of mNIntVal: genUnaryABC(c, n, dest, opcNIntVal) of mNFloatVal: genUnaryABC(c, n, dest, opcNFloatVal) of mNSymbol: genUnaryABC(c, n, dest, opcNSymbol) @@ -1097,22 +1099,22 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = #genUnaryABC(c, n, dest, opcNGetType) of mNStrVal: genUnaryABC(c, n, dest, opcNStrVal) of mNSetIntVal: - unused(n, dest) + unused(c, n, dest) genBinaryStmt(c, n, opcNSetIntVal) of mNSetFloatVal: - unused(n, dest) + unused(c, n, dest) genBinaryStmt(c, n, opcNSetFloatVal) of mNSetSymbol: - unused(n, dest) + unused(c, n, dest) genBinaryStmt(c, n, opcNSetSymbol) of mNSetIdent: - unused(n, dest) + unused(c, n, dest) genBinaryStmt(c, n, opcNSetIdent) of mNSetType: - unused(n, dest) + unused(c, n, dest) genBinaryStmt(c, n, opcNSetType) of mNSetStrVal: - unused(n, dest) + unused(c, n, dest) genBinaryStmt(c, n, opcNSetStrVal) of mNNewNimNode: genBinaryABC(c, n, dest, opcNNewNimNode) of mNCopyNimNode: genUnaryABC(c, n, dest, opcNCopyNimNode) @@ -1123,9 +1125,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = if dest < 0: dest = c.getTemp(n.typ) c.gABx(n, opcNBindSym, dest, idx) else: - localError(n.info, "invalid bindSym usage") + localError(c.config, n.info, "invalid bindSym usage") of mStrToIdent: genUnaryABC(c, n, dest, opcStrToIdent) - of mIdentToStr: genUnaryABC(c, n, dest, opcIdentToStr) of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent) of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode) of mSameNodeType: genBinaryABC(c, n, dest, opcSameNodeType) @@ -1138,12 +1139,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of "getColumn": genUnaryABC(c, n, dest, opcNGetColumn) else: - internalAssert false + internalAssert c.config, false of mNHint: - unused(n, dest) + unused(c, n, dest) genUnaryStmt(c, n, opcNHint) of mNWarning: - unused(n, dest) + unused(c, n, dest) genUnaryStmt(c, n, opcNWarning) of mNError: if n.len <= 1: @@ -1151,7 +1152,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.gABC(n, opcQueryErrorFlag, dest) else: # setter - unused(n, dest) + unused(c, n, dest) genBinaryStmt(c, n, opcNError) of mNCallSite: if dest < 0: dest = c.getTemp(n.typ) @@ -1162,7 +1163,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.genCall(n, dest) of mExpandToAst: if n.len != 2: - globalError(n.info, errGenerated, "expandToAst requires 1 argument") + globalError(c.config, n.info, "expandToAst requires 1 argument") let arg = n.sons[1] if arg.kind in nkCallKinds: #if arg[0].kind != nkSym or arg[0].sym.kind notin {skTemplate, skMacro}: @@ -1172,12 +1173,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = # do not call clearDest(n, dest) here as getAst has a meta-type as such # produces a value else: - globalError(n.info, "expandToAst requires a call expression") + globalError(c.config, n.info, "expandToAst requires a call expression") of mRunnableExamples: discard "just ignore any call to runnableExamples" else: # mGCref, mGCunref, - globalError(n.info, "cannot generate code for: " & $m) + globalError(c.config, n.info, "cannot generate code for: " & $m) proc genMarshalLoad(c: PCtx, n: PNode, dest: var TDest) = ## Signature: proc to*[T](data: string): T @@ -1288,7 +1289,7 @@ proc whichAsgnOpc(n: PNode): TOpcode = opcAsgnStr of tyFloat..tyFloat128: opcAsgnFloat - of tyRef, tyNil, tyVar: + of tyRef, tyNil, tyVar, tyLent, tyPtr: opcAsgnRef else: opcAsgnComplex @@ -1306,14 +1307,14 @@ proc setSlot(c: PCtx; v: PSym) = if v.position == 0: if c.prc.maxSlots == 0: c.prc.maxSlots = 1 if c.prc.maxSlots >= high(TRegister): - globalError(v.info, "cannot generate code; too many registers required") + globalError(c.config, v.info, "cannot generate code; too many registers required") v.position = c.prc.maxSlots c.prc.slots[v.position] = (inUse: true, kind: if v.kind == skLet: slotFixedLet else: slotFixedVar) inc c.prc.maxSlots -proc cannotEval(n: PNode) {.noinline.} = - globalError(n.info, errGenerated, "cannot evaluate at compile time: " & +proc cannotEval(c: PCtx; n: PNode) {.noinline.} = + globalError(c.config, n.info, "cannot evaluate at compile time: " & n.renderTree) proc isOwnedBy(a, b: PSym): bool = @@ -1333,10 +1334,10 @@ proc checkCanEval(c: PCtx; n: PNode) = if {sfCompileTime, sfGlobal} <= s.flags: return if s.kind in {skVar, skTemp, skLet, skParam, skResult} and not s.isOwnedBy(c.prc.sym) and s.owner != c.module and c.mode != emRepl: - cannotEval(n) + cannotEval(c, n) elif s.kind in {skProc, skFunc, skConverter, skMethod, skIterator} and sfForward in s.flags: - cannotEval(n) + cannotEval(c, n) proc isTemp(c: PCtx; dest: TDest): bool = result = dest >= 0 and c.prc.slots[dest].kind >= slotTempUnknown @@ -1378,7 +1379,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = # XXX field checks here let left = if le.kind == nkDotExpr: le else: le.sons[0] let dest = c.genx(left.sons[0], {gfAddrOf, gfFieldAccess}) - let idx = genField(left.sons[1]) + let idx = genField(c, left.sons[1]) let tmp = c.genx(ri) c.preventFalseAlias(left, opcWrObj, dest, idx, tmp) c.freeTemp(tmp) @@ -1398,7 +1399,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = c.freeTemp(val) else: if s.kind == skForVar: c.setSlot s - internalAssert s.position > 0 or (s.position == 0 and + internalAssert c.config, s.position > 0 or (s.position == 0 and s.kind in {skParam,skResult}) var dest: TRegister = s.position + ord(s.kind == skParam) assert le.typ != nil @@ -1424,15 +1425,15 @@ proc importcSym(c: PCtx; info: TLineInfo; s: PSym) = c.globals.add(importcSymbol(s)) s.position = c.globals.len else: - localError(info, errGenerated, "VM is not allowed to 'importc'") + localError(c.config, info, "VM is not allowed to 'importc'") else: - localError(info, errGenerated, + localError(c.config, info, "cannot 'importc' variable at compile time") -proc getNullValue*(typ: PType, info: TLineInfo): PNode +proc getNullValue*(typ: PType, info: TLineInfo; conf: ConfigRef): PNode proc genGlobalInit(c: PCtx; n: PNode; s: PSym) = - c.globals.add(getNullValue(s.typ, n.info)) + c.globals.add(getNullValue(s.typ, n.info, c.config)) s.position = c.globals.len # This is rather hard to support, due to the laziness of the VM code # generator. See tests/compile/tmacro2 for why this is necessary: @@ -1451,7 +1452,7 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = if sfCompileTime in s.flags or c.mode == emRepl: discard elif s.position == 0: - cannotEval(n) + cannotEval(c, n) if s.position == 0: if sfImportc in s.flags: c.importcSym(n.info, s) else: genGlobalInit(c, n, s) @@ -1472,16 +1473,16 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = s.kind in {skParam,skResult}): if dest < 0: dest = s.position + ord(s.kind == skParam) - internalAssert(c.prc.slots[dest].kind < slotSomeTemp) + internalAssert(c.config, c.prc.slots[dest].kind < slotSomeTemp) else: # we need to generate an assignment: genAsgn(c, dest, n, c.prc.slots[dest].kind >= slotSomeTemp) else: # see tests/t99bott for an example that triggers it: - cannotEval(n) + cannotEval(c, n) template needsRegLoad(): untyped = - gfAddrOf notin flags and fitsRegister(n.typ.skipTypes({tyVar})) + gfAddrOf notin flags and fitsRegister(n.typ.skipTypes({tyVar, tyLent})) proc genArrAccess2(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; flags: TGenFlags) = @@ -1502,7 +1503,7 @@ proc genArrAccess2(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = let a = c.genx(n.sons[0], flags) - let b = genField(n.sons[1]) + let b = genField(c, n.sons[1]) if dest < 0: dest = c.getTemp(n.typ) if needsRegLoad(): var cc = c.getTemp(n.typ) @@ -1526,22 +1527,22 @@ proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = else: genArrAccess2(c, n, dest, opcLdArr, flags) -proc getNullValueAux(obj: PNode, result: PNode) = +proc getNullValueAux(obj: PNode, result: PNode; conf: ConfigRef) = case obj.kind of nkRecList: - for i in countup(0, sonsLen(obj) - 1): getNullValueAux(obj.sons[i], result) + for i in countup(0, sonsLen(obj) - 1): getNullValueAux(obj.sons[i], result, conf) of nkRecCase: - getNullValueAux(obj.sons[0], result) + getNullValueAux(obj.sons[0], result, conf) for i in countup(1, sonsLen(obj) - 1): - getNullValueAux(lastSon(obj.sons[i]), result) + getNullValueAux(lastSon(obj.sons[i]), result, conf) of nkSym: let field = newNodeI(nkExprColonExpr, result.info) field.add(obj) - field.add(getNullValue(obj.sym.typ, result.info)) + field.add(getNullValue(obj.sym.typ, result.info, conf)) addSon(result, field) - else: globalError(result.info, "cannot create null element for: " & $obj) + else: globalError(conf, result.info, "cannot create null element for: " & $obj) -proc getNullValue(typ: PType, info: TLineInfo): PNode = +proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode = var t = skipTypes(typ, abstractRange-{tyTypeDesc}) result = emptyNode case t.kind @@ -1553,14 +1554,15 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = result = newNodeIT(nkFloatLit, info, t) of tyCString, tyString: result = newNodeIT(nkStrLit, info, t) - of tyVar, tyPointer, tyPtr, tySequence, tyExpr, + result.strVal = "" + of tyVar, tyLent, tyPointer, tyPtr, tyExpr, tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil: result = newNodeIT(nkNilLit, info, t) of tyProc: if t.callConv != ccClosure: result = newNodeIT(nkNilLit, info, t) else: - result = newNodeIT(nkPar, info, t) + result = newNodeIT(nkTupleConstr, info, t) result.add(newNodeIT(nkNilLit, info, t)) result.add(newNodeIT(nkNilLit, info, t)) of tyObject: @@ -1569,23 +1571,25 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = # initialize inherited fields: var base = t.sons[0] while base != nil: - getNullValueAux(skipTypes(base, skipPtrs).n, result) + getNullValueAux(skipTypes(base, skipPtrs).n, result, conf) base = base.sons[0] - getNullValueAux(t.n, result) + getNullValueAux(t.n, result, conf) of tyArray: result = newNodeIT(nkBracket, info, t) for i in countup(0, int(lengthOrd(t)) - 1): - addSon(result, getNullValue(elemType(t), info)) + addSon(result, getNullValue(elemType(t), info, conf)) of tyTuple: - result = newNodeIT(nkPar, info, t) + result = newNodeIT(nkTupleConstr, info, t) for i in countup(0, sonsLen(t) - 1): - addSon(result, getNullValue(t.sons[i], info)) + addSon(result, getNullValue(t.sons[i], info, conf)) of tySet: result = newNodeIT(nkCurly, info, t) of tyOpt: result = newNodeIT(nkNilLit, info, t) + of tySequence: + result = newNodeIT(nkBracket, info, t) else: - globalError(info, "cannot create null element for: " & $t.kind) + globalError(conf, info, "cannot create null element for: " & $t.kind) proc ldNullOpcode(t: PType): TOpcode = assert t != nil @@ -1599,7 +1603,7 @@ proc genVarSection(c: PCtx; n: PNode) = for i in 0 .. a.len-3: if not a[i].sym.isGlobal: setSlot(c, a[i].sym) checkCanEval(c, a[i]) - c.gen(lowerTupleUnpacking(a, c.getOwner)) + c.gen(lowerTupleUnpacking(c.graph, a, c.getOwner)) elif a.sons[0].kind == nkSym: let s = a.sons[0].sym checkCanEval(c, a.sons[0]) @@ -1607,7 +1611,7 @@ proc genVarSection(c: PCtx; n: PNode) = if s.position == 0: if sfImportc in s.flags: c.importcSym(a.info, s) else: - let sa = getNullValue(s.typ, a.info) + let sa = getNullValue(s.typ, a.info, c.config) #if s.ast.isNil: getNullValue(s.typ, a.info) #else: canonValue(s.ast) assert sa.kind != nkCall @@ -1649,7 +1653,7 @@ proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) = if dest < 0: dest = c.getTemp(n.typ) c.gABx(n, opcLdNull, dest, c.genType(n.typ)) - let intType = getSysType(tyInt) + let intType = getSysType(c.graph, n.info, tyInt) let seqType = n.typ.skipTypes(abstractVar-{tyTypeDesc}) if seqType.kind == tySequence: var tmp = c.getTemp(intType) @@ -1693,13 +1697,13 @@ proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) = for i in 1..<n.len: let it = n.sons[i] if it.kind == nkExprColonExpr and it.sons[0].kind == nkSym: - let idx = genField(it.sons[0]) + let idx = genField(c, it.sons[0]) let tmp = c.genx(it.sons[1]) c.preventFalseAlias(it.sons[1], whichAsgnOpc(it.sons[1], opcWrObj), dest, idx, tmp) c.freeTemp(tmp) else: - globalError(n.info, "invalid object constructor") + globalError(c.config, n.info, "invalid object constructor") proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) = if dest < 0: dest = c.getTemp(n.typ) @@ -1708,7 +1712,7 @@ proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) = for i in 0..<n.len: let it = n.sons[i] if it.kind == nkExprColonExpr: - let idx = genField(it.sons[0]) + let idx = genField(c, it.sons[0]) let tmp = c.genx(it.sons[1]) c.preventFalseAlias(it.sons[1], whichAsgnOpc(it.sons[1], opcWrObj), dest, idx, tmp) @@ -1768,6 +1772,9 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = let constVal = if s.ast != nil: s.ast else: s.typ.n gen(c, constVal, dest) of skEnumField: + # we never reach this case - as of the time of this comment, + # skEnumField is folded to an int in semfold.nim, but this code + # remains for robustness if dest < 0: dest = c.getTemp(n.typ) if s.position >= low(int16) and s.position <= high(int16): c.gABx(n, opcLdImmInt, dest, s.position) @@ -1780,9 +1787,9 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = if c.prc.sym != nil and c.prc.sym.kind == skMacro: genRdVar(c, n, dest, flags) else: - globalError(n.info, errGenerated, "cannot generate code for: " & s.name.s) + globalError(c.config, n.info, "cannot generate code for: " & s.name.s) else: - globalError(n.info, errGenerated, "cannot generate code for: " & s.name.s) + globalError(c.config, n.info, "cannot generate code for: " & s.name.s) of nkCallKinds: if n.sons[0].kind == nkSym: let s = n.sons[0].sym @@ -1806,10 +1813,10 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = genLit(c, n, dest) of nkUIntLit..pred(nkNilLit): genLit(c, n, dest) of nkNilLit: - if not n.typ.isEmptyType: genLit(c, getNullValue(n.typ, n.info), dest) - else: unused(n, dest) + if not n.typ.isEmptyType: genLit(c, getNullValue(n.typ, n.info, c.config), dest) + else: unused(c, n, dest) of nkAsgn, nkFastAsgn: - unused(n, dest) + unused(c, n, dest) genAsgn(c, n.sons[0], n.sons[1], n.kind == nkAsgn) of nkDotExpr: genObjAccess(c, n, dest, flags) of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest, flags) @@ -1822,21 +1829,20 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = gen(c, n.sons[0].sons[1], dest) of nkCaseStmt: genCase(c, n, dest) of nkWhileStmt: - unused(n, dest) + unused(c, n, dest) genWhile(c, n) of nkBlockExpr, nkBlockStmt: genBlock(c, n, dest) of nkReturnStmt: - unused(n, dest) + unused(c, n, dest) genReturn(c, n) of nkRaiseStmt: - unused(n, dest) genRaise(c, n) of nkBreakStmt: - unused(n, dest) + unused(c, n, dest) genBreak(c, n) of nkTryStmt: genTry(c, n, dest) of nkStmtList: - #unused(n, dest) + #unused(c, n, dest) # XXX Fix this bug properly, lexim triggers it for x in n: gen(c, x) of nkStmtListExpr: @@ -1846,17 +1852,17 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = of nkPragmaBlock: gen(c, n.lastSon, dest, flags) of nkDiscardStmt: - unused(n, dest) + unused(c, n, dest) gen(c, n.sons[0]) of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(c, n, n.sons[1], dest) of nkObjDownConv: genConv(c, n, n.sons[0], dest) of nkVarSection, nkLetSection: - unused(n, dest) + unused(c, n, dest) genVarSection(c, n) of declarativeDefs, nkMacroDef: - unused(n, dest) + unused(c, n, dest) of nkLambdaKinds: #let s = n.sons[namePos].sym #discard genProc(c, s) @@ -1876,13 +1882,13 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = dest = tmp0 of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma, nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt: - unused(n, dest) + unused(c, n, dest) of nkStringToCString, nkCStringToString: gen(c, n.sons[0], dest) of nkBracket: genArrayConstr(c, n, dest) of nkCurly: genSetConstr(c, n, dest) of nkObjConstr: genObjConstr(c, n, dest) - of nkPar, nkClosure: genTupleConstr(c, n, dest) + of nkPar, nkClosure, nkTupleConstr: genTupleConstr(c, n, dest) of nkCast: if allowCast in c.features: genConv(c, n, n.sons[1], dest, opcCast) @@ -1893,7 +1899,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = of nkComesFrom: discard "XXX to implement for better stack traces" else: - globalError(n.info, errGenerated, "cannot generate VM code for " & $n) + globalError(c.config, n.info, "cannot generate VM code for " & $n) proc removeLastEof(c: PCtx) = let last = c.code.len-1 @@ -1910,7 +1916,7 @@ proc genStmt*(c: PCtx; n: PNode): int = c.gen(n, d) c.gABC(n, opcEof) if d >= 0: - globalError(n.info, errGenerated, "VM problem: dest register is set") + globalError(c.config, n.info, "VM problem: dest register is set") proc genExpr*(c: PCtx; n: PNode, requiresValue = true): int = c.removeLastEof @@ -1919,7 +1925,7 @@ proc genExpr*(c: PCtx; n: PNode, requiresValue = true): int = c.gen(n, d) if d < 0: if requiresValue: - globalError(n.info, errGenerated, "VM problem: dest register is not set") + globalError(c.config, n.info, "VM problem: dest register is not set") d = 0 c.gABC(n, opcEof, d) @@ -1934,7 +1940,7 @@ proc genParams(c: PCtx; params: PNode) = c.prc.maxSlots = max(params.len, 1) proc finalJumpTarget(c: PCtx; pc, diff: int) = - internalAssert(-0x7fff < diff and diff < 0x7fff) + internalAssert(c.config, -0x7fff < diff and diff < 0x7fff) let oldInstr = c.code[pc] # opcode and regA stay the same: c.code[pc] = ((oldInstr.uint32 and 0xffff'u32).uint32 or diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim index 0939a5953..f38be7c29 100644 --- a/compiler/vmmarshal.nim +++ b/compiler/vmmarshal.nim @@ -9,7 +9,8 @@ ## Implements marshaling for the VM. -import streams, json, intsets, tables, ast, astalgo, idents, types, msgs +import streams, json, intsets, tables, ast, astalgo, idents, types, msgs, + options proc ptrToInt(x: PNode): int {.inline.} = result = cast[int](x) # don't skip alignment @@ -28,37 +29,38 @@ proc getField(n: PNode; position: int): PSym = of nkOfBranch, nkElse: result = getField(lastSon(n.sons[i]), position) if result != nil: return - else: internalError(n.info, "getField(record case branch)") + else: discard 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 storeAny(s: var string; t: PType; a: PNode; stored: var IntSet; conf: ConfigRef) -proc storeObj(s: var string; typ: PType; x: PNode; stored: var IntSet) = - internalAssert x.kind == nkObjConstr +proc storeObj(s: var string; typ: PType; x: PNode; stored: var IntSet; conf: ConfigRef) = + assert x.kind == nkObjConstr let start = 1 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) + if 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, conf) elif typ.n != nil: let field = getField(typ.n, i) s.add(escapeJson(field.name.s)) s.add(": ") - storeAny(s, field.typ, it, stored) + storeAny(s, field.typ, it, stored, conf) 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) = +proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet; + conf: ConfigRef) = case t.kind of tyNone: assert false of tyBool: s.add($(a.intVal != 0)) @@ -74,7 +76,7 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet) = s.add("[") for i in 0 .. a.len-1: if i > 0: s.add(", ") - storeAny(s, t.elemType, a[i], stored) + storeAny(s, t.elemType, a[i], stored, conf) s.add("]") of tyTuple: s.add("{") @@ -82,11 +84,11 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet) = if i > 0: s.add(", ") s.add("\"Field" & $i) s.add("\": ") - storeAny(s, t.sons[i], a[i].skipColon, stored) + storeAny(s, t.sons[i], a[i].skipColon, stored, conf) s.add("}") of tyObject: s.add("{") - storeObj(s, t, a, stored) + storeObj(s, t, a, stored, conf) s.add("}") of tySet: s.add("[") @@ -94,15 +96,16 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet) = if i > 0: s.add(", ") if a[i].kind == nkRange: var x = copyNode(a[i][0]) - storeAny(s, t.lastSon, x, stored) + storeAny(s, t.lastSon, x, stored, conf) while x.intVal+1 <= a[i][1].intVal: s.add(", ") - storeAny(s, t.lastSon, x, stored) + storeAny(s, t.lastSon, x, stored, conf) inc x.intVal else: - storeAny(s, t.lastSon, a[i], stored) + storeAny(s, t.lastSon, a[i], stored, conf) s.add("]") - of tyRange, tyGenericInst, tyAlias: storeAny(s, t.lastSon, a, stored) + of tyRange, tyGenericInst, tyAlias, tySink: + storeAny(s, t.lastSon, a, stored, conf) of tyEnum: # we need a slow linear search because of enums with holes: for e in items(t.n): @@ -121,7 +124,7 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet) = s.add("[") s.add($x.ptrToInt) s.add(", ") - storeAny(s, t.lastSon, a, stored) + storeAny(s, t.lastSon, a, stored, conf) s.add("]") of tyString, tyCString: if a.kind == nkNilLit or a.strVal.isNil: s.add("null") @@ -129,14 +132,15 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet) = 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 + internalError conf, a.info, "cannot marshal at compile-time " & t.typeToString -proc storeAny*(s: var string; t: PType; a: PNode) = +proc storeAny*(s: var string; t: PType; a: PNode; conf: ConfigRef) = var stored = initIntSet() - storeAny(s, t, a, stored) + storeAny(s, t, a, stored, conf) proc loadAny(p: var JsonParser, t: PType, - tab: var Table[BiggestInt, PNode]): PNode = + tab: var Table[BiggestInt, PNode]; + conf: ConfigRef): PNode = case t.kind of tyNone: assert false of tyBool: @@ -170,7 +174,7 @@ proc loadAny(p: var JsonParser, t: PType, next(p) result = newNode(nkBracket) while p.kind != jsonArrayEnd and p.kind != jsonEof: - result.add loadAny(p, t.elemType, tab) + result.add loadAny(p, t.elemType, tab, conf) if p.kind == jsonArrayEnd: next(p) else: raiseParseErr(p, "']' end of array expected") of tySequence: @@ -182,7 +186,7 @@ proc loadAny(p: var JsonParser, t: PType, next(p) result = newNode(nkBracket) while p.kind != jsonArrayEnd and p.kind != jsonEof: - result.add loadAny(p, t.elemType, tab) + result.add loadAny(p, t.elemType, tab, conf) if p.kind == jsonArrayEnd: next(p) else: raiseParseErr(p, "") else: @@ -190,7 +194,7 @@ proc loadAny(p: var JsonParser, t: PType, of tyTuple: if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object") next(p) - result = newNode(nkPar) + result = newNode(nkTupleConstr) var i = 0 while p.kind != jsonObjectEnd and p.kind != jsonEof: if p.kind != jsonString: @@ -198,7 +202,7 @@ proc loadAny(p: var JsonParser, t: PType, next(p) if i >= t.len: raiseParseErr(p, "too many fields to tuple type " & typeToString(t)) - result.add loadAny(p, t.sons[i], tab) + result.add loadAny(p, t.sons[i], tab, conf) inc i if p.kind == jsonObjectEnd: next(p) else: raiseParseErr(p, "'}' end of object expected") @@ -220,7 +224,7 @@ proc loadAny(p: var JsonParser, t: PType, setLen(result.sons, pos + 1) let fieldNode = newNode(nkExprColonExpr) fieldNode.addSon(newSymNode(newSym(skField, ident, nil, unknownLineInfo()))) - fieldNode.addSon(loadAny(p, field.typ, tab)) + fieldNode.addSon(loadAny(p, field.typ, tab, conf)) result.sons[pos] = fieldNode if p.kind == jsonObjectEnd: next(p) else: raiseParseErr(p, "'}' end of object expected") @@ -229,7 +233,7 @@ proc loadAny(p: var JsonParser, t: PType, next(p) result = newNode(nkCurly) while p.kind != jsonArrayEnd and p.kind != jsonEof: - result.add loadAny(p, t.lastSon, tab) + result.add loadAny(p, t.lastSon, tab, conf) next(p) if p.kind == jsonArrayEnd: next(p) else: raiseParseErr(p, "']' end of array expected") @@ -248,7 +252,7 @@ proc loadAny(p: var JsonParser, t: PType, if p.kind == jsonInt: let idx = p.getInt next(p) - result = loadAny(p, t.lastSon, tab) + result = loadAny(p, t.lastSon, tab, conf) tab[idx] = result else: raiseParseErr(p, "index for ref type expected") if p.kind == jsonArrayEnd: next(p) @@ -275,14 +279,15 @@ proc loadAny(p: var JsonParser, t: PType, next(p) return raiseParseErr(p, "float expected") - of tyRange, tyGenericInst, tyAlias: result = loadAny(p, t.lastSon, tab) + of tyRange, tyGenericInst, tyAlias, tySink: + result = loadAny(p, t.lastSon, tab, conf) else: - internalError "cannot marshal at compile-time " & t.typeToString + internalError conf, "cannot marshal at compile-time " & t.typeToString -proc loadAny*(s: string; t: PType): PNode = +proc loadAny*(s: string; t: PType; conf: ConfigRef): PNode = var tab = initTable[BiggestInt, PNode]() var p: JsonParser open(p, newStringStream(s), "unknown file") next(p) - result = loadAny(p, t, tab) + result = loadAny(p, t, tab, conf) close(p) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 2a00f207a..617295b0d 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -13,7 +13,7 @@ from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin, arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc, floor, ceil, fmod -from os import getEnv, existsEnv, dirExists, fileExists, walkDir +from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir template mathop(op) {.dirty.} = registerCallback(c, "stdlib.math." & astToStr(op), `op Wrapper`) @@ -27,6 +27,9 @@ template ospathsop(op) {.dirty.} = template systemop(op) {.dirty.} = registerCallback(c, "stdlib.system." & astToStr(op), `op Wrapper`) +template macrosop(op) {.dirty.} = + registerCallback(c, "stdlib.macros." & astToStr(op), `op Wrapper`) + template wrap1f_math(op) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = setResult(a, op(getFloat(a, 0))) @@ -37,30 +40,30 @@ template wrap2f_math(op) {.dirty.} = setResult(a, op(getFloat(a, 0), getFloat(a, 1))) mathop op -template wrap1s_os(op) {.dirty.} = +template wrap0(op, modop) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = - setResult(a, op(getString(a, 0))) - osop op + setResult(a, op()) + modop op -template wrap1s_ospaths(op) {.dirty.} = +template wrap1s(op, modop) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = setResult(a, op(getString(a, 0))) - ospathsop op + modop op -template wrap2s_ospaths(op) {.dirty.} = +template wrap2s(op, modop) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = setResult(a, op(getString(a, 0), getString(a, 1))) - ospathsop op + modop op -template wrap1s_system(op) {.dirty.} = +template wrap1svoid(op, modop) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = - setResult(a, op(getString(a, 0))) - systemop op + op(getString(a, 0)) + modop op -template wrap2svoid_system(op) {.dirty.} = +template wrap2svoid(op, modop) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = op(getString(a, 0), getString(a, 1)) - systemop op + modop op proc getCurrentExceptionMsgWrapper(a: VmArgs) {.nimcall.} = setResult(a, if a.currentException.isNil: "" @@ -69,15 +72,18 @@ proc getCurrentExceptionMsgWrapper(a: VmArgs) {.nimcall.} = proc staticWalkDirImpl(path: string, relative: bool): PNode = result = newNode(nkBracket) for k, f in walkDir(path, relative): - result.add newTree(nkPar, newIntNode(nkIntLit, k.ord), + result.add newTree(nkTupleConstr, newIntNode(nkIntLit, k.ord), newStrNode(nkStrLit, f)) -proc gorgeExWrapper(a: VmArgs) {.nimcall.} = - let (s, e) = opGorge(getString(a, 0), getString(a, 1), getString(a, 2), - a.currentLineInfo) - setResult a, newTree(nkPar, newStrNode(nkStrLit, s), newIntNode(nkIntLit, e)) - proc registerAdditionalOps*(c: PCtx) = + proc gorgeExWrapper(a: VmArgs) = + let (s, e) = opGorge(getString(a, 0), getString(a, 1), getString(a, 2), + a.currentLineInfo, c.config) + setResult a, newTree(nkTupleConstr, newStrNode(nkStrLit, s), newIntNode(nkIntLit, e)) + + proc getProjectPathWrapper(a: VmArgs) = + setResult a, c.config.projectPath + wrap1f_math(sqrt) wrap1f_math(ln) wrap1f_math(log10) @@ -101,13 +107,15 @@ proc registerAdditionalOps*(c: PCtx) = wrap1f_math(ceil) wrap2f_math(fmod) - wrap2s_ospaths(getEnv) - wrap1s_ospaths(existsEnv) - wrap1s_os(dirExists) - wrap1s_os(fileExists) - wrap2svoid_system(writeFile) - wrap1s_system(readFile) + wrap2s(getEnv, ospathsop) + wrap1s(existsEnv, ospathsop) + wrap2svoid(putEnv, ospathsop) + wrap1s(dirExists, osop) + wrap1s(fileExists, osop) + wrap2svoid(writeFile, systemop) + wrap1s(readFile, systemop) systemop getCurrentExceptionMsg registerCallback c, "stdlib.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} = setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1))) systemop gorgeEx + macrosop getProjectPath diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index e458cad03..91b527e02 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -52,10 +52,11 @@ type wNimcall, wStdcall, wCdecl, wSafecall, wSyscall, wInline, wNoInline, wFastcall, wClosure, wNoconv, wOn, wOff, wChecks, wRangechecks, wBoundchecks, wOverflowchecks, wNilchecks, - wFloatchecks, wNanChecks, wInfChecks, + wFloatchecks, wNanChecks, wInfChecks, wMoveChecks, wAssertions, wPatterns, wWarnings, wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags, - wDeadCodeElim, wSafecode, wPackage, wNoForward, wReorder, wNoRewrite, + wDeadCodeElimUnused, # deprecated, dead code elim always happens + wSafecode, wPackage, wNoForward, wReorder, wNoRewrite, wPragma, wCompileTime, wNoInit, wPassc, wPassl, wBorrow, wDiscardable, @@ -139,11 +140,12 @@ const "cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "closure", "noconv", "on", "off", "checks", "rangechecks", "boundchecks", "overflowchecks", "nilchecks", - "floatchecks", "nanchecks", "infchecks", + "floatchecks", "nanchecks", "infchecks", "movechecks", "assertions", "patterns", "warnings", "hints", "optimization", "raises", "writes", "reads", "size", "effects", "tags", - "deadcodeelim", "safecode", "package", "noforward", "reorder", "norewrite", + "deadcodeelim", # deprecated, dead code elim always happens + "safecode", "package", "noforward", "reorder", "norewrite", "pragma", "compiletime", "noinit", "passc", "passl", "borrow", "discardable", "fieldchecks", diff --git a/compiler/writetracking.nim b/compiler/writetracking.nim index 577db613d..c0f7b7b20 100644 --- a/compiler/writetracking.nim +++ b/compiler/writetracking.nim @@ -15,7 +15,7 @@ ## * Computing an aliasing relation based on the assignments. This relation ## is then used to compute the 'writes' and 'escapes' effects. -import intsets, idents, ast, astalgo, trees, renderer, msgs, types +import intsets, idents, ast, astalgo, trees, renderer, msgs, types, options const debug = false @@ -120,7 +120,7 @@ proc returnsNewExpr*(n: PNode): NewLocation = nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkOfBranch, nkElifBranch, nkElse, nkExceptBranch, nkFinally, nkCast: result = returnsNewExpr(n.lastSon) - of nkCurly, nkBracket, nkPar, nkObjConstr, nkClosure, + of nkCurly, nkBracket, nkPar, nkTupleConstr, nkObjConstr, nkClosure, nkIfExpr, nkIfStmt, nkWhenStmt, nkCaseStmt, nkTryStmt: result = newLit for i in ord(n.kind == nkObjConstr) ..< n.len: @@ -179,8 +179,8 @@ proc deps(w: var W; n: PNode) = for child in n: let last = lastSon(child) if last.kind == nkEmpty: continue - if child.kind == nkVarTuple and last.kind == nkPar: - internalAssert child.len-2 == last.len + if child.kind == nkVarTuple and last.kind in {nkPar, nkTupleConstr}: + if child.len-2 != last.len: return for i in 0 .. child.len-3: deps(w, child.sons[i], last.sons[i], {}) else: @@ -220,7 +220,7 @@ proc possibleAliases(w: var W; result: var seq[ptr TSym]) = # x = f(..., y, ....) for i in 0 ..< a.srcNoTc: addNoDup a.src[i] -proc markWriteOrEscape(w: var W) = +proc markWriteOrEscape(w: var W; conf: ConfigRef) = ## Both 'writes' and 'escapes' effects ultimately only care ## about *parameters*. ## However, due to aliasing, even locals that might not look as parameters @@ -249,7 +249,7 @@ proc markWriteOrEscape(w: var W) = if p.kind == skParam and p.owner == w.owner: incl(p.flags, sfWrittenTo) if w.owner.kind == skFunc and p.typ.kind != tyVar: - localError(a.info, "write access to non-var parameter: " & p.name.s) + localError(conf, a.info, "write access to non-var parameter: " & p.name.s) if {rootIsResultOrParam, rootIsHeapAccess, markAsEscaping}*a.destInfo != {}: var destIsParam = false @@ -263,14 +263,14 @@ proc markWriteOrEscape(w: var W) = if p.kind == skParam and p.owner == w.owner: incl(p.flags, sfEscapes) -proc trackWrites*(owner: PSym; body: PNode) = +proc trackWrites*(owner: PSym; body: PNode; conf: ConfigRef) = var w: W w.owner = owner w.assignments = @[] # Phase 1: Collect and preprocess any assignments in the proc body: deps(w, body) # Phase 2: Compute the 'writes' and 'escapes' effects: - markWriteOrEscape(w) + markWriteOrEscape(w, conf) if w.returnsNew != asgnOther and not isEmptyType(owner.typ.sons[0]) and containsGarbageCollectedRef(owner.typ.sons[0]): incl(owner.typ.flags, tfReturnsNew) |