diff options
Diffstat (limited to 'compiler')
56 files changed, 2021 insertions, 2049 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 97f48b253..516954b88 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -510,6 +510,7 @@ const tfUncheckedArray* = tfVarargs tfUnion* = tfNoSideEffect tfGcSafe* = tfThread + tfObjHasKids* = tfEnumHasHoles skError* = skUnknown # type flags that are essential for type equality: @@ -549,7 +550,7 @@ type mFields, mFieldPairs, mOmpParFor, mAppendStrCh, mAppendStrStr, mAppendSeqElem, mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, - mIsPartOf, mAstToStr, mRand, + mIsPartOf, mAstToStr, mParallel, mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mNewString, mNewStringOfCap, mReset, @@ -560,7 +561,7 @@ type mFloat, mFloat32, mFloat64, mFloat128, mBool, mChar, mString, mCstring, mPointer, mEmptySet, mIntSetBaseType, mNil, mExpr, mStmt, mTypeDesc, - mVoidType, mPNimrodNode, mShared, mGuarded, mLock, mSpawn, + mVoidType, mPNimrodNode, mShared, mGuarded, mLock, mSpawn, mDeepCopy, mIsMainModule, mCompileDate, mCompileTime, mNimrodVersion, mNimrodMajor, mNimrodMinor, mNimrodPatch, mCpuEndian, mHostOS, mHostCPU, mAppType, mNaN, mInf, mNegInf, @@ -597,18 +598,17 @@ const mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mConArrArr, mConArrT, - mConTArr, mConTT, mSlice, + mConTArr, mConTT, mAppendStrCh, mAppendStrStr, mAppendSeqElem, mInRange, mInSet, mRepr, - mRand, mCopyStr, mCopyStrLast} # magics that require special semantic checking and # thus cannot be overloaded (also documented in the spec!): SpecialSemMagics* = { mDefined, mDefinedInScope, mCompiles, mLow, mHigh, mSizeOf, mIs, mOf, - mEcho, mShallowCopy, mExpandToAst} + mEcho, mShallowCopy, mExpandToAst, mParallel, mSpawn} -type +type PNode* = ref TNode TNodeSeq* = seq[PNode] PType* = ref TType @@ -873,7 +873,7 @@ const skMacro, skTemplate, skConverter, skEnumField, skLet, skStub} PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfDotSetter, nfDotField, - nfAllConst,nfIsRef} + nfIsRef} namePos* = 0 patternPos* = 1 # empty except for term rewriting macros genericParamsPos* = 2 @@ -886,6 +886,8 @@ const nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit, nkHiddenCallConv} + nkIdentKinds* = {nkIdent, nkSym, nkAccQuoted, nkOpenSymChoice, + nkClosedSymChoice} nkLiterals* = {nkCharLit..nkTripleStrLit} nkLambdaKinds* = {nkLambda, nkDo} @@ -1046,7 +1048,7 @@ proc discardSons(father: PNode) = father.sons = nil when defined(useNodeIds): - const nodeIdToDebug* = 482228 # 612794 + const nodeIdToDebug* = 310841 # 612794 #612840 # 612905 # 614635 # 614637 # 614641 # 423408 #429107 # 430443 # 441048 # 441090 # 441153 diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim index 52d50ca39..2e31af528 100644 --- a/compiler/c2nim/cparse.nim +++ b/compiler/c2nim/cparse.nim @@ -19,7 +19,7 @@ import os, llstream, renderer, clex, idents, strutils, pegs, ast, astalgo, msgs, - options, strtabs + options, strtabs, hashes, algorithm type TParserFlag = enum @@ -63,6 +63,15 @@ type ERetryParsing = object of ESynch + + +proc addTypeDef(section, name, t: PNode) +proc parseStruct(p: var TParser, stmtList: PNode, isUnion: bool): PNode +proc parseStructBody(p: var TParser, stmtList: PNode, isUnion: bool, + kind: TNodeKind = nkRecList): PNode + + + proc newParserOptions*(): PParserOptions = new(result) result.prefixes = @[] @@ -682,12 +691,75 @@ proc parseField(p: var TParser, kind: TNodeKind): PNode = else: result = mangledIdent(p.tok.s, p) getTok(p, result) -proc parseStructBody(p: var TParser, isUnion: bool, +proc structPragmas(p: TParser, name: PNode, origName: string): PNode = + assert name.kind == nkIdent + result = newNodeP(nkPragmaExpr, p) + addSon(result, exportSym(p, name, origName)) + var pragmas = newNodeP(nkPragma, p) + #addSon(pragmas, newIdentNodeP("pure", p), newIdentNodeP("final", p)) + if p.options.header.len > 0: + addSon(pragmas, newIdentStrLitPair("importc", origName, p), + newIdentStrLitPair("header", p.options.header, p)) + if pragmas.len > 0: addSon(result, pragmas) + else: addSon(result, ast.emptyNode) + +proc hashPosition(p: TParser): string = + let lineInfo = parLineInfo(p) + let fileInfo = fileInfos[lineInfo.fileIndex] + result = $hash(fileInfo.shortName & "_" & $lineInfo.line & "_" & $lineInfo.col).uint + +proc parseInnerStruct(p: var TParser, stmtList: PNode, isUnion: bool): PNode = + getTok(p, nil) + if p.tok.xkind != pxCurlyLe: + parMessage(p, errUser, "Expected '{' but found '" & $(p.tok[]) & "'") + + let structName = if isUnion: "INNER_C_UNION_" & p.hashPosition + else: "INNER_C_STRUCT_" & p.hashPosition + let typeSection = newNodeP(nkTypeSection, p) + let newStruct = newNodeP(nkObjectTy, p) + var pragmas = ast.emptyNode + if isUnion: + pragmas = newNodeP(nkPragma, p) + addSon(pragmas, newIdentNodeP("union", p)) + addSon(newStruct, pragmas, ast.emptyNode) # no inheritance + result = newNodeP(nkIdent, p) + result.ident = getIdent(structName) + let struct = parseStructBody(p, stmtList, isUnion) + let defName = newNodeP(nkIdent, p) + defName.ident = getIdent(structName) + addSon(newStruct, struct) + addTypeDef(typeSection, structPragmas(p, defName, "no_name"), newStruct) + addSon(stmtList, typeSection) + +proc parseStructBody(p: var TParser, stmtList: PNode, isUnion: bool, kind: TNodeKind = nkRecList): PNode = result = newNodeP(kind, p) eat(p, pxCurlyLe, result) while p.tok.xkind notin {pxEof, pxCurlyRi}: - var baseTyp = typeAtom(p) + skipConst(p) + var baseTyp: PNode + if p.tok.xkind == pxSymbol and (p.tok.s == "struct" or p.tok.s == "union"): + let gotUnion = if p.tok.s == "union": true else: false + saveContext(p) + getTok(p, nil) + if p.tok.xkind == pxSymbol: + backtrackContext(p) + baseTyp = typeAtom(p) + else: + backtrackContext(p) + baseTyp = parseInnerStruct(p, stmtList, gotUnion) + if p.tok.xkind == pxSemiColon: + let def = newNodeP(nkIdentDefs, p) + var t = pointer(p, baseTyp) + let i = fieldIdent("ano_" & p.hashPosition, p) + t = parseTypeSuffix(p, t) + addSon(def, i, t, ast.emptyNode) + addSon(result, def) + getTok(p, nil) + continue + else: + baseTyp = typeAtom(p) + while true: var def = newNodeP(nkIdentDefs, p) var t = pointer(p, baseTyp) @@ -700,18 +772,6 @@ proc parseStructBody(p: var TParser, isUnion: bool, eat(p, pxSemicolon, lastSon(result)) eat(p, pxCurlyRi, result) -proc structPragmas(p: TParser, name: PNode, origName: string): PNode = - assert name.kind == nkIdent - result = newNodeP(nkPragmaExpr, p) - addSon(result, exportSym(p, name, origName)) - var pragmas = newNodeP(nkPragma, p) - #addSon(pragmas, newIdentNodeP("pure", p), newIdentNodeP("final", p)) - if p.options.header.len > 0: - addSon(pragmas, newIdentStrLitPair("importc", origName, p), - newIdentStrLitPair("header", p.options.header, p)) - if pragmas.len > 0: addSon(result, pragmas) - else: addSon(result, ast.emptyNode) - proc enumPragmas(p: TParser, name: PNode): PNode = result = newNodeP(nkPragmaExpr, p) addSon(result, name) @@ -722,7 +782,7 @@ proc enumPragmas(p: TParser, name: PNode): PNode = addSon(pragmas, e) addSon(result, pragmas) -proc parseStruct(p: var TParser, isUnion: bool): PNode = +proc parseStruct(p: var TParser, stmtList: PNode, isUnion: bool): PNode = result = newNodeP(nkObjectTy, p) var pragmas = ast.emptyNode if isUnion: @@ -730,7 +790,7 @@ proc parseStruct(p: var TParser, isUnion: bool): PNode = addSon(pragmas, newIdentNodeP("union", p)) addSon(result, pragmas, ast.emptyNode) # no inheritance if p.tok.xkind == pxCurlyLe: - addSon(result, parseStructBody(p, isUnion)) + addSon(result, parseStructBody(p, stmtList, isUnion)) else: addSon(result, newNodeP(nkRecList, p)) @@ -855,9 +915,28 @@ proc parseTrailingDefinedTypes(p: var TParser, section, typ: PNode) = newTyp = parseTypeSuffix(p, newTyp) addTypeDef(section, newName, newTyp) -proc enumFields(p: var TParser): PNode = +proc createConst(name, typ, val: PNode, p: TParser): PNode = + result = newNodeP(nkConstDef, p) + addSon(result, name, typ, val) + +proc exprToNumber(n: PNode not nil): tuple[succ: bool, val: BiggestInt] = + result = (false, 0.BiggestInt) + case n.kind: + of nkPrefix: + # Check for negative/positive numbers -3 or +6 + if n.sons.len == 2 and n.sons[0].kind == nkIdent and n.sons[1].kind == nkIntLit: + let pre = n.sons[0] + let num = n.sons[1] + if pre.ident.s == "-": result = (true, - num.intVal) + elif pre.ident.s == "+": result = (true, num.intVal) + else: discard + +proc enumFields(p: var TParser, constList: PNode): PNode = result = newNodeP(nkEnumTy, p) addSon(result, ast.emptyNode) # enum does not inherit from anything + var i: BiggestInt = 0 + var field: tuple[id: BiggestInt, isNumber: bool, node: PNode] + var fields = newSeq[type(field)]() while true: var e = skipIdent(p) if p.tok.xkind == pxAsgn: @@ -867,17 +946,59 @@ proc enumFields(p: var TParser): PNode = e = newNodeP(nkEnumFieldDef, p) addSon(e, a, c) skipCom(p, e) - - addSon(result, e) + if c.kind == nkIntLit: + i = c.intVal + field.isNumber = true + else: + var (success, number) = exprToNumber(c) + if success: + i = number + field.isNumber = true + else: + field.isNumber = false + else: + inc(i) + field.isNumber = true + field.id = i + field.node = e + fields.add(field) if p.tok.xkind != pxComma: break getTok(p, e) # allow trailing comma: if p.tok.xkind == pxCurlyRi: break - -proc parseTypedefStruct(p: var TParser, result: PNode, isUnion: bool) = + fields.sort do (x, y: type(field)) -> int: + cmp(x.id, y.id) + var lastId: BiggestInt + var lastIdent: PNode + for count, f in fields: + if not f.isNumber: + addSon(result, f.node) + elif f.id == lastId and count > 0: + var currentIdent: PNode + case f.node.kind: + of nkEnumFieldDef: + if f.node.sons.len > 0 and f.node.sons[0].kind == nkIdent: + currentIdent = f.node.sons[0] + else: parMessage(p, errGenerated, "Warning: When sorting enum fields an expected nkIdent was not found. Check the fields!") + of nkIdent: currentIdent = f.node + else: parMessage(p, errGenerated, "Warning: When sorting enum fields an expected nkIdent was not found. Check the fields!") + var constant = createConst( currentIdent, ast.emptyNode, lastIdent, p) + constList.addSon(constant) + else: + addSon(result, f.node) + lastId = f.id + case f.node.kind: + of nkEnumFieldDef: + if f.node.sons.len > 0 and f.node.sons[0].kind == nkIdent: + lastIdent = f.node.sons[0] + else: parMessage(p, errGenerated, "Warning: When sorting enum fields an expected nkIdent was not found. Check the fields!") + of nkIdent: lastIdent = f.node + else: parMessage(p, errGenerated, "Warning: When sorting enum fields an expected nkIdent was not found. Check the fields!") + +proc parseTypedefStruct(p: var TParser, result: PNode, stmtList: PNode, isUnion: bool) = getTok(p, result) if p.tok.xkind == pxCurlyLe: - var t = parseStruct(p, isUnion) + var t = parseStruct(p, stmtList, isUnion) var origName = p.tok.s markTypeIdent(p, nil) var name = skipIdent(p) @@ -890,7 +1011,7 @@ proc parseTypedefStruct(p: var TParser, result: PNode, isUnion: bool) = var nameOrType = skipIdent(p) case p.tok.xkind of pxCurlyLe: - var t = parseStruct(p, isUnion) + var t = parseStruct(p, stmtList, isUnion) if p.tok.xkind == pxSymbol: # typedef struct tagABC {} abc, *pabc; # --> abc is a better type name than tagABC! @@ -914,11 +1035,11 @@ proc parseTypedefStruct(p: var TParser, result: PNode, isUnion: bool) = else: expectIdent(p) -proc parseTypedefEnum(p: var TParser, result: PNode) = +proc parseTypedefEnum(p: var TParser, result, constSection: PNode) = getTok(p, result) if p.tok.xkind == pxCurlyLe: getTok(p, result) - var t = enumFields(p) + var t = enumFields(p, constSection) eat(p, pxCurlyRi, t) var origName = p.tok.s markTypeIdent(p, nil) @@ -933,7 +1054,7 @@ proc parseTypedefEnum(p: var TParser, result: PNode) = case p.tok.xkind of pxCurlyLe: getTok(p, result) - var t = enumFields(p) + var t = enumFields(p, constSection) eat(p, pxCurlyRi, t) if p.tok.xkind == pxSymbol: # typedef enum tagABC {} abc, *pabc; @@ -960,27 +1081,36 @@ proc parseTypedefEnum(p: var TParser, result: PNode) = expectIdent(p) proc parseTypeDef(p: var TParser): PNode = - result = newNodeP(nkTypeSection, p) + result = newNodeP(nkStmtList, p) + var typeSection = newNodeP(nkTypeSection, p) + var afterStatements = newNodeP(nkStmtList, p) while p.tok.xkind == pxSymbol and p.tok.s == "typedef": - getTok(p, result) + getTok(p, typeSection) inc(p.inTypeDef) expectIdent(p) case p.tok.s - of "struct": parseTypedefStruct(p, result, isUnion=false) - of "union": parseTypedefStruct(p, result, isUnion=true) - of "enum": parseTypedefEnum(p, result) + of "struct": parseTypedefStruct(p, typeSection, result, isUnion=false) + of "union": parseTypedefStruct(p, typeSection, result, isUnion=true) + of "enum": + var constSection = newNodeP(nkConstSection, p) + parseTypedefEnum(p, typeSection, constSection) + addSon(afterStatements, constSection) of "class": if pfCpp in p.options.flags: - parseTypedefStruct(p, result, isUnion=false) + parseTypedefStruct(p, typeSection, result, isUnion=false) else: var t = typeAtom(p) - otherTypeDef(p, result, t) + otherTypeDef(p, typeSection, t) else: var t = typeAtom(p) - otherTypeDef(p, result, t) + otherTypeDef(p, typeSection, t) eat(p, pxSemicolon) dec(p.inTypeDef) - + + addSon(result, typeSection) + for s in afterStatements: + addSon(result, s) + proc skipDeclarationSpecifiers(p: var TParser) = while p.tok.xkind == pxSymbol: case p.tok.s @@ -1092,10 +1222,6 @@ proc declaration(p: var TParser): PNode = result = parseVarDecl(p, baseTyp, rettyp, origName) assert result != nil -proc createConst(name, typ, val: PNode, p: TParser): PNode = - result = newNodeP(nkConstDef, p) - addSon(result, name, typ, val) - proc enumSpecifier(p: var TParser): PNode = saveContext(p) getTok(p, nil) # skip "enum" @@ -1141,12 +1267,16 @@ proc enumSpecifier(p: var TParser): PNode = closeContext(p) var name = result # create a type section containing the enum - result = newNodeP(nkTypeSection, p) + result = newNodeP(nkStmtList, p) + var tSection = newNodeP(nkTypeSection, p) var t = newNodeP(nkTypeDef, p) getTok(p, t) - var e = enumFields(p) + var constSection = newNodeP(nkConstSection, p) + var e = enumFields(p, constSection) addSon(t, exportSym(p, name, origName), ast.emptyNode, e) - addSon(result, t) + addSon(tSection, t) + addSon(result, tSection) + addSon(result, constSection) eat(p, pxCurlyRi, result) eat(p, pxSemicolon) of pxSemicolon: @@ -1608,8 +1738,8 @@ proc declarationOrStatement(p: var TParser): PNode = result = expressionStatement(p) assert result != nil -proc parseTuple(p: var TParser, isUnion: bool): PNode = - result = parseStructBody(p, isUnion, nkTupleTy) +proc parseTuple(p: var TParser, statements: PNode, isUnion: bool): PNode = + parseStructBody(p, statements, isUnion, nkTupleTy) proc parseTrailingDefinedIdents(p: var TParser, result, baseTyp: PNode) = var varSection = newNodeP(nkVarSection, p) @@ -1640,13 +1770,13 @@ proc parseStandaloneStruct(p: var TParser, isUnion: bool): PNode = if p.tok.xkind in {pxCurlyLe, pxSemiColon}: if origName.len > 0: var name = mangledIdent(origName, p) - var t = parseStruct(p, isUnion) + var t = parseStruct(p, result, isUnion) var typeSection = newNodeP(nkTypeSection, p) addTypeDef(typeSection, structPragmas(p, name, origName), t) addSon(result, typeSection) parseTrailingDefinedIdents(p, result, name) else: - var t = parseTuple(p, isUnion) + var t = parseTuple(p, result, isUnion) parseTrailingDefinedIdents(p, result, t) else: backtrackContext(p) @@ -2034,7 +2164,7 @@ proc parseStandaloneClass(p: var TParser, isStruct: bool): PNode = addTypeDef(typeSection, structPragmas(p, name, origName), t) parseTrailingDefinedIdents(p, result, name) else: - var t = parseTuple(p, isUnion=false) + var t = parseTuple(p, result, isUnion=false) parseTrailingDefinedIdents(p, result, t) else: backtrackContext(p) diff --git a/compiler/c2nim/tests/enum.h b/compiler/c2nim/tests/enum.h new file mode 100644 index 000000000..16bc59058 --- /dev/null +++ b/compiler/c2nim/tests/enum.h @@ -0,0 +1,40 @@ + +enum vehicles +{ + car = 0x10, + truck, + boat = 0x01, + ship = 1, + speedboat = 1, + bicycle = 4, + bobycar +}; + +enum +{ + red = 4, + green = 2, + blue +}; + +typedef enum food +{ + bread = 4, + toast = 4, + bun = 0x04, + cucumber = 2, + chocolate = 6 +}; + +typedef enum numbers +{ + one = 1, + two, + nten = - 10, + nnine, + four = 4, + three = + 3, + positivenine = + 9, + nfour = - 4, + negativeten = -10 +}; \ No newline at end of file diff --git a/compiler/c2nim/tests/struct_anonym.h b/compiler/c2nim/tests/struct_anonym.h new file mode 100644 index 000000000..859bfc206 --- /dev/null +++ b/compiler/c2nim/tests/struct_anonym.h @@ -0,0 +1,27 @@ + +struct normal{ + int a; + int b; +}; + +typedef struct outerStruct { + struct normal a_nomal_one; + + int a; + + struct { + union { + int b; + } a_union_in_the_struct; + + int c; + }; + + union { + int d; + + struct { + int e; + } a_struct_in_the_union; + } a_union; +}; \ No newline at end of file diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 84c5bf419..71e23aa1d 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -77,18 +77,38 @@ proc isInCurrentFrame(p: BProc, n: PNode): bool = proc openArrayLoc(p: BProc, n: PNode): PRope = var a: TLoc - initLocExpr(p, n, a) - case skipTypes(a.t, abstractVar).kind - of tyOpenArray, tyVarargs: - result = ropef("$1, $1Len0", [rdLoc(a)]) - of tyString, tySequence: - if skipTypes(n.typ, abstractInst).kind == tyVar: - result = ropef("(*$1)->data, (*$1)->$2", [a.rdLoc, lenField()]) - else: - result = ropef("$1->data, $1->$2", [a.rdLoc, lenField()]) - of tyArray, tyArrayConstr: - result = ropef("$1, $2", [rdLoc(a), toRope(lengthOrd(a.t))]) - else: internalError("openArrayLoc: " & typeToString(a.t)) + + let q = skipConv(n) + if getMagic(q) == mSlice: + # magic: pass slice to openArray: + var b, c: TLoc + 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, tyArrayConstr: + "($1)+($2), ($3)-($2)+1" + of tyString, tySequence: + if skipTypes(n.typ, abstractInst).kind == tyVar: + "(*$1)->data+($2), ($3)-($2)+1" + else: + "$1->data+($2), ($3)-($2)+1" + else: (internalError("openArrayLoc: " & typeToString(a.t)); "") + result = ropef(fmt, [rdLoc(a), rdLoc(b), rdLoc(c)]) + else: + initLocExpr(p, n, a) + case skipTypes(a.t, abstractVar).kind + of tyOpenArray, tyVarargs: + result = ropef("$1, $1Len0", [rdLoc(a)]) + of tyString, tySequence: + if skipTypes(n.typ, abstractInst).kind == tyVar: + result = ropef("(*$1)->data, (*$1)->$2", [a.rdLoc, lenField()]) + else: + result = ropef("$1->data, $1->$2", [a.rdLoc, lenField()]) + of tyArray, tyArrayConstr: + result = ropef("$1, $2", [rdLoc(a), toRope(lengthOrd(a.t))]) + else: internalError("openArrayLoc: " & typeToString(a.t)) proc genArgStringToCString(p: BProc, n: PNode): PRope {.inline.} = diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 49350fa9c..4698082f1 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -484,7 +484,7 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = opr: array[mUnaryMinusI..mAbsI64, string] = [ mUnaryMinusI: "((NI$2)-($1))", mUnaryMinusI64: "-($1)", - mAbsI: "(NI$2)abs($1)", + mAbsI: "($1 > 0? ($1) : -($1))", mAbsI64: "($1 > 0? ($1) : -($1))"] var a: TLoc @@ -714,11 +714,12 @@ proc genFieldCheck(p: BProc, e: PNode, obj: PRope, field: PSym) = assert(it.sons[0].kind == nkSym) let op = it.sons[0].sym if op.magic == mNot: it = it.sons[1] - assert(it.sons[2].kind == nkSym) + let disc = it.sons[2].skipConv + assert(disc.kind == nkSym) initLoc(test, locNone, it.typ, OnStack) initLocExpr(p, it.sons[1], u) - initLoc(v, locExpr, it.sons[2].typ, OnUnknown) - v.r = ropef("$1.$2", [obj, it.sons[2].sym.loc.r]) + initLoc(v, locExpr, disc.typ, OnUnknown) + v.r = ropef("$1.$2", [obj, disc.sym.loc.r]) genInExprAux(p, it, u, v, test) let id = nodeTableTestOrSet(p.module.dataCache, newStrNode(nkStrLit, field.name.s), gBackendId) @@ -1144,6 +1145,24 @@ proc genNewFinalize(p: BProc, e: PNode) = genObjectInit(p, cpsStmts, bt, a, false) gcUsage(e) +proc genOfHelper(p: BProc; dest: PType; a: PRope): PRope = + # unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we + # have to call it here first: + let ti = genTypeInfo(p.module, dest) + if tfFinal in dest.flags or (p.module.objHasKidsValid and + tfObjHasKids notin dest.flags): + result = ropef("$1.m_type == $2", a, ti) + else: + discard cgsym(p.module, "TNimType") + inc p.module.labels + let cache = con("Nim_OfCheck_CACHE", p.module.labels.toRope) + appf(p.module.s[cfsVars], "static TNimType* $#[2];$n", cache) + result = rfmt(p.module, "#isObjWithCache($#.m_type, $#, $#)", a, ti, cache) + when false: + # former version: + result = rfmt(p.module, "#isObj($1.m_type, $2)", + a, genTypeInfo(p.module, dest)) + proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = var a: TLoc initLocExpr(p, x, a) @@ -1163,11 +1182,9 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = globalError(x.info, errGenerated, "no 'of' operator available for pure objects") if nilCheck != nil: - r = rfmt(p.module, "(($1) && #isObj($2.m_type, $3))", - nilCheck, r, genTypeInfo(p.module, dest)) + r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r)) else: - r = rfmt(p.module, "#isObj($1.m_type, $2)", - r, genTypeInfo(p.module, dest)) + r = rfmt(p.module, "($1)", genOfHelper(p, dest, r)) putIntoDest(p, d, getSysType(tyBool), r) proc genOf(p: BProc, n: PNode, d: var TLoc) = @@ -1382,10 +1399,10 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mIncl: var ts = "NI" & $(size * 8) binaryStmtInExcl(p, e, d, - "$1 |=((" & ts & ")(1)<<(($2)%(sizeof(" & ts & ")*8)));$n") + "$1 |= ((" & ts & ")1)<<(($2)%(sizeof(" & ts & ")*8));$n") of mExcl: var ts = "NI" & $(size * 8) - binaryStmtInExcl(p, e, d, "$1 &= ~((" & ts & ")(1) << (($2) % (sizeof(" & + binaryStmtInExcl(p, e, d, "$1 &= ~(((" & ts & ")1) << (($2) % (sizeof(" & ts & ")*8)));$n") of mCard: if size <= 4: unaryExprChar(p, e, d, "#countBits32($1)") @@ -1623,7 +1640,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet, mInSet: genSetOp(p, e, d, op) - of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit, mRand: + of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit: var opr = e.sons[0].sym if lfNoDecl notin opr.loc.flags: discard cgsym(p.module, opr.loc.r.ropeToStr) @@ -1636,7 +1653,10 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mSlurp..mQuoteAst: localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s) of mSpawn: - let n = lowerings.wrapProcForSpawn(p.module.module, e.sons[1]) + let n = lowerings.wrapProcForSpawn(p.module.module, e, e.typ, nil, nil) + expr(p, n, d) + of mParallel: + let n = semparallel.liftParallel(p.module.module, e) expr(p, n, d) else: internalError(e.info, "genMagicExpr: " & $op) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 311149cb3..a8cfa57e4 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -593,6 +593,7 @@ proc genStringCase(p: BProc, t: PNode, d: var TLoc) = else: # else statement: nothing to do yet # but we reserved a label, which we use later + discard linefmt(p, cpsStmts, "switch (#hashString($1) & $2) {$n", rdLoc(a), toRope(bitMask)) for j in countup(0, high(branches)): diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index f51e66897..8e762ce27 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -11,49 +11,10 @@ # ------------------------- Name Mangling -------------------------------- -proc mangleField(name: string): string = - case name[0] - of 'a'..'z': - result = "" - add(result, chr(ord(name[0]) - ord('a') + ord('A'))) - of '0'..'9', 'A'..'Z': - result = "" - add(result, name[0]) - else: result = "HEX" & toHex(ord(name[0]), 2) - for i in countup(1, len(name) - 1): - case name[i] - of 'A'..'Z': - add(result, chr(ord(name[i]) - ord('A') + ord('a'))) - of '_': - discard - of 'a'..'z', '0'..'9': - add(result, name[i]) - else: - add(result, "HEX") - add(result, toHex(ord(name[i]), 2)) - -proc mangle(name: string): string = - when false: - case name[0] - of 'a'..'z': - result = "" - add(result, chr(ord(name[0]) - ord('a') + ord('A'))) - of '0'..'9', 'A'..'Z': - result = "" - add(result, name[0]) - else: result = "HEX" & toHex(ord(name[0]), 2) - result = "" - for i in countup(0, len(name) - 1): - case name[i] - of 'A'..'Z': - add(result, chr(ord(name[i]) - ord('A') + ord('a'))) - of '_': - discard - of 'a'..'z', '0'..'9': - add(result, name[i]) - else: - add(result, "HEX") - add(result, toHex(ord(name[i]), 2)) +proc mangleField(name: string): string = + result = mangle(name) + result[0] = result[0].toUpper # Mangling makes everything lowercase, + # but some identifiers are C keywords proc isKeyword(w: PIdent): bool = # nimrod and C++ share some keywords @@ -835,6 +796,11 @@ proc genObjectInfo(m: BModule, typ: PType, name: PRope) = var tmp = getNimNode(m) genObjectFields(m, typ, typ.n, tmp) appf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, tmp]) + var t = typ.sons[0] + while t != nil: + t = t.skipTypes(abstractInst) + t.flags.incl tfObjHasKids + t = t.sons[0] proc genTupleInfo(m: BModule, typ: PType, name: PRope) = genTypeInfoAuxBase(m, typ, name, toRope("0")) diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index 1d8f0158b..04983d6a4 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -161,6 +161,30 @@ proc makeSingleLineCString*(s: string): string = result.add(c.toCChar) result.add('\"') +proc mangle*(name: string): string = + ## Lowercases the given name and manges any non-alphanumeric characters + ## so they are represented as `HEX____`. If the name starts with a number, + ## `N` is prepended + result = "" + case name[0] + of Letters: + result.add(name[0].toLower) + of Digits: + result.add("N" & name[0]) + else: + result = "HEX" & toHex(ord(name[0]), 2) + for i in 1..(name.len-1): + let c = name[i] + case c + of 'A'..'Z': + add(result, c.toLower) + of '_': + discard + of 'a'..'z', '0'..'9': + add(result, c) + else: + add(result, "HEX" & toHex(ord(c), 2)) + proc makeLLVMString*(s: string): PRope = const MaxLineLength = 64 result = nil diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 8d66d7a3b..e2f3b5ab0 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -14,7 +14,8 @@ import options, intsets, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os, times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth, - rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, lowerings + rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, lowerings, + semparallel when options.hasTinyCBackend: import tccgen @@ -503,7 +504,8 @@ proc assignLocalVar(p: BProc, s: PSym) = if sfRegister in s.flags: app(decl, " register") #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds: # app(decl, " GC_GUARD") - if sfVolatile in s.flags or p.nestedTryStmts.len > 0: + if sfVolatile in s.flags or (p.nestedTryStmts.len > 0 and + gCmd != cmdCompileToCpp): app(decl, " volatile") appf(decl, " $1;$n", [s.loc.r]) else: @@ -1048,6 +1050,7 @@ proc getSomeInitName(m: PSym, suffix: string): PRope = assert m.owner.kind == skPackage if {sfSystemModule, sfMainModule} * m.flags == {}: result = m.owner.name.s.mangle.toRope + result.app "_" result.app m.name.s result.app suffix @@ -1382,6 +1385,7 @@ proc myClose(b: PPassContext, n: PNode): PNode = registerModuleToMain(m.module) if sfMainModule in m.module.flags: + m.objHasKidsValid = true var disp = generateMethodDispatchers() for i in 0..sonsLen(disp)-1: genProcAux(m, disp.sons[i].sym) genMainProc(m) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index e7d818556..12041c55b 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -96,6 +96,7 @@ type # a frame var twice in an init proc isHeaderFile*: bool # C source file is the header file includesStringh*: bool # C source file already includes ``<string.h>`` + objHasKidsValid*: bool # whether we can rely on tfObjHasKids cfilename*: string # filename of the module (including path, # without extension) typeCache*: TIdTable # cache the generated types diff --git a/compiler/commands.nim b/compiler/commands.nim index 8339219ed..38c8dd294 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -9,10 +9,34 @@ # This module handles the parsing of command line arguments. + +# We do this here before the 'import' statement so 'defined' does not get +# confused with 'TGCMode.gcGenerational' etc. +template bootSwitch(name, expr, userString: expr): expr = + # Helper to build boot constants, for debugging you can 'echo' the else part. + const name = if expr: " " & userString else: "" + +bootSwitch(usedRelease, defined(release), "-d:release") +bootSwitch(usedGnuReadline, defined(useGnuReadline), "-d:useGnuReadline") +bootSwitch(usedNoCaas, defined(noCaas), "-d:noCaas") +bootSwitch(usedBoehm, defined(boehmgc), "--gc:boehm") +bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep") +bootSwitch(usedGenerational, defined(gcgenerational), "--gc:generational") +bootSwitch(usedNoGC, defined(nogc), "--gc:none") + import os, msgs, options, nversion, condsyms, strutils, extccomp, platform, lists, wordrecg, parseutils, babelcmd, idents +# 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 @@ -55,6 +79,14 @@ proc writeVersionInfo(pass: TCmdLinePass) = msgWriteln(`%`(HelpMessage, [VersionAsString, platform.OS[platform.hostOS].name, CPU[platform.hostCPU].name])) + + const gitHash = gorge("git log -n 1 --format=%H") + if gitHash.strip.len == 40: + msgWriteln("git hash: " & gitHash) + + msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine & + usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas & + usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedNoGC) quit(0) var diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 6948c4979..4c9803401 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -541,8 +541,14 @@ proc genOutFile(d: PDoc): PRope = if toc != nil: toc = ropeFormatNamedVars(getConfigVar("doc.toc"), ["content"], [toc]) for i in countup(low(TSymKind), high(TSymKind)): app(code, d.section[i]) - if d.meta[metaTitle].len != 0: title = d.meta[metaTitle] - else: title = "Module " & extractFilename(changeFileExt(d.filename, "")) + + # Extract the title. Non API modules generate an entry in the index table. + if d.meta[metaTitle].len != 0: + title = d.meta[metaTitle] + setIndexTerm(d[], "", title) + else: + # Modules get an automatic title for the HTML, but no entry in the index. + title = "Module " & extractFilename(changeFileExt(d.filename, "")) let bodyname = if d.hasToc: "doc.body_toc" else: "doc.body_no_toc" content = ropeFormatNamedVars(getConfigVar(bodyname), ["title", diff --git a/compiler/evals.nim b/compiler/evals.nim deleted file mode 100644 index 151adf690..000000000 --- a/compiler/evals.nim +++ /dev/null @@ -1,1502 +0,0 @@ -# -# -# The Nimrod Compiler -# (c) Copyright 2013 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -# This file implements the evaluator for Nimrod code. -# The evaluator is very slow, but simple. Since this -# is used mainly for evaluating macros and some other -# stuff at compile time, performance is not that -# important. - -import - strutils, magicsys, lists, options, ast, astalgo, trees, treetab, nimsets, - msgs, os, condsyms, idents, renderer, types, passes, semfold, transf, - parser, ropes, rodread, idgen, osproc, streams, evaltempl - -when hasFFI: - import evalffi - -type - PStackFrame* = ref TStackFrame - TStackFrame* = object - prc: PSym # current prc; proc that is evaluated - slots: TNodeSeq # parameters passed to the proc + locals; - # parameters come first - call: PNode - next: PStackFrame # for stacking - - TEvalMode* = enum ## reason for evaluation - emRepl, ## evaluate because in REPL mode - emConst, ## evaluate for 'const' according to spec - emOptimize, ## evaluate for optimization purposes (same as - ## emConst?) - emStatic ## evaluate for enforced compile time eval - ## ('static' context) - - TSandboxFlag* = enum ## what the evaluation engine should allow - allowCast, ## allow unsafe language feature: 'cast' - allowFFI, ## allow the FFI - allowInfiniteLoops ## allow endless loops - TSandboxFlags* = set[TSandboxFlag] - - TEvalContext* = object of passes.TPassContext - module*: PSym - tos*: PStackFrame # top of stack - lastException*: PNode - callsite: PNode # for 'callsite' magic - mode*: TEvalMode - features: TSandboxFlags - globals*: TIdNodeTable # state of global vars - getType*: proc(n: PNode): PNode {.closure.} - handleIsOperator*: proc(n: PNode): PNode {.closure.} - - PEvalContext* = ref TEvalContext - - TEvalFlag = enum - efNone, efLValue - TEvalFlags = set[TEvalFlag] - -const - evalMaxIterations = 500_000 # max iterations of all loops - evalMaxRecDepth = 10_000 # max recursion depth for evaluation - -# other idea: use a timeout! -> Wether code compiles depends on the machine -# the compiler runs on then! Bad idea! - -proc newStackFrame*(): PStackFrame = - new(result) - result.slots = @[] - -proc newEvalContext*(module: PSym, mode: TEvalMode): PEvalContext = - new(result) - result.module = module - result.mode = mode - result.features = {allowFFI} - initIdNodeTable(result.globals) - -proc pushStackFrame*(c: PEvalContext, t: PStackFrame) {.inline.} = - t.next = c.tos - c.tos = t - -proc popStackFrame*(c: PEvalContext) {.inline.} = - if c.tos != nil: c.tos = c.tos.next - else: InternalError("popStackFrame") - -proc evalMacroCall*(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode -proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode - -proc raiseCannotEval(c: PEvalContext, info: TLineInfo): PNode = - if defined(debug) and gVerbosity >= 3: writeStackTrace() - result = newNodeI(nkExceptBranch, info) - # creating a nkExceptBranch without sons - # means that it could not be evaluated - -proc stackTraceAux(x: PStackFrame) = - if x != nil: - stackTraceAux(x.next) - var info = if x.call != nil: x.call.info else: UnknownLineInfo() - # we now use the same format as in system/except.nim - var s = toFilename(info) - var line = toLineNumber(info) - if line > 0: - add(s, '(') - add(s, $line) - add(s, ')') - if x.prc != nil: - for k in 1..max(1, 25-s.len): add(s, ' ') - add(s, x.prc.name.s) - MsgWriteln(s) - -proc stackTrace(c: PEvalContext, info: TLineInfo, msg: TMsgKind, arg = "") = - MsgWriteln("stack trace: (most recent call last)") - stackTraceAux(c.tos) - LocalError(info, msg, arg) - -template isSpecial(n: PNode): bool = n.kind == nkExceptBranch -template bailout() {.dirty.} = - if isSpecial(result): return - -template evalX(n, flags) {.dirty.} = - result = evalAux(c, n, flags) - bailout() - -proc myreset(n: PNode) = - when defined(system.reset): - var oldInfo = n.info - reset(n[]) - n.info = oldInfo - -proc evalIf(c: PEvalContext, n: PNode): PNode = - var i = 0 - var length = sonsLen(n) - while (i < length) and (sonsLen(n.sons[i]) >= 2): - evalX(n.sons[i].sons[0], {}) - if result.kind == nkIntLit and result.intVal != 0: - return evalAux(c, n.sons[i].sons[1], {}) - inc(i) - if (i < length) and (sonsLen(n.sons[i]) < 2): - result = evalAux(c, n.sons[i].sons[0], {}) - else: - result = emptyNode - -proc evalCase(c: PEvalContext, n: PNode): PNode = - evalX(n.sons[0], {}) - var res = result - result = emptyNode - for i in countup(1, sonsLen(n) - 1): - if n.sons[i].kind == nkOfBranch: - for j in countup(0, sonsLen(n.sons[i]) - 2): - if overlap(res, n.sons[i].sons[j]): - return evalAux(c, lastSon(n.sons[i]), {}) - else: - result = evalAux(c, lastSon(n.sons[i]), {}) - -var - gWhileCounter: int # Use a counter to prevent endless loops! - # We make this counter global, because otherwise - # nested loops could make the compiler extremely slow. - gNestedEvals: int # count the recursive calls to ``evalAux`` to prevent - # endless recursion - -proc evalWhile(c: PEvalContext, n: PNode): PNode = - while true: - evalX(n.sons[0], {}) - if getOrdValue(result) == 0: - result = emptyNode; break - result = evalAux(c, n.sons[1], {}) - case result.kind - of nkBreakStmt: - if result.sons[0].kind == nkEmpty: - result = emptyNode # consume ``break`` token - # Bugfix (see tmacro2): but break in any case! - break - of nkExceptBranch, nkReturnToken: break - else: nil - dec(gWhileCounter) - if gWhileCounter <= 0: - if allowInfiniteLoops in c.features: - gWhileCounter = 0 - else: - stackTrace(c, n.info, errTooManyIterations) - break - -proc evalBlock(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {}) - if result.kind == nkBreakStmt: - if result.sons[0] != nil: - assert(result.sons[0].kind == nkSym) - if n.sons[0].kind != nkEmpty: - assert(n.sons[0].kind == nkSym) - if result.sons[0].sym.id == n.sons[0].sym.id: result = emptyNode - # blocks can only be left with an explicit label now! - #else: - # result = emptyNode # consume ``break`` token - -proc evalFinally(c: PEvalContext, n, exc: PNode): PNode = - var finallyNode = lastSon(n) - if finallyNode.kind == nkFinally: - result = evalAux(c, finallyNode, {}) - if result.kind != nkExceptBranch: result = exc - else: - result = exc - -proc evalTry(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[0], {}) - case result.kind - of nkBreakStmt, nkReturnToken: - nil - of nkExceptBranch: - if sonsLen(result) >= 1: - # creating a nkExceptBranch without sons means that it could not be - # evaluated - var exc = result - var i = 1 - var length = sonsLen(n) - while (i < length) and (n.sons[i].kind == nkExceptBranch): - var blen = sonsLen(n.sons[i]) - if blen == 1: - # general except section: - result = evalAux(c, n.sons[i].sons[0], {}) - exc = result - break - else: - for j in countup(0, blen - 2): - assert(n.sons[i].sons[j].kind == nkType) - let a = exc.typ.skipTypes(abstractPtrs) - let b = n.sons[i].sons[j].typ.skipTypes(abstractPtrs) - if a == b: - result = evalAux(c, n.sons[i].sons[blen - 1], {}) - exc = result - break - inc(i) - result = evalFinally(c, n, exc) - else: result = evalFinally(c, n, emptyNode) - -proc getNullValue(typ: PType, info: TLineInfo): PNode -proc getNullValueAux(obj: PNode, result: PNode) = - case obj.kind - of nkRecList: - for i in countup(0, sonsLen(obj) - 1): getNullValueAux(obj.sons[i], result) - of nkRecCase: - getNullValueAux(obj.sons[0], result) - for i in countup(1, sonsLen(obj) - 1): - getNullValueAux(lastSon(obj.sons[i]), result) - of nkSym: - var s = obj.sym - var p = newNodeIT(nkExprColonExpr, result.info, s.typ) - addSon(p, newSymNode(s, result.info)) - addSon(p, getNullValue(s.typ, result.info)) - addSon(result, p) - else: InternalError(result.info, "getNullValueAux") - -proc getNullValue(typ: PType, info: TLineInfo): PNode = - var t = skipTypes(typ, abstractRange-{tyTypeDesc}) - result = emptyNode - case t.kind - of tyBool, tyEnum, tyChar, tyInt..tyInt64: - result = newNodeIT(nkIntLit, info, t) - of tyUInt..tyUInt64: - result = newNodeIT(nkUIntLit, info, t) - of tyFloat..tyFloat128: - result = newNodeIt(nkFloatLit, info, t) - of tyVar, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr, - tyStmt, tyTypeDesc, tyStatic, tyProc: - result = newNodeIT(nkNilLit, info, t) - of tyObject: - result = newNodeIT(nkPar, info, t) - getNullValueAux(t.n, result) - # initialize inherited fields: - var base = t.sons[0] - while base != nil: - getNullValueAux(skipTypes(base, skipPtrs).n, result) - base = base.sons[0] - of tyArray, tyArrayConstr: - result = newNodeIT(nkBracket, info, t) - for i in countup(0, int(lengthOrd(t)) - 1): - addSon(result, getNullValue(elemType(t), info)) - of tyTuple: - # XXX nkExprColonExpr is out of fashion ... - result = newNodeIT(nkPar, info, t) - for i in countup(0, sonsLen(t) - 1): - var p = newNodeIT(nkExprColonExpr, info, t.sons[i]) - var field = if t.n != nil: t.n.sons[i].sym else: newSym( - skField, getIdent(":tmp" & $i), t.owner, info) - addSon(p, newSymNode(field, info)) - addSon(p, getNullValue(t.sons[i], info)) - addSon(result, p) - of tySet: - result = newNodeIT(nkCurly, info, t) - else: InternalError("getNullValue: " & $t.kind) - -proc evalVarValue(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n, {}) - if result.kind in {nkType..nkNilLit}: result = result.copyNode - -proc allocSlot(c: PStackFrame; sym: PSym): int = - result = sym.position + ord(sym.kind == skParam) - if result == 0 and sym.kind != skResult: - result = c.slots.len - if result == 0: result = 1 - sym.position = result - setLen(c.slots, max(result+1, c.slots.len)) - -proc setSlot(c: PStackFrame, sym: PSym, val: PNode) = - assert sym.owner == c.prc or sfFromGeneric in sym.flags - let idx = allocSlot(c, sym) - c.slots[idx] = val - -proc setVar(c: PEvalContext, v: PSym, n: PNode) = - if sfGlobal notin v.flags: setSlot(c.tos, v, n) - else: IdNodeTablePut(c.globals, v, n) - -proc evalVar(c: PEvalContext, n: PNode): PNode = - for i in countup(0, sonsLen(n) - 1): - let a = n.sons[i] - if a.kind == nkCommentStmt: continue - #assert(a.sons[0].kind == nkSym) can happen for transformed vars - if a.kind == nkVarTuple: - result = evalVarValue(c, a.lastSon) - if result.kind in {nkType..nkNilLit}: - result = result.copyNode - bailout() - if result.kind != nkPar: - return raiseCannotEval(c, n.info) - for i in 0 .. a.len-3: - var v = a.sons[i].sym - setVar(c, v, result.sons[i]) - else: - if a.sons[2].kind != nkEmpty: - result = evalVarValue(c, a.sons[2]) - bailout() - else: - result = getNullValue(a.sons[0].typ, a.sons[0].info) - if a.sons[0].kind == nkSym: - var v = a.sons[0].sym - setVar(c, v, result) - else: - # assign to a.sons[0]: - var x = result - evalX(a.sons[0], {}) - myreset(x) - x.kind = result.kind - x.typ = result.typ - case x.kind - of nkCharLit..nkInt64Lit: x.intVal = result.intVal - of nkFloatLit..nkFloat64Lit: x.floatVal = result.floatVal - of nkStrLit..nkTripleStrLit: x.strVal = result.strVal - of nkIdent: x.ident = result.ident - of nkSym: x.sym = result.sym - else: - if x.kind notin {nkEmpty..nkNilLit}: - discardSons(x) - for j in countup(0, sonsLen(result) - 1): addSon(x, result.sons[j]) - result = emptyNode - -proc aliasNeeded(n: PNode, flags: TEvalFlags): bool = - result = efLValue in flags or n.typ == nil or - n.typ.kind in {tyExpr, tyStatic, tyStmt, tyTypeDesc} - -proc evalVariable(c: PStackFrame, sym: PSym, flags: TEvalFlags): PNode = - # We need to return a node to the actual value, - # which can be modified. - assert sym.position != 0 or skResult == sym.kind - var x = c - while x != nil: - if sym.owner == x.prc: - result = x.slots[sym.position] - assert result != nil - if not aliasNeeded(result, flags): - result = copyTree(result) - return - x = x.next - #internalError(sym.info, "cannot eval " & sym.name.s & " " & $sym.position) - result = raiseCannotEval(nil, sym.info) - #result = emptyNode - -proc evalGlobalVar(c: PEvalContext, s: PSym, flags: TEvalFlags): PNode = - if sfCompileTime in s.flags or c.mode == emRepl or s.kind == skForVar: - result = IdNodeTableGet(c.globals, s) - if result != nil: - if not aliasNeeded(result, flags): - result = copyTree(result) - else: - when hasFFI: - if sfImportc in s.flags and allowFFI in c.features: - result = importcSymbol(s) - IdNodeTablePut(c.globals, s, result) - return result - - result = s.ast - if result == nil or result.kind == nkEmpty: - result = getNullValue(s.typ, s.info) - else: - result = evalAux(c, result, {}) - if isSpecial(result): return - IdNodeTablePut(c.globals, s, result) - else: - result = raiseCannotEval(nil, s.info) - -proc optBody(c: PEvalContext, s: PSym): PNode = - result = s.getBody - -proc evalCall(c: PEvalContext, n: PNode): PNode = - var d = newStackFrame() - d.call = n - var prc = n.sons[0] - let isClosure = prc.kind == nkClosure - setlen(d.slots, sonsLen(n) + ord(isClosure)) - if isClosure: - #debug prc - evalX(prc.sons[1], {efLValue}) - d.slots[sonsLen(n)] = result - result = evalAux(c, prc.sons[0], {}) - else: - result = evalAux(c, prc, {}) - - if isSpecial(result): return - prc = result - # bind the actual params to the local parameter of a new binding - if prc.kind != nkSym: - InternalError(n.info, "evalCall " & n.renderTree) - return - d.prc = prc.sym - if prc.sym.kind notin {skProc, skConverter, skMacro}: - InternalError(n.info, "evalCall") - return - for i in countup(1, sonsLen(n) - 1): - evalX(n.sons[i], {}) - d.slots[i] = result - if n.typ != nil: d.slots[0] = getNullValue(n.typ, n.info) - - when hasFFI: - if sfImportc in prc.sym.flags and allowFFI in c.features: - var newCall = newNodeI(nkCall, n.info, n.len) - newCall.sons[0] = evalGlobalVar(c, prc.sym, {}) - for i in 1 .. <n.len: - newCall.sons[i] = d.slots[i] - return callForeignFunction(newCall) - - pushStackFrame(c, d) - evalX(optBody(c, prc.sym), {}) - if n.typ != nil: result = d.slots[0] - popStackFrame(c) - -proc evalArrayAccess(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = - evalX(n.sons[0], flags) - var x = result - evalX(n.sons[1], {}) - var idx = getOrdValue(result) - result = emptyNode - case x.kind - of nkPar: - if (idx >= 0) and (idx < sonsLen(x)): - result = x.sons[int(idx)] - if result.kind == nkExprColonExpr: result = result.sons[1] - if not aliasNeeded(result, flags): result = copyTree(result) - else: - stackTrace(c, n.info, errIndexOutOfBounds) - of nkBracket, nkMetaNode: - if (idx >= 0) and (idx < sonsLen(x)): - result = x.sons[int(idx)] - if not aliasNeeded(result, flags): result = copyTree(result) - else: - stackTrace(c, n.info, errIndexOutOfBounds) - of nkStrLit..nkTripleStrLit: - if efLValue in flags: return raiseCannotEval(c, n.info) - result = newNodeIT(nkCharLit, x.info, getSysType(tyChar)) - if (idx >= 0) and (idx < len(x.strVal)): - result.intVal = ord(x.strVal[int(idx) + 0]) - elif idx == len(x.strVal): - nil - else: - stackTrace(c, n.info, errIndexOutOfBounds) - else: stackTrace(c, n.info, errNilAccess) - -proc evalFieldAccess(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = - # a real field access; proc calls have already been transformed - # XXX: field checks! - evalX(n.sons[0], flags) - var x = result - if x.kind != nkPar: return raiseCannotEval(c, n.info) - # this is performance critical: - var field = n.sons[1].sym - result = x.sons[field.position] - if result.kind == nkExprColonExpr: result = result.sons[1] - if not aliasNeeded(result, flags): result = copyTree(result) - -proc evalAsgn(c: PEvalContext, n: PNode): PNode = - var a = n.sons[0] - if a.kind == nkBracketExpr and a.sons[0].typ.kind in {tyString, tyCString}: - evalX(a.sons[0], {efLValue}) - var x = result - evalX(a.sons[1], {}) - var idx = getOrdValue(result) - - evalX(n.sons[1], {}) - if result.kind notin {nkIntLit, nkCharLit}: return c.raiseCannotEval(n.info) - - if idx >= 0 and idx < len(x.strVal): - x.strVal[int(idx)] = chr(int(result.intVal)) - else: - stackTrace(c, n.info, errIndexOutOfBounds) - else: - evalX(n.sons[0], {efLValue}) - var x = result - evalX(n.sons[1], {}) - myreset(x) - x.kind = result.kind - x.typ = result.typ - case x.kind - of nkCharLit..nkInt64Lit: x.intVal = result.intVal - of nkFloatLit..nkFloat64Lit: x.floatVal = result.floatVal - of nkStrLit..nkTripleStrLit: x.strVal = result.strVal - of nkIdent: x.ident = result.ident - of nkSym: x.sym = result.sym - else: - if x.kind notin {nkEmpty..nkNilLit}: - discardSons(x) - for i in countup(0, sonsLen(result) - 1): addSon(x, result.sons[i]) - result = emptyNode - assert result.kind == nkEmpty - -proc evalSwap(c: PEvalContext, n: PNode): PNode = - evalX(n.sons[0], {efLValue}) - var x = result - evalX(n.sons[1], {efLValue}) - if x.kind != result.kind: - stackTrace(c, n.info, errCannotInterpretNodeX, $n.kind) - else: - case x.kind - of nkCharLit..nkInt64Lit: swap(x.intVal, result.intVal) - of nkFloatLit..nkFloat64Lit: swap(x.floatVal, result.floatVal) - of nkStrLit..nkTripleStrLit: swap(x.strVal, result.strVal) - of nkIdent: swap(x.ident, result.ident) - of nkSym: swap(x.sym, result.sym) - else: - var tmpn = copyTree(x) - discardSons(x) - for i in countup(0, sonsLen(result) - 1): addSon(x, result.sons[i]) - discardSons(result) - for i in countup(0, sonsLen(tmpn) - 1): addSon(result, tmpn.sons[i]) - result = emptyNode - -proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = - var s = n.sym - case s.kind - of skProc, skConverter, skMacro, skType: - result = n - #result = s.getBody - of skVar, skLet, skForVar, skTemp, skResult: - if sfGlobal notin s.flags: - result = evalVariable(c.tos, s, flags) - else: - result = evalGlobalVar(c, s, flags) - of skParam: - # XXX what about LValue? - if s.position + 1 <% c.tos.slots.len: - result = c.tos.slots[s.position + 1] - of skConst: result = s.ast - of skEnumField: result = newIntNodeT(s.position, n) - else: result = nil - let mask = if hasFFI and allowFFI in c.features: {sfForward} - else: {sfImportc, sfForward} - if result == nil or mask * s.flags != {}: - result = raiseCannotEval(c, n.info) - -proc evalIncDec(c: PEvalContext, n: PNode, sign: biggestInt): PNode = - evalX(n.sons[1], {efLValue}) - var a = result - evalX(n.sons[2], {}) - var b = result - case a.kind - of nkCharLit..nkInt64Lit: a.intval = a.intVal + sign * getOrdValue(b) - else: return raiseCannotEval(c, n.info) - result = emptyNode - -proc getStrValue(n: PNode): string = - case n.kind - of nkStrLit..nkTripleStrLit: result = n.strVal - else: - InternalError(n.info, "getStrValue") - result = "" - -proc evalEcho(c: PEvalContext, n: PNode): PNode = - for i in countup(1, sonsLen(n) - 1): - evalX(n.sons[i], {}) - Write(stdout, getStrValue(result)) - writeln(stdout, "") - result = emptyNode - -proc evalExit(c: PEvalContext, n: PNode): PNode = - if c.mode in {emRepl, emStatic}: - evalX(n.sons[1], {}) - Message(n.info, hintQuitCalled) - quit(int(getOrdValue(result))) - else: - result = raiseCannotEval(c, n.info) - -proc evalOr(c: PEvalContext, n: PNode): PNode = - evalX(n.sons[1], {}) - if result.intVal == 0: result = evalAux(c, n.sons[2], {}) - -proc evalAnd(c: PEvalContext, n: PNode): PNode = - evalX(n.sons[1], {}) - if result.intVal != 0: result = evalAux(c, n.sons[2], {}) - -proc evalNew(c: PEvalContext, n: PNode): PNode = - #if c.mode == emOptimize: return raiseCannotEval(c, n.info) - - # we ignore the finalizer for now and most likely forever :-) - evalX(n.sons[1], {efLValue}) - var a = result - var t = skipTypes(n.sons[1].typ, abstractVar) - if a.kind == nkEmpty: InternalError(n.info, "first parameter is empty") - myreset(a) - let u = getNullValue(t.sons[0], n.info) - a.kind = u.kind - a.typ = t - shallowCopy(a.sons, u.sons) - result = emptyNode - when false: - a.kind = nkRefTy - a.info = n.info - a.typ = t - a.sons = nil - addSon(a, getNullValue(t.sons[0], n.info)) - result = emptyNode - -proc evalDeref(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = - evalX(n.sons[0], {efLValue}) - case result.kind - of nkNilLit: stackTrace(c, n.info, errNilAccess) - of nkRefTy: - # XXX efLValue? - result = result.sons[0] - else: - if skipTypes(n.sons[0].typ, abstractInst).kind != tyRef: - result = raiseCannotEval(c, n.info) - -proc evalAddr(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = - evalX(n.sons[0], {efLValue}) - var a = result - var t = newType(tyPtr, c.module) - addSonSkipIntLit(t, a.typ) - result = newNodeIT(nkRefTy, n.info, t) - addSon(result, a) - -proc evalConv(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - if result.typ != nil: - var a = result - result = foldConv(n, a) - if result == nil: - # foldConv() cannot deal with everything that we want to do here: - result = a - -proc evalCast(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = - if allowCast in c.features: - when hasFFI: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - InternalAssert result.typ != nil - result = fficast(result, n.typ) - else: - result = evalConv(c, n) - else: - result = raiseCannotEval(c, n.info) - -proc evalCheckedFieldAccess(c: PEvalContext, n: PNode, - flags: TEvalFlags): PNode = - result = evalAux(c, n.sons[0], flags) - -proc evalUpConv(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = - result = evalAux(c, n.sons[0], flags) - if isSpecial(result): return - var dest = skipTypes(n.typ, abstractPtrs) - var src = skipTypes(result.typ, abstractPtrs) - if inheritanceDiff(src, dest) > 0: - stackTrace(c, n.info, errInvalidConversionFromTypeX, typeToString(src)) - -proc evalRangeChck(c: PEvalContext, n: PNode): PNode = - evalX(n.sons[0], {}) - var x = result - evalX(n.sons[1], {}) - var a = result - evalX(n.sons[2], {}) - var b = result - if leValueConv(a, x) and leValueConv(x, b): - result = x # a <= x and x <= b - result.typ = n.typ - else: - stackTrace(c, n.info, errGenerated, - msgKindToString(errIllegalConvFromXtoY) % [ - typeToString(n.sons[0].typ), typeToString(n.typ)]) - -proc evalConvStrToCStr(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[0], {}) - if isSpecial(result): return - result.typ = n.typ - -proc evalConvCStrToStr(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[0], {}) - if isSpecial(result): return - result.typ = n.typ - -proc evalRaise(c: PEvalContext, n: PNode): PNode = - if c.mode in {emRepl, emStatic}: - if n.sons[0].kind != nkEmpty: - result = evalAux(c, n.sons[0], {}) - if isSpecial(result): return - var a = result - result = newNodeIT(nkExceptBranch, n.info, a.typ) - addSon(result, a) - c.lastException = result - elif c.lastException != nil: - result = c.lastException - else: - stackTrace(c, n.info, errExceptionAlreadyHandled) - result = newNodeIT(nkExceptBranch, n.info, nil) - addSon(result, ast.emptyNode) - else: - result = raiseCannotEval(c, n.info) - -proc evalReturn(c: PEvalContext, n: PNode): PNode = - if n.sons[0].kind != nkEmpty: - result = evalAsgn(c, n.sons[0]) - if isSpecial(result): return - result = newNodeIT(nkReturnToken, n.info, nil) - -proc evalProc(c: PEvalContext, n: PNode): PNode = - if n.sons[genericParamsPos].kind == nkEmpty: - var s = n.sons[namePos].sym - if (resultPos < sonsLen(n)) and (n.sons[resultPos].kind != nkEmpty): - var v = n.sons[resultPos].sym - result = getNullValue(v.typ, n.info) - if c.tos.slots.len == 0: setLen(c.tos.slots, 1) - c.tos.slots[0] = result - #IdNodeTablePut(c.tos.mapping, v, result) - result = evalAux(c, s.getBody, {}) - if result.kind == nkReturnToken: - result = c.tos.slots[0] - else: - result = evalAux(c, s.getBody, {}) - if result.kind == nkReturnToken: - result = emptyNode - else: - result = emptyNode - -proc evalHigh(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - case skipTypes(n.sons[1].typ, abstractVar).kind - of tyOpenArray, tySequence, tyVarargs: - result = newIntNodeT(sonsLen(result)-1, n) - of tyString: result = newIntNodeT(len(result.strVal) - 1, n) - else: InternalError(n.info, "evalHigh") - -proc evalOf(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - result = newIntNodeT(ord(inheritanceDiff(result.typ, n.sons[2].typ) >= 0), n) - -proc evalSetLengthStr(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - var b = result - case a.kind - of nkStrLit..nkTripleStrLit: - var newLen = int(getOrdValue(b)) - setlen(a.strVal, newLen) - else: InternalError(n.info, "evalSetLengthStr") - result = emptyNode - -proc evalSetLengthSeq(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - var b = result - if a.kind != nkBracket: - InternalError(n.info, "evalSetLengthSeq") - return - var newLen = int(getOrdValue(b)) - var oldLen = sonsLen(a) - setlen(a.sons, newLen) - for i in countup(oldLen, newLen - 1): - a.sons[i] = getNullValue(skipTypes(n.sons[1].typ, abstractVar), n.info) - result = emptyNode - -proc evalNewSeq(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - var b = result - var t = skipTypes(n.sons[1].typ, abstractVar) - if a.kind == nkEmpty: InternalError(n.info, "first parameter is empty") - myreset(a) - a.kind = nkBracket - a.info = n.info - a.typ = t - a.sons = nil - var L = int(getOrdValue(b)) - newSeq(a.sons, L) - for i in countup(0, L-1): - a.sons[i] = getNullValue(t.sons[0], n.info) - result = emptyNode - -proc evalIncl(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - var b = result - if not inSet(a, b): addSon(a, copyTree(b)) - result = emptyNode - -proc evalExcl(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - var b = newNodeIT(nkCurly, n.info, n.sons[1].typ) - addSon(b, result) - var r = diffSets(a, b) - discardSons(a) - for i in countup(0, sonsLen(r) - 1): addSon(a, r.sons[i]) - result = emptyNode - -proc evalAppendStrCh(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - var b = result - case a.kind - of nkStrLit..nkTripleStrLit: add(a.strVal, chr(int(getOrdValue(b)))) - else: return raiseCannotEval(c, n.info) - result = emptyNode - -proc evalConStrStr(c: PEvalContext, n: PNode): PNode = - # we cannot use ``evalOp`` for this as we can here have more than 2 arguments - var a = newNodeIT(nkStrLit, n.info, n.typ) - a.strVal = "" - for i in countup(1, sonsLen(n) - 1): - result = evalAux(c, n.sons[i], {}) - if isSpecial(result): return - a.strVal.add(getStrOrChar(result)) - result = a - -proc evalAppendStrStr(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - var b = result - case a.kind - of nkStrLit..nkTripleStrLit: a.strVal = a.strVal & getStrOrChar(b) - else: return raiseCannotEval(c, n.info) - result = emptyNode - -proc evalAppendSeqElem(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - var b = result - if a.kind == nkBracket: addSon(a, copyTree(b)) - else: return raiseCannotEval(c, n.info) - result = emptyNode - -proc evalRepr(c: PEvalContext, n: PNode): PNode = - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - result = newStrNodeT(renderTree(result, {renderNoComments}), n) - -proc isEmpty(n: PNode): bool = - result = n != nil and n.kind == nkEmpty - -proc evalParseExpr(c: PEvalContext, n: PNode): PNode = - var code = evalAux(c, n.sons[1], {}) - var ast = parseString(code.getStrValue, code.info.toFilename, - code.info.line.int) - if sonsLen(ast) != 1: - GlobalError(code.info, errExprExpected, "multiple statements") - result = ast.sons[0] - #result.typ = newType(tyExpr, c.module) - -proc evalParseStmt(c: PEvalContext, n: PNode): PNode = - var code = evalAux(c, n.sons[1], {}) - result = parseString(code.getStrValue, code.info.toFilename, - code.info.line.int) - #result.typ = newType(tyStmt, c.module) - -proc evalTypeTrait*(trait, operand: PNode, context: PSym): PNode = - let typ = operand.typ.skipTypes({tyTypeDesc}) - case trait.sym.name.s.normalize - of "name": - result = newStrNode(nkStrLit, typ.typeToString(preferName)) - result.typ = newType(tyString, context) - result.info = trait.info - of "arity": - result = newIntNode(nkIntLit, typ.n.len-1) - result.typ = newType(tyInt, context) - result.info = trait.info - else: - internalAssert false - -proc expectString(n: PNode) = - if n.kind notin nkStrKinds: - GlobalError(n.info, errStringLiteralExpected) - -proc evalSlurp*(e: PNode, module: PSym): PNode = - expectString(e) - result = newNodeIT(nkStrLit, e.info, getSysType(tyString)) - try: - var filename = e.strVal.FindFile - result.strVal = readFile(filename) - # we produce a fake include statement for every slurped filename, so that - # the module dependencies are accurate: - appendToModule(module, newNode(nkIncludeStmt, e.info, @[ - newStrNode(nkStrLit, filename)])) - except EIO: - result.strVal = "" - LocalError(e.info, errCannotOpenFile, e.strVal) - -proc readOutput(p: PProcess): string = - result = "" - var output = p.outputStream - discard p.waitForExit - while not output.atEnd: - result.add(output.readLine) - -proc evalStaticExec*(cmd, input: PNode): PNode = - expectString(cmd) - var p = startCmd(cmd.strVal) - if input != nil: - expectString(input) - p.inputStream.write(input.strVal) - p.inputStream.close() - result = newStrNode(nkStrLit, p.readOutput) - result.typ = getSysType(tyString) - result.info = cmd.info - -proc evalExpandToAst(c: PEvalContext, original: PNode): PNode = - var - n = original.copyTree - macroCall = n.sons[1] - expandedSym = macroCall.sons[0].sym - - for i in countup(1, macroCall.sonsLen - 1): - macroCall.sons[i] = evalAux(c, macroCall.sons[i], {}) - - case expandedSym.kind - of skTemplate: - let genSymOwner = if c.tos != nil and c.tos.prc != nil: - c.tos.prc - else: - c.module - result = evalTemplate(macroCall, expandedSym, genSymOwner) - of skMacro: - # At this point macroCall.sons[0] is nkSym node. - # To be completely compatible with normal macro invocation, - # we want to replace it with nkIdent node featuring - # the original unmangled macro name. - macroCall.sons[0] = newIdentNode(expandedSym.name, expandedSym.info) - result = evalMacroCall(c, macroCall, original, expandedSym) - else: - InternalError(macroCall.info, - "ExpandToAst: expanded symbol is no macro or template") - result = emptyNode - -proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode = - var m = getMagic(n) - case m - of mNone: result = evalCall(c, n) - of mOf: result = evalOf(c, n) - of mSizeOf: result = raiseCannotEval(c, n.info) - of mHigh: result = evalHigh(c, n) - of mExit: result = evalExit(c, n) - of mNew, mNewFinalize: result = evalNew(c, n) - of mNewSeq: result = evalNewSeq(c, n) - of mSwap: result = evalSwap(c, n) - of mInc: result = evalIncDec(c, n, 1) - of ast.mDec: result = evalIncDec(c, n, - 1) - of mEcho: result = evalEcho(c, n) - of mSetLengthStr: result = evalSetLengthStr(c, n) - of mSetLengthSeq: result = evalSetLengthSeq(c, n) - of mIncl: result = evalIncl(c, n) - of mExcl: result = evalExcl(c, n) - of mAnd: result = evalAnd(c, n) - of mOr: result = evalOr(c, n) - of mAppendStrCh: result = evalAppendStrCh(c, n) - of mAppendStrStr: result = evalAppendStrStr(c, n) - of mAppendSeqElem: result = evalAppendSeqElem(c, n) - of mParseExprToAst: result = evalParseExpr(c, n) - of mParseStmtToAst: result = evalParseStmt(c, n) - of mExpandToAst: result = evalExpandToAst(c, n) - of mTypeTrait: - let operand = evalAux(c, n.sons[1], {}) - result = evalTypeTrait(n[0], operand, c.module) - of mIs: - n.sons[1] = evalAux(c, n.sons[1], {}) - result = c.handleIsOperator(n) - of mSlurp: result = evalSlurp(evalAux(c, n.sons[1], {}), c.module) - of mStaticExec: - let cmd = evalAux(c, n.sons[1], {}) - let input = if n.sonsLen == 3: evalAux(c, n.sons[2], {}) else: nil - result = evalStaticExec(cmd, input) - of mNLen: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = newNodeIT(nkIntLit, n.info, n.typ) - case a.kind - of nkEmpty..nkNilLit: nil - else: result.intVal = sonsLen(a) - of mNChild: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {efLValue}) - if isSpecial(result): return - var k = getOrdValue(result) - if not (a.kind in {nkEmpty..nkNilLit}) and (k >= 0) and (k < sonsLen(a)): - result = a.sons[int(k)] - if result == nil: result = newNode(nkEmpty) - else: - stackTrace(c, n.info, errIndexOutOfBounds) - result = emptyNode - of mNSetChild: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {efLValue}) - if isSpecial(result): return - var b = result - result = evalAux(c, n.sons[3], {efLValue}) - if isSpecial(result): return - var k = getOrdValue(b) - if (k >= 0) and (k < sonsLen(a)) and not (a.kind in {nkEmpty..nkNilLit}): - a.sons[int(k)] = result - else: - stackTrace(c, n.info, errIndexOutOfBounds) - result = emptyNode - of mNAdd: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {efLValue}) - if isSpecial(result): return - addSon(a, result) - result = a - of mNAddMultiple: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {efLValue}) - if isSpecial(result): return - for i in countup(0, sonsLen(result) - 1): addSon(a, result.sons[i]) - result = a - of mNDel: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {efLValue}) - if isSpecial(result): return - var b = result - result = evalAux(c, n.sons[3], {efLValue}) - if isSpecial(result): return - for i in countup(0, int(getOrdValue(result)) - 1): - delSon(a, int(getOrdValue(b))) - result = emptyNode - of mNKind: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - var a = result - result = newNodeIT(nkIntLit, n.info, n.typ) - result.intVal = ord(a.kind) - of mNIntVal: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - var a = result - result = newNodeIT(nkIntLit, n.info, n.typ) - case a.kind - of nkCharLit..nkInt64Lit: result.intVal = a.intVal - else: stackTrace(c, n.info, errFieldXNotFound, "intVal") - of mNFloatVal: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - var a = result - result = newNodeIT(nkFloatLit, n.info, n.typ) - case a.kind - of nkFloatLit..nkFloat64Lit: result.floatVal = a.floatVal - else: stackTrace(c, n.info, errFieldXNotFound, "floatVal") - of mNSymbol: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - if result.kind != nkSym: stackTrace(c, n.info, errFieldXNotFound, "symbol") - of mNIdent: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - if result.kind != nkIdent: stackTrace(c, n.info, errFieldXNotFound, "ident") - of mNGetType: - var ast = evalAux(c, n.sons[1], {}) - InternalAssert c.getType != nil - result = c.getType(ast) - of mNStrVal: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - var a = result - result = newNodeIT(nkStrLit, n.info, n.typ) - case a.kind - of nkStrLit..nkTripleStrLit: result.strVal = a.strVal - else: stackTrace(c, n.info, errFieldXNotFound, "strVal") - of mNSetIntVal: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - if a.kind in {nkCharLit..nkInt64Lit} and - result.kind in {nkCharLit..nkInt64Lit}: - a.intVal = result.intVal - else: - stackTrace(c, n.info, errFieldXNotFound, "intVal") - result = emptyNode - of mNSetFloatVal: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - if a.kind in {nkFloatLit..nkFloat64Lit} and - result.kind in {nkFloatLit..nkFloat64Lit}: - a.floatVal = result.floatVal - else: - stackTrace(c, n.info, errFieldXNotFound, "floatVal") - result = emptyNode - of mNSetSymbol: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {efLValue}) - if isSpecial(result): return - if a.kind == nkSym and result.kind == nkSym: - a.sym = result.sym - else: - stackTrace(c, n.info, errFieldXNotFound, "symbol") - result = emptyNode - of mNSetIdent: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {efLValue}) - if isSpecial(result): return - if a.kind == nkIdent and result.kind == nkIdent: - a.ident = result.ident - else: - stackTrace(c, n.info, errFieldXNotFound, "ident") - result = emptyNode - of mNSetType: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {efLValue}) - if isSpecial(result): return - InternalAssert result.kind == nkSym and result.sym.kind == skType - a.typ = result.sym.typ - result = emptyNode - of mNSetStrVal: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - - if a.kind in {nkStrLit..nkTripleStrLit} and - result.kind in {nkStrLit..nkTripleStrLit}: - a.strVal = result.strVal - else: stackTrace(c, n.info, errFieldXNotFound, "strVal") - result = emptyNode - of mNNewNimNode: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - var k = getOrdValue(result) - result = evalAux(c, n.sons[2], {efLValue}) - if result.kind == nkExceptBranch: return - var a = result - if k < 0 or k > ord(high(TNodeKind)): - internalError(n.info, "request to create a NimNode with invalid kind") - result = newNodeI(TNodeKind(int(k)), - if a.kind == nkNilLit: n.info else: a.info) - of mNCopyNimNode: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - result = copyNode(result) - of mNCopyNimTree: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - result = copyTree(result) - of mNBindSym: - # trivial implementation: - result = n.sons[1] - of mNGenSym: - evalX(n.sons[1], {efLValue}) - let k = getOrdValue(result) - evalX(n.sons[2], {efLValue}) - let b = result - let name = if b.strVal.len == 0: ":tmp" else: b.strVal - if k < 0 or k > ord(high(TSymKind)): - internalError(n.info, "request to create a symbol with invalid kind") - result = newSymNode(newSym(k.TSymKind, name.getIdent, c.module, n.info)) - incl(result.sym.flags, sfGenSym) - of mStrToIdent: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - if not (result.kind in {nkStrLit..nkTripleStrLit}): - stackTrace(c, n.info, errFieldXNotFound, "strVal") - return - var a = result - result = newNodeIT(nkIdent, n.info, n.typ) - result.ident = getIdent(a.strVal) - of mIdentToStr: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - var a = result - result = newNodeIT(nkStrLit, n.info, n.typ) - if a.kind == nkSym: - result.strVal = a.sym.name.s - else: - if a.kind != nkIdent: InternalError(n.info, "no ident node") - result.strVal = a.ident.s - of mEqIdent: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - var b = result - result = newNodeIT(nkIntLit, n.info, n.typ) - if (a.kind == nkIdent) and (b.kind == nkIdent): - if a.ident.id == b.ident.id: result.intVal = 1 - of mEqNimrodNode: - result = evalAux(c, n.sons[1], {efLValue}) - if isSpecial(result): return - var a = result - result = evalAux(c, n.sons[2], {efLValue}) - if isSpecial(result): return - var b = result - result = newNodeIT(nkIntLit, n.info, n.typ) - if (a == b) or - (b.kind in {nkNilLit, nkEmpty}) and (a.kind in {nkNilLit, nkEmpty}): - result.intVal = 1 - of mNLineInfo: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - result = newStrNodeT(result.info.toFileLineCol, n) - of mNHint: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - Message(n.info, hintUser, getStrValue(result)) - result = emptyNode - of mNWarning: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - Message(n.info, warnUser, getStrValue(result)) - result = emptyNode - of mNError: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - stackTrace(c, n.info, errUser, getStrValue(result)) - result = emptyNode - of mConStrStr: - result = evalConStrStr(c, n) - of mRepr: - result = evalRepr(c, n) - of mNewString: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - var a = result - result = newNodeIT(nkStrLit, n.info, n.typ) - result.strVal = newString(int(getOrdValue(a))) - of mNewStringOfCap: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - var a = result - result = newNodeIT(nkStrLit, n.info, n.typ) - result.strVal = newString(0) - of mNCallSite: - if c.callsite != nil: result = c.callsite - else: stackTrace(c, n.info, errFieldXNotFound, "callsite") - else: - result = evalAux(c, n.sons[1], {}) - if isSpecial(result): return - var a = result - var b: PNode = nil - var cc: PNode = nil - if sonsLen(n) > 2: - result = evalAux(c, n.sons[2], {}) - if isSpecial(result): return - b = result - if sonsLen(n) > 3: - result = evalAux(c, n.sons[3], {}) - if isSpecial(result): return - cc = result - if isEmpty(a) or isEmpty(b) or isEmpty(cc): result = emptyNode - else: result = evalOp(m, n, a, b, cc) - -proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = - result = emptyNode - dec(gNestedEvals) - if gNestedEvals <= 0: stackTrace(c, n.info, errTooManyIterations) - case n.kind - of nkSym: result = evalSym(c, n, flags) - of nkType..nkNilLit, nkTypeOfExpr: - # nkStrLit is VERY common in the traces, so we should avoid - # the 'copyNode' here. - result = n #.copyNode - of nkAsgn, nkFastAsgn: result = evalAsgn(c, n) - of nkCommand..nkHiddenCallConv: - result = evalMagicOrCall(c, n) - of nkDotExpr: result = evalFieldAccess(c, n, flags) - of nkBracketExpr: - result = evalArrayAccess(c, n, flags) - of nkDerefExpr, nkHiddenDeref: result = evalDeref(c, n, flags) - of nkAddr, nkHiddenAddr: result = evalAddr(c, n, flags) - of nkHiddenStdConv, nkHiddenSubConv, nkConv: result = evalConv(c, n) - of nkCurly, nkBracket, nkRange: - # flags need to be passed here for mNAddMultiple :-( - # XXX this is not correct in every case! - var a = copyNode(n) - for i in countup(0, sonsLen(n) - 1): - result = evalAux(c, n.sons[i], flags) - if isSpecial(result): return - addSon(a, result) - result = a - of nkPar, nkClosure: - var a = copyTree(n) - for i in countup(0, sonsLen(n) - 1): - var it = n.sons[i] - if it.kind == nkExprColonExpr: - result = evalAux(c, it.sons[1], flags) - if isSpecial(result): return - a.sons[i].sons[1] = result - else: - result = evalAux(c, it, flags) - if isSpecial(result): return - a.sons[i] = result - result = a - of nkObjConstr: - let t = skipTypes(n.typ, abstractInst) - var a: PNode - if t.kind == tyRef: - result = newNodeIT(nkRefTy, n.info, t) - a = getNullValue(t.sons[0], n.info) - addSon(result, a) - else: - a = getNullValue(t, n.info) - result = a - for i in countup(1, sonsLen(n) - 1): - let it = n.sons[i] - if it.kind == nkExprColonExpr: - let value = evalAux(c, it.sons[1], flags) - if isSpecial(value): return value - a.sons[it.sons[0].sym.position] = value - else: return raiseCannotEval(c, n.info) - of nkWhenStmt, nkIfStmt, nkIfExpr: result = evalIf(c, n) - of nkWhileStmt: result = evalWhile(c, n) - of nkCaseStmt: result = evalCase(c, n) - of nkVarSection, nkLetSection: result = evalVar(c, n) - of nkTryStmt: result = evalTry(c, n) - of nkRaiseStmt: result = evalRaise(c, n) - of nkReturnStmt: result = evalReturn(c, n) - of nkBreakStmt, nkReturnToken: result = n - of nkBlockExpr, nkBlockStmt: result = evalBlock(c, n) - of nkDiscardStmt: result = evalAux(c, n.sons[0], {}) - of nkCheckedFieldExpr: result = evalCheckedFieldAccess(c, n, flags) - of nkObjDownConv: result = evalAux(c, n.sons[0], flags) - of nkObjUpConv: result = evalUpConv(c, n, flags) - of nkChckRangeF, nkChckRange64, nkChckRange: result = evalRangeChck(c, n) - of nkStringToCString: result = evalConvStrToCStr(c, n) - of nkCStringToString: result = evalConvCStrToStr(c, n) - of nkStmtListExpr, nkStmtList: - for i in countup(0, sonsLen(n) - 1): - result = evalAux(c, n.sons[i], flags) - case result.kind - of nkExceptBranch, nkReturnToken, nkBreakStmt: break - else: nil - of nkProcDef, nkMethodDef, nkMacroDef, nkCommentStmt, nkPragma, - nkTypeSection, nkTemplateDef, nkConstSection, nkIteratorDef, - nkConverterDef, nkIncludeStmt, nkImportStmt, nkFromStmt: - nil - of nkMetaNode: - result = copyTree(n.sons[0]) - result.typ = n.typ - of nkPragmaBlock: - result = evalAux(c, n.sons[1], flags) - of nkCast: - result = evalCast(c, n, flags) - of nkIdentDefs, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr, - nkLambdaKinds, nkContinueStmt, nkIdent, nkParForStmt, nkBindStmt, - nkClosedSymChoice, nkOpenSymChoice: - result = raiseCannotEval(c, n.info) - of nkRefTy: - result = evalAux(c, n.sons[0], flags) - of nkEmpty: - # nkEmpty occurs once in each trace that I looked at - result = n - else: InternalError(n.info, "evalAux: " & $n.kind) - if result == nil: - InternalError(n.info, "evalAux: returned nil " & $n.kind) - inc(gNestedEvals) - -proc tryEval(c: PEvalContext, n: PNode): PNode = - #internalAssert nfTransf in n.flags - var n = transformExpr(c.module, n) - gWhileCounter = evalMaxIterations - gNestedEvals = evalMaxRecDepth - result = evalAux(c, n, {}) - -proc eval*(c: PEvalContext, n: PNode): PNode = - ## eval never returns nil! This simplifies the code a lot and - ## makes it faster too. - result = tryEval(c, n) - if result.kind == nkExceptBranch: - if sonsLen(result) >= 1: - stackTrace(c, n.info, errUnhandledExceptionX, typeToString(result.typ)) - else: - stackTrace(c, result.info, errCannotInterpretNodeX, renderTree(n)) - -proc evalConstExprAux*(p: PEvalContext, module, prc: PSym, e: PNode): PNode = - var s = newStackFrame() - s.call = e - s.prc = prc - pushStackFrame(p, s) - result = tryEval(p, e) - if result != nil and result.kind == nkExceptBranch: result = nil - popStackFrame(p) - -proc setupMacroParam(x: PNode): PNode = - result = x - if result.kind in {nkHiddenSubConv, nkHiddenStdConv}: result = result.sons[1] - -proc evalMacroCall(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode = - # XXX GlobalError() is ugly here, but I don't know a better solution for now - inc(evalTemplateCounter) - if evalTemplateCounter > 100: - GlobalError(n.info, errTemplateInstantiationTooNested) - - c.callsite = nOrig - var s = newStackFrame() - s.call = n - s.prc = sym - var L = n.safeLen - if L == 0: L = 1 - setlen(s.slots, L) - # return value: - s.slots[0] = newNodeIT(nkNilLit, n.info, sym.typ.sons[0]) - # setup parameters: - for i in 1 .. < L: s.slots[i] = setupMacroParam(n.sons[i]) - pushStackFrame(c, s) - discard eval(c, optBody(c, sym)) - result = s.slots[0] - popStackFrame(c) - if cyclicTree(result): GlobalError(n.info, errCyclicTree) - dec(evalTemplateCounter) - c.callsite = nil - -proc myOpen(module: PSym): PPassContext = - var c = newEvalContext(module, emRepl) - c.features = {allowCast, allowFFI, allowInfiniteLoops} - pushStackFrame(c, newStackFrame()) - result = c - -var oldErrorCount: int - -proc myProcess(c: PPassContext, n: PNode): PNode = - # don't eval errornous code: - if oldErrorCount == msgs.gErrorCounter: - result = eval(PEvalContext(c), n) - else: - result = n - oldErrorCount = msgs.gErrorCounter - -const evalPass* = makePass(myOpen, nil, myProcess, myProcess) - diff --git a/compiler/guards.nim b/compiler/guards.nim index f475f5068..4cf06fe02 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2013 Andreas Rumpf +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -9,7 +9,8 @@ ## This module implements the 'implies' relation for guards. -import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents +import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents, + saturate const someEq = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, @@ -25,6 +26,17 @@ const someIn = {mInRange, mInSet} + someHigh = {mHigh} + # we don't list unsigned here because wrap around semantics suck for + # proving anything: + someAdd = {mAddI, mAddI64, mAddF64, mSucc} + someSub = {mSubI, mSubI64, mSubF64, mPred} + someMul = {mMulI, mMulI64, mMulF64} + someDiv = {mDivI, mDivI64, mDivF64} + someMod = {mModI, mModI64} + someMax = {mMaxI, mMaxI64, mMaxF64} + someMin = {mMinI, mMinI64, mMinF64} + proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit} proc isLocation(n: PNode): bool = not n.isValue @@ -69,19 +81,24 @@ proc isLetLocation(m: PNode, isApprox: bool): bool = proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true) -proc getMagicOp(name: string, m: TMagic): PSym = +proc createMagic*(name: string, m: TMagic): PSym = result = newSym(skProc, getIdent(name), nil, unknownLineInfo()) result.magic = m let - opLe = getMagicOp("<=", mLeI) - opLt = getMagicOp("<", mLtI) - opAnd = getMagicOp("and", mAnd) - opOr = getMagicOp("or", mOr) - opNot = getMagicOp("not", mNot) - opIsNil = getMagicOp("isnil", mIsNil) - opContains = getMagicOp("contains", mInSet) - opEq = getMagicOp("==", mEqI) + opLe = createMagic("<=", mLeI) + opLt = createMagic("<", mLtI) + opAnd = createMagic("and", mAnd) + opOr = createMagic("or", mOr) + opNot = createMagic("not", mNot) + opIsNil = createMagic("isnil", mIsNil) + opContains = createMagic("contains", mInSet) + opEq = createMagic("==", mEqI) + opAdd = createMagic("+", mAddI) + opSub = createMagic("-", mSubI) + opMul = createMagic("*", mMulI) + opDiv = createMagic("div", mDivI) + opLen = createMagic("len", mLengthSeq) proc swapArgs(fact: PNode, newOp: PSym): PNode = result = newNodeI(nkCall, fact.info, 3) @@ -137,17 +154,141 @@ proc neg(n: PNode): PNode = result.sons[0] = newSymNode(opNot) result.sons[1] = n -proc buildIsNil(arg: PNode): PNode = - result = newNodeI(nkCall, arg.info, 2) - result.sons[0] = newSymNode(opIsNil) - result.sons[1] = arg +proc buildCall(op: PSym; a: PNode): PNode = + result = newNodeI(nkCall, a.info, 2) + result.sons[0] = newSymNode(op) + result.sons[1] = a + +proc buildCall(op: PSym; a, b: PNode): PNode = + result = newNodeI(nkInfix, a.info, 3) + result.sons[0] = newSymNode(op) + result.sons[1] = a + result.sons[2] = b + +proc `|+|`(a, b: PNode): PNode = + result = copyNode(a) + if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal |+| b.intVal + else: result.floatVal = a.floatVal + b.floatVal + +proc `|*|`(a, b: PNode): PNode = + result = copyNode(a) + if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal |*| b.intVal + else: result.floatVal = a.floatVal * b.floatVal + +proc negate(a, b, res: PNode): PNode = + if b.kind in {nkCharLit..nkUInt64Lit} and b.intVal != low(BiggestInt): + var b = copyNode(b) + b.intVal = -b.intVal + if a.kind in {nkCharLit..nkUInt64Lit}: + b.intVal = b.intVal |+| a.intVal + result = b + else: + result = buildCall(opAdd, a, b) + elif b.kind in {nkFloatLit..nkFloat64Lit}: + var b = copyNode(b) + b.floatVal = -b.floatVal + result = buildCall(opAdd, a, b) + else: + result = res + +proc zero(): PNode = nkIntLit.newIntNode(0) +proc one(): PNode = nkIntLit.newIntNode(1) +proc minusOne(): PNode = nkIntLit.newIntNode(-1) + +proc lowBound*(x: PNode): PNode = + result = nkIntLit.newIntNode(firstOrd(x.typ)) + result.info = x.info + +proc highBound*(x: PNode): PNode = + result = if x.typ.skipTypes(abstractInst).kind == tyArray: + nkIntLit.newIntNode(lastOrd(x.typ)) + else: + opAdd.buildCall(opLen.buildCall(x), minusOne()) + result.info = x.info + +proc reassociation(n: PNode): 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]) + of someMul: + if result[2].isValue and + result[1].getMagic in someMul and result[1][2].isValue: + result = opAdd.buildCall(result[1][1], result[1][2] |*| result[2]) + else: discard + +proc canon*(n: PNode): 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]) + else: + result = n + case result.getMagic + of someEq, someAdd, someMul, someMin, someMax: + # these are symmetric; put value as last: + if result.sons[1].isValue and not result.sons[2].isValue: + result = swapArgs(result, result.sons[0].sym) + # (4 + foo) + 2 --> (foo + 4) + 2 + of someHigh: + # high == len+(-1) + result = opAdd.buildCall(opLen.buildCall(result[1]), minusOne()) + of mUnaryMinusI, mUnaryMinusI64: + result = buildCall(opAdd, result[1], newIntNode(nkIntLit, -1)) + of someSub: + # x - 4 --> x + (-4) + result = negate(result[1], result[2], result) + of someLen: + result.sons[0] = opLen.newSymNode + else: discard + + result = skipConv(result) + result = reassociation(result) + # most important rule: (x-4) < a.len --> x < a.len+4 + case result.getMagic + of someLe, someLt: + let x = result[1] + let y = result[2] + if x.kind in nkCallKinds and x.len == 3 and x[2].isValue and + isLetLocation(x[1], true): + case x.getMagic + of someSub: + result = buildCall(result[0].sym, x[1], + reassociation(opAdd.buildCall(y, x[2]))) + of someAdd: + # Rule A: + let plus = negate(y, x[2], nil).reassociation + 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 + isLetLocation(y[1], true): + # a.len < x-3 + case y.getMagic + of someSub: + result = buildCall(result[0].sym, y[1], + reassociation(opAdd.buildCall(x, y[2]))) + of someAdd: + let plus = negate(x, y[2], nil).reassociation + # ensure that Rule A will not trigger afterwards with the + # additional 'not isLetLocation' constraint: + if plus != nil and not isLetLocation(x, true): + result = buildCall(result[0].sym, plus, y[1]) + else: discard + else: discard + +proc `+@`*(a: PNode; b: BiggestInt): PNode = + canon(if b != 0: opAdd.buildCall(a, nkIntLit.newIntNode(b)) else: a) proc usefulFact(n: PNode): 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 = buildIsNil(n.sons[1]) + result = 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' @@ -217,7 +358,7 @@ proc addFactNeg*(m: var TModel, n: PNode) = let n = n.neg if n != nil: addFact(m, n) -proc sameTree(a, b: PNode): bool = +proc sameTree*(a, b: PNode): bool = result = false if a == b: result = true @@ -484,7 +625,7 @@ proc factImplies(fact, prop: PNode): TImplication = # == not a or not b == not (a and b) let arg = fact.sons[1] case arg.getMagic - of mIsNil: + of mIsNil, mEqRef: return ~factImplies(arg, prop) of mAnd: # not (a and b) means not a or not b: @@ -519,7 +660,144 @@ proc doesImply*(facts: TModel, prop: PNode): TImplication = if result != impUnknown: return proc impliesNotNil*(facts: TModel, arg: PNode): TImplication = - result = doesImply(facts, buildIsNil(arg).neg) + result = doesImply(facts, opIsNil.buildCall(arg).neg) + +proc simpleSlice*(a, b: PNode): BiggestInt = + # returns 'c' if a..b matches (i+c)..(i+c), -1 otherwise. (i)..(i) is matched + # as if it is (i+0)..(i+0). + if guards.sameTree(a, b): + if a.getMagic in someAdd and a[2].kind in {nkCharLit..nkUInt64Lit}: + result = a[2].intVal + else: + result = 0 + else: + result = -1 + +proc pleViaModel(model: TModel; aa, bb: PNode): TImplication + +proc ple(m: TModel; a, b: PNode): TImplication = + template `<=?`(a,b): expr = ple(m,a,b) == impYes + # 0 <= 3 + if a.isValue and b.isValue: + return if leValue(a, b): impYes else: impNo + + # use type information too: x <= 4 iff high(x) <= 4 + if b.isValue and a.typ != nil and a.typ.isOrdinalType: + if lastOrd(a.typ) <= b.intVal: return impYes + # 3 <= x iff low(x) <= 3 + if a.isValue and b.typ != nil and b.typ.isOrdinalType: + if firstOrd(b.typ) <= a.intVal: return impYes + + # x <= x + if sameTree(a, b): return impYes + + # 0 <= x.len + if b.getMagic in someLen and a.isValue: + if a.intVal <= 0: return impYes + + # x <= y+c if 0 <= c and x <= y + if b.getMagic in someAdd and zero() <=? b[2] and a <=? b[1]: return impYes + + # x+c <= y if c <= 0 and x <= y + if a.getMagic in someAdd and a[2] <=? zero() and a[1] <=? b: return impYes + + # x <= y*c if 1 <= c and x <= y and 0 <= y + if b.getMagic in someMul: + if a <=? b[1] and one() <=? b[2] and zero() <=? b[1]: return impYes + + # x div c <= y if 1 <= c and 0 <= y and x <= y: + if a.getMagic in someDiv: + if one() <=? a[2] and zero() <=? b and a[1] <=? b: return impYes + + # slightly subtle: + # x <= max(y, z) iff x <= y or x <= z + # note that 'x <= max(x, z)' is a special case of the above rule + if b.getMagic in someMax: + if a <=? b[1] or a <=? b[2]: return impYes + + # min(x, y) <= z iff x <= z or y <= z + if a.getMagic in someMin: + if a[1] <=? b or a[2] <=? b: return impYes + + # use the knowledge base: + return pleViaModel(m, a, b) + #return doesImply(m, opLe.buildCall(a, b)) + +type TReplacements = seq[tuple[a,b: PNode]] + +proc replaceSubTree(n, x, by: PNode): PNode = + if sameTree(n, x): + result = by + elif hasSubTree(n, x): + result = shallowCopy(n) + for i in 0 .. safeLen(n)-1: + result.sons[i] = replaceSubTree(n.sons[i], x, by) + else: + result = n + +proc applyReplacements(n: PNode; rep: TReplacements): PNode = + result = n + for x in rep: result = result.replaceSubTree(x.a, x.b) + +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] + if fact != nil and fact.getMagic in someLe: + # x <= y implies a <= b if a <= x and y <= b + let x = fact[1] + let y = fact[2] + # mark as used: + m[i] = nil + if ple(m, a, x) == impYes: + if ple(m, y, b) == impYes: return impYes + #if pleViaModelRec(m, y, b): return impYes + # fact: 16 <= i + # x y + # question: i <= 15? no! + result = impliesLe(fact, a, b) + if result != impUnknown: return result + if sameTree(y, a): + result = ple(m, x, b) + if result != impUnknown: return result + +proc pleViaModel(model: TModel; aa, bb: PNode): TImplication = + # compute replacements: + var replacements: TReplacements = @[] + for fact in model: + if fact != nil and fact.getMagic in someEq: + let a = fact[1] + let b = fact[2] + if a.kind == nkSym: replacements.add((a,b)) + else: replacements.add((b,a)) + var m: TModel + var a = aa + var b = bb + if replacements.len > 0: + m = @[] + # make the other facts consistent: + for fact in model: + if fact != nil and fact.getMagic notin someEq: + # XXX 'canon' should not be necessary here, but it is + m.add applyReplacements(fact, replacements).canon + a = applyReplacements(aa, replacements) + b = applyReplacements(bb, replacements) + else: + # we have to make a copy here, because the model will be modified: + m = model + result = pleViaModelRec(m, a, b) + +proc proveLe*(m: TModel; a, b: PNode): TImplication = + let x = canon(opLe.buildCall(a, b)) + #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)) + result = ~ple(m, y[1], y[2]) + +proc addFactLe*(m: var TModel; a, b: PNode) = + m.add canon(opLe.buildCall(a, b)) proc settype(n: PNode): PType = result = newType(tySet, n.typ.owner) diff --git a/compiler/importer.nim b/compiler/importer.nim index 7a73f2bbf..b4cae017e 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -93,7 +93,7 @@ proc rawImportSymbol(c: PContext, s: PSym) = if hasPattern(s): addPattern(c, s) proc importSymbol(c: PContext, n: PNode, fromMod: PSym) = - let ident = lookups.considerAcc(n) + let ident = lookups.considerQuotedIdent(n) let s = strTableGet(fromMod.tab, ident) if s == nil: localError(n.info, errUndeclaredIdentifier, ident.s) @@ -193,7 +193,7 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode = addDecl(c, m) # add symbol to symbol table of module var exceptSet = initIntSet() for i in countup(1, sonsLen(n) - 1): - let ident = lookups.considerAcc(n.sons[i]) + let ident = lookups.considerQuotedIdent(n.sons[i]) exceptSet.incl(ident.id) importAllSymbolsExcept(c, m, exceptSet) importForwarded(c, m.ast, exceptSet) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 373a11e9a..6687e2e8e 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -136,18 +136,6 @@ proc mapType(typ: PType): TJSTypeKind = of tyProc: result = etyProc of tyCString: result = etyString -proc mangle(name: string): string = - result = "" - for i in countup(0, len(name) - 1): - case name[i] - of 'A'..'Z': - add(result, chr(ord(name[i]) - ord('A') + ord('a'))) - of '_': - discard - of 'a'..'z', '0'..'9': - add(result, name[i]) - else: add(result, 'X' & toHex(ord(name[i]), 2)) - proc mangleName(s: PSym): PRope = result = s.loc.r if result == nil: diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 2bfd8d1eb..0e4dfc2ac 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -347,7 +347,7 @@ proc getNumber(L: var TLexer): TToken = result.base = base2 while true: case L.buf[pos] - of 'A'..'Z', 'a'..'z', '2'..'9', '.': + of '2'..'9', '.': lexMessage(L, errInvalidNumber, result.literal) inc(pos) of '_': @@ -363,7 +363,7 @@ proc getNumber(L: var TLexer): TToken = result.base = base8 while true: case L.buf[pos] - of 'A'..'Z', 'a'..'z', '8'..'9', '.': + of '8'..'9', '.': lexMessage(L, errInvalidNumber, result.literal) inc(pos) of '_': @@ -377,25 +377,22 @@ proc getNumber(L: var TLexer): TToken = else: break of 'O': lexMessage(L, errInvalidNumber, result.literal) - of 'x', 'X': + of 'x', 'X': result.base = base16 - while true: + while true: case L.buf[pos] - of 'G'..'Z', 'g'..'z': - lexMessage(L, errInvalidNumber, result.literal) - inc(pos) - of '_': - if L.buf[pos+1] notin {'0'..'9', 'a'..'f', 'A'..'F'}: + of '_': + if L.buf[pos+1] notin {'0'..'9', 'a'..'f', 'A'..'F'}: lexMessage(L, errInvalidToken, "_") break inc(pos) - of '0'..'9': + of '0'..'9': xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0')) inc(pos) - of 'a'..'f': + of 'a'..'f': xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('a') + 10) inc(pos) - of 'A'..'F': + of 'A'..'F': xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10) inc(pos) else: break @@ -424,8 +421,14 @@ proc getNumber(L: var TLexer): TToken = if (result.iNumber < low(int32)) or (result.iNumber > high(int32)): if result.tokType == tkIntLit: result.tokType = tkInt64Lit - elif result.tokType in {tkInt8Lit, tkInt16Lit}: - lexMessage(L, errInvalidNumber, result.literal) + elif result.tokType in {tkInt8Lit, tkInt16Lit, tkInt32Lit}: + lexMessage(L, errNumberOutOfRange, result.literal) + elif result.tokType == tkInt8Lit and + (result.iNumber < int8.low or result.iNumber > int8.high): + lexMessage(L, errNumberOutOfRange, result.literal) + elif result.tokType == tkInt16Lit and + (result.iNumber < int16.low or result.iNumber > int16.high): + lexMessage(L, errNumberOutOfRange, result.literal) except EInvalidValue: lexMessage(L, errInvalidNumber, result.literal) except EOverflow, EOutOfRange: @@ -537,6 +540,10 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) = tok.tokType = tkTripleStrLit # long string literal: inc(pos, 2) # skip "" # skip leading newline: + if buf[pos] in {' ', '\t'}: + var newpos = pos+1 + while buf[newpos] in {' ', '\t'}: inc newpos + if buf[newpos] in {CR, LF}: pos = newpos pos = handleCRLF(L, pos) buf = L.buf while true: diff --git a/compiler/llstream.nim b/compiler/llstream.nim index 510880ffd..86bfeaabd 100644 --- a/compiler/llstream.nim +++ b/compiler/llstream.nim @@ -82,6 +82,9 @@ when not defined(readLineFromStdin): proc readLineFromStdin(prompt: string, line: var string): bool = stdout.write(prompt) result = readLine(stdin, line) + if not result: + stdout.write("\n") + quit(0) proc endsWith*(x: string, s: set[char]): bool = var i = x.len-1 diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 60125177c..aee64f52f 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -15,14 +15,15 @@ import proc ensureNoMissingOrUnusedSymbols(scope: PScope) -proc considerAcc*(n: PNode): PIdent = +proc considerQuotedIdent*(n: PNode): PIdent = + ## Retrieve a PIdent from a PNode, taking into account accent nodes. case n.kind of nkIdent: result = n.ident of nkSym: result = n.sym.name of nkAccQuoted: case n.len of 0: globalError(n.info, errIdentifierExpected, renderTree(n)) - of 1: result = considerAcc(n.sons[0]) + of 1: result = considerQuotedIdent(n.sons[0]) else: var id = "" for i in 0.. <n.len: @@ -82,10 +83,10 @@ proc searchInScopes*(c: PContext, s: PIdent, filter: TSymKinds): PSym = proc errorSym*(c: PContext, n: PNode): PSym = ## creates an error symbol to avoid cascading errors (for IDE support) var m = n - # ensure that 'considerAcc' can't fail: + # ensure that 'considerQuotedIdent' can't fail: if m.kind == nkDotExpr: m = m.sons[1] let ident = if m.kind in {nkIdent, nkSym, nkAccQuoted}: - considerAcc(m) + considerQuotedIdent(m) else: getIdent("err:" & renderTree(m)) result = newSym(skError, ident, getCurrOwner(), n.info) @@ -189,7 +190,7 @@ proc lookUp*(c: PContext, n: PNode): PSym = of nkSym: result = n.sym of nkAccQuoted: - var ident = considerAcc(n) + var ident = considerQuotedIdent(n) result = searchInScopes(c, ident) if result == nil: localError(n.info, errUndeclaredIdentifier, ident.s) @@ -208,7 +209,7 @@ type proc qualifiedLookUp*(c: PContext, n: PNode, flags = {checkUndeclared}): PSym = case n.kind of nkIdent, nkAccQuoted: - var ident = considerAcc(n) + var ident = considerQuotedIdent(n) result = searchInScopes(c, ident) if result == nil and checkUndeclared in flags: localError(n.info, errUndeclaredIdentifier, ident.s) @@ -228,7 +229,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags = {checkUndeclared}): PSym = if n.sons[1].kind == nkIdent: ident = n.sons[1].ident elif n.sons[1].kind == nkAccQuoted: - ident = considerAcc(n.sons[1]) + ident = considerQuotedIdent(n.sons[1]) if ident != nil: if m == c.module: result = strTableGet(c.topLevelScope.symbols, ident) @@ -251,7 +252,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags = {checkUndeclared}): PSym = proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = case n.kind of nkIdent, nkAccQuoted: - var ident = considerAcc(n) + var ident = considerQuotedIdent(n) o.scope = c.currentScope o.mode = oimNoQualifier while true: @@ -272,7 +273,7 @@ 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 = considerAcc(n.sons[1]) + ident = considerQuotedIdent(n.sons[1]) if ident != nil: if o.m == c.module: # a module may access its private members: @@ -354,5 +355,5 @@ when false: if sfImmediate in a.flags: return a a = nextOverloadIter(o, c, n) if result == nil and checkUndeclared in flags: - localError(n.info, errUndeclaredIdentifier, n.considerAcc.s) + localError(n.info, errUndeclaredIdentifier, n.considerQuotedIdent.s) result = errorSym(c, n) diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 1b9e5fe0f..e2afa4362 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -13,6 +13,8 @@ const genPrefix* = ":tmp" # prefix for generated names import ast, astalgo, types, idents, magicsys, msgs, options +from guards import createMagic +from trees import getMagic proc newTupleAccess*(tup: PNode, i: int): PNode = result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes( @@ -68,6 +70,7 @@ proc addField*(obj: PType; s: PSym) = var field = newSym(skField, getIdent(s.name.s & $s.id), s.owner, s.info) let t = skipIntLit(s.typ) field.typ = t + assert t.kind != tyStmt field.position = sonsLen(obj.n) addSon(obj.n, newSymNode(field)) @@ -79,19 +82,30 @@ proc newDotExpr(obj, b: PSym): PNode = addSon(result, newSymNode(field)) result.typ = field.typ -proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode = +proc indirectAccess*(a: PNode, b: string, info: TLineInfo): PNode = # returns a[].b as a node var deref = newNodeI(nkHiddenDeref, info) - deref.typ = a.typ.sons[0] - assert deref.typ.kind == tyObject - let field = getSymFromList(deref.typ.n, getIdent(b.name.s & $b.id)) - assert field != nil, b.name.s + deref.typ = a.typ.skipTypes(abstractInst).sons[0] + var t = deref.typ.skipTypes(abstractInst) + var field: PSym + while true: + assert t.kind == tyObject + field = getSymFromList(t.n, getIdent(b)) + if field != nil: break + t = t.sons[0] + if t == nil: break + t = t.skipTypes(abstractInst) + assert field != nil, b addSon(deref, a) result = newNodeI(nkDotExpr, info) addSon(result, deref) addSon(result, newSymNode(field)) result.typ = field.typ +proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode = + # returns a[].b as a node + result = indirectAccess(a, b.name.s & $b.id, info) + proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode = result = indirectAccess(newSymNode(a), b, info) @@ -101,6 +115,11 @@ proc genAddrOf*(n: PNode): PNode = result.typ = newType(tyPtr, n.typ.owner) result.typ.rawAddSon(n.typ) +proc genDeref*(n: PNode): PNode = + result = newNodeIT(nkHiddenDeref, n.info, + n.typ.skipTypes(abstractInst).sons[0]) + result.add n + proc callCodegenProc*(name: string, arg1: PNode; arg2, arg3: PNode = nil): PNode = result = newNodeI(nkCall, arg1.info) @@ -112,13 +131,120 @@ proc callCodegenProc*(name: string, arg1: PNode; result.add arg1 if arg2 != nil: result.add arg2 if arg3 != nil: result.add arg3 + result.typ = sym.typ.sons[0] + +proc callProc(a: PNode): PNode = + result = newNodeI(nkCall, a.info) + result.add a + result.typ = a.typ.sons[0] + +# we have 4 cases to consider: +# - a void proc --> nothing to do +# - a proc returning GC'ed memory --> requires a flowVar +# - a proc returning non GC'ed memory --> pass as hidden 'var' parameter +# - not in a parallel environment --> requires a flowVar for memory safety +type + TSpawnResult = enum + srVoid, srFlowVar, srByVar + TFlowVarKind = enum + fvInvalid # invalid type T for 'FlowVar[T]' + fvGC # FlowVar of a GC'ed type + fvBlob # FlowVar of a blob type + +proc spawnResult(t: PType; inParallel: bool): TSpawnResult = + if t.isEmptyType: srVoid + elif inParallel and not containsGarbageCollectedRef(t): srByVar + else: srFlowVar + +proc flowVarKind(t: PType): TFlowVarKind = + if t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: fvGC + elif containsGarbageCollectedRef(t): fvInvalid + else: fvBlob + +proc addLocalVar(varSection: PNode; owner: PSym; typ: PType; v: PNode): PSym = + result = newSym(skTemp, getIdent(genPrefix), owner, varSection.info) + result.typ = typ + incl(result.flags, sfFromGeneric) + + var vpart = newNodeI(nkIdentDefs, varSection.info, 3) + vpart.sons[0] = newSymNode(result) + vpart.sons[1] = ast.emptyNode + vpart.sons[2] = v + varSection.add vpart + +discard """ +We generate roughly this: + +proc f_wrapper(thread, args) = + barrierEnter(args.barrier) # for parallel statement + var a = args.a # thread transfer; deepCopy or shallowCopy or no copy + # depending on whether we're in a 'parallel' statement + var b = args.b + var fv = args.fv + + fv.owner = thread # optional + nimArgsPassingDone() # signal parent that the work is done + # + args.fv.blob = f(a, b, ...) + nimFlowVarSignal(args.fv) + + # - or - + f(a, b, ...) + barrierLeave(args.barrier) # for parallel statement + +stmtList: + var scratchObj + scratchObj.a = a + scratchObj.b = b + + nimSpawn(f_wrapper, addr scratchObj) + scratchObj.fv # optional + +""" proc createWrapperProc(f: PNode; threadParam, argsParam: PSym; - varSection, call: PNode): PSym = + varSection, call, barrier, fv: PNode; + spawnKind: TSpawnResult): PSym = var body = newNodeI(nkStmtList, f.info) + var threadLocalBarrier: PSym + if barrier != nil: + var varSection = newNodeI(nkVarSection, barrier.info) + threadLocalBarrier = addLocalVar(varSection, argsParam.owner, + barrier.typ, barrier) + body.add varSection + body.add callCodeGenProc("barrierEnter", threadLocalBarrier.newSymNode) + var threadLocalProm: PSym + if spawnKind == srByVar: + threadLocalProm = addLocalVar(varSection, argsParam.owner, fv.typ, fv) + elif fv != nil: + internalAssert fv.typ.kind == tyGenericInst + threadLocalProm = addLocalVar(varSection, argsParam.owner, fv.typ, fv) + body.add varSection - body.add callCodeGenProc("nimArgsPassingDone", newSymNode(threadParam)) - body.add call + if fv != nil and spawnKind != srByVar: + # generate: + # fv.owner = threadParam + body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode, + "owner", fv.info), threadParam.newSymNode) + + body.add callCodeGenProc("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: " & + typeToString(fv.typ.sons[1])) + body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode, + if fk == fvGC: "data" else: "blob", fv.info), call) + 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) + else: + body.add call + if barrier != nil: + body.add callCodeGenProc("barrierLeave", threadLocalBarrier.newSymNode) var params = newNodeI(nkFormalParams, f.info) params.add emptyNode @@ -146,10 +272,152 @@ proc createCastExpr(argsParam: PSym; objType: PType): PNode = result.typ = newType(tyPtr, objType.owner) result.typ.rawAddSon(objType) -proc wrapProcForSpawn*(owner: PSym; n: PNode): PNode = - result = newNodeI(nkStmtList, n.info) - if n.kind notin nkCallKinds or not n.typ.isEmptyType: - localError(n.info, "'spawn' takes a call expression of type void") +proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym, + castExpr, call, varSection, result: PNode) = + let formals = n[0].typ.n + let tmpName = getIdent(genPrefix) + for i in 1 .. <n.len: + # 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") + 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) + field.typ = argType + objType.addField(field) + result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i]) + + let temp = addLocalVar(varSection, objType.owner, argType, + indirectAccess(castExpr, field, n.info)) + call.add(newSymNode(temp)) + +proc getRoot*(n: PNode): PSym = + ## ``getRoot`` takes a *path* ``n``. A path is an lvalue expression + ## like ``obj.x[i].y``. The *root* of a path is the symbol that can be + ## determined as the owner; ``obj`` in the example. + case n.kind + of nkSym: + if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar}: + result = n.sym + of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr, + nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: + result = getRoot(n.sons[0]) + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + result = getRoot(n.sons[1]) + of nkCallKinds: + if getMagic(n) == mSlice: result = getRoot(n.sons[1]) + else: discard + +proc newIntLit(value: BiggestInt): PNode = + result = nkIntLit.newIntNode(value) + result.typ = getSysType(tyInt) + +proc genHigh(n: PNode): PNode = + if skipTypes(n.typ, abstractVar).kind in {tyArrayConstr, tyArray}: + result = newIntLit(lastOrd(skipTypes(n.typ, abstractVar))) + else: + result = newNodeI(nkCall, n.info, 2) + result.typ = getSysType(tyInt) + result.sons[0] = newSymNode(createMagic("high", mHigh)) + result.sons[1] = n + +proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym; + castExpr, call, varSection, result: PNode) = + let formals = n[0].typ.n + let tmpName = getIdent(genPrefix) + # we need to copy the foreign scratch object fields into local variables + # for correctness: These are called 'threadLocal' here. + for i in 1 .. <n.len: + let n = n[i] + let argType = skipTypes(if i < formals.len: formals[i].typ else: n.typ, + abstractInst) + if containsTyRef(argType): + 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) + + 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)) + var fieldB = newSym(skField, tmpName, objType.owner, n.info) + fieldB.typ = getSysType(tyInt) + objType.addField(fieldB) + + if getMagic(n) == mSlice: + let a = genAddrOf(n[1]) + field.typ = a.typ + objType.addField(field) + result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a) + + var fieldA = newSym(skField, tmpName, objType.owner, n.info) + fieldA.typ = getSysType(tyInt) + objType.addField(fieldA) + result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2]) + result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3]) + + let threadLocal = addLocalVar(varSection, objType.owner, fieldA.typ, + indirectAccess(castExpr, fieldA, n.info)) + slice.sons[2] = threadLocal.newSymNode + else: + let a = genAddrOf(n) + field.typ = a.typ + objType.addField(field) + result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a) + result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(n)) + + slice.sons[2] = newIntLit(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, objType.owner, fieldB.typ, + indirectAccess(castExpr, fieldB, n.info)) + slice.sons[3] = threadLocal.newSymNode + call.add slice + elif (let size = computeSize(argType); size < 0 or size > 16) and + n.getRoot != nil: + # it is more efficient to pass a pointer instead: + let a = genAddrOf(n) + field.typ = a.typ + objType.addField(field) + result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a) + let threadLocal = addLocalVar(varSection, objType.owner, field.typ, + indirectAccess(castExpr, field, n.info)) + call.add(genDeref(threadLocal.newSymNode)) + else: + # boring case + field.typ = argType + objType.addField(field) + result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n) + let threadLocal = addLocalVar(varSection, objType.owner, field.typ, + indirectAccess(castExpr, field, n.info)) + call.add(threadLocal.newSymNode) + +proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType; + barrier, dest: PNode = nil): PNode = + # if 'barrier' != nil, then it is in a 'parallel' section and we + # generate quite different code + let n = spawnExpr[1] + let spawnKind = spawnResult(retType, barrier!=nil) + case spawnKind + of srVoid: + internalAssert dest == nil + result = newNodeI(nkStmtList, n.info) + of srFlowVar: + internalAssert dest == nil + result = newNodeIT(nkStmtListExpr, n.info, retType) + of srByVar: + if dest == nil: localError(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") return if optThreadAnalysis in gGlobalOptions: if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}: @@ -162,6 +430,7 @@ proc wrapProcForSpawn*(owner: PSym; n: PNode): PNode = threadParam.typ = ptrType argsParam.typ = ptrType argsParam.position = 1 + var objType = createObj(owner, n.info) incl(objType.flags, tfFinal) let castExpr = createCastExpr(argsParam, objType) @@ -174,7 +443,7 @@ proc wrapProcForSpawn*(owner: PSym; n: PNode): PNode = varSectionB.addVar(scratchObj.newSymNode) result.add varSectionB - var call = newNodeI(nkCall, n.info) + var call = newNodeIT(nkCall, n.info, n.typ) var fn = n.sons[0] # templates and macros are in fact valid here due to the nature of # the transformation: @@ -194,35 +463,44 @@ proc wrapProcForSpawn*(owner: PSym; n: PNode): PNode = call.add(fn) var varSection = newNodeI(nkVarSection, n.info) - let formals = n[0].typ.n - let tmpName = getIdent(genPrefix) - for i in 1 .. <n.len: - # 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") - elif containsTyRef(argType): - localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure") + if barrier.isNil: + setupArgsForConcurrency(n, objType, scratchObj, castExpr, call, varSection, result) + else: + setupArgsForParallelism(n, objType, scratchObj, castExpr, call, varSection, result) - let fieldname = if i < formals.len: formals[i].sym.name else: tmpName - var field = newSym(skField, fieldname, owner, n.info) - field.typ = argType + 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) + field.typ = typ objType.addField(field) - result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i]) - - var temp = newSym(skTemp, tmpName, owner, n.info) - temp.typ = argType - incl(temp.flags, sfFromGeneric) + result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier) + barrierAsExpr = indirectAccess(castExpr, field, n.info) - var vpart = newNodeI(nkIdentDefs, n.info, 3) - vpart.sons[0] = newSymNode(temp) - vpart.sons[1] = ast.emptyNode - vpart.sons[2] = indirectAccess(castExpr, field, n.info) - varSection.add vpart + var fvField, fvAsExpr: PNode = nil + if spawnKind == srFlowVar: + var field = newSym(skField, getIdent"fv", owner, n.info) + field.typ = retType + objType.addField(field) + fvField = newDotExpr(scratchObj, field) + fvAsExpr = indirectAccess(castExpr, field, n.info) + # create flowVar: + result.add newFastAsgnStmt(fvField, callProc(spawnExpr[2])) + if barrier == nil: + result.add callCodeGenProc("nimFlowVarCreateCondVar", fvField) - call.add(newSymNode(temp)) + elif spawnKind == srByVar: + var field = newSym(skField, getIdent"fv", owner, n.info) + 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, varSection, call) + let wrapper = createWrapperProc(fn, threadParam, argsParam, varSection, call, + barrierAsExpr, fvAsExpr, spawnKind) result.add callCodeGenProc("nimSpawn", wrapper.newSymNode, genAddrOf(scratchObj.newSymNode)) + + if spawnKind == srFlowVar: result.add fvField diff --git a/compiler/main.nim b/compiler/main.nim index f6d11d960..b4af49248 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -69,6 +69,7 @@ proc commandCompileToC = # echo "BEFORE CHECK DEP" # discard checkDepMem(gProjectMainIdx) # echo "CHECK DEP COMPLETE" + discard compileProject() cgenWriteModules() @@ -283,11 +284,11 @@ proc resetMemory = echo GC_getStatistics() const - SimiluateCaasMemReset = false + SimulateCaasMemReset = false PrintRopeCacheStats = false proc mainCommand* = - when SimiluateCaasMemReset: + when SimulateCaasMemReset: gGlobalOptions.incl(optCaasEnabled) # In "nimrod serve" scenario, each command must reset the registered passes @@ -309,7 +310,7 @@ proc mainCommand* = of "cpp", "compiletocpp": extccomp.cExt = ".cpp" gCmd = cmdCompileToCpp - if cCompiler == ccGcc: setCC("gpp") + if cCompiler == ccGcc: setCC("gcc") wantMainModule() defineSymbol("cpp") commandCompileToC() @@ -453,6 +454,6 @@ proc mainCommand* = echo " efficiency: ", formatFloat(1-(gCacheMisses.float/gCacheTries.float), ffDecimal, 3) - when SimiluateCaasMemReset: + when SimulateCaasMemReset: resetMemory() diff --git a/compiler/modules.nim b/compiler/modules.nim index fb1940741..b102224cd 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -115,7 +115,7 @@ proc newModule(fileIdx: int32): PSym = new(result) result.id = - 1 # for better error checking result.kind = skModule - let filename = fileIdx.toFilename + let filename = fileIdx.toFullPath result.name = getIdent(splitFile(filename).name) if not isNimrodIdentifier(result.name.s): rawMessage(errInvalidModuleName, result.name.s) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 8374c92a7..730cb9605 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -116,7 +116,7 @@ type warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored, warnNilStatement, warnAnalysisLoophole, - warnDifferentHeaps, warnWriteToForeignHeap, warnImplicitClosure, + warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode, warnEachIdentIsTuple, warnShadowIdent, warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, warnUninit, warnGcMem, warnUser, @@ -380,7 +380,7 @@ const warnAnalysisLoophole: "thread analysis incomplete due to unknown call '$1' [AnalysisLoophole]", warnDifferentHeaps: "possible inconsistency of thread local heaps [DifferentHeaps]", warnWriteToForeignHeap: "write to foreign heap [WriteToForeignHeap]", - warnImplicitClosure: "implicit closure convention: '$1' [ImplicitClosure]", + warnUnsafeCode: "unsafe code: '$1' [UnsafeCode]", warnEachIdentIsTuple: "each identifier is a tuple [EachIdentIsTuple]", warnShadowIdent: "shadowed identifier: '$1' [ShadowIdent]", warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future. [ProveInit]", @@ -416,7 +416,7 @@ const "RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported", "CommentXIgnored", "NilStmt", "AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap", - "ImplicitClosure", "EachIdentIsTuple", "ShadowIdent", + "UnsafeCode", "EachIdentIsTuple", "ShadowIdent", "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", "GcMem", "User"] diff --git a/compiler/nimrod.ini b/compiler/nimrod.ini index 0dc44a7c9..44e16cec8 100644 --- a/compiler/nimrod.ini +++ b/compiler/nimrod.ini @@ -3,7 +3,7 @@ Name: "Nimrod" Version: "$version" Platforms: """ windows: i386;amd64 - linux: i386;amd64;powerpc64;arm;sparc;mips + linux: i386;amd64;powerpc64;arm;sparc;mips;powerpc macosx: i386;amd64;powerpc64 solaris: i386;amd64;sparc freebsd: i386;amd64 @@ -79,6 +79,7 @@ Files: "lib/system/*.nim" Files: "lib/core/*.nim" Files: "lib/pure/*.nim" Files: "lib/pure/collections/*.nim" +Files: "lib/pure/concurrency/*.nim" Files: "lib/impure/*.nim" Files: "lib/wrappers/*.nim" diff --git a/compiler/nversion.nim b/compiler/nversion.nim index b996d0b9b..3c868ed2a 100644 --- a/compiler/nversion.nim +++ b/compiler/nversion.nim @@ -12,10 +12,9 @@ const MaxSetElements* = 1 shl 16 # (2^16) to support unicode character sets? - defaultAsmMarkerSymbol* = '!' VersionMajor* = 0 VersionMinor* = 9 - VersionPatch* = 4 + VersionPatch* = 5 VersionAsString* = $VersionMajor & "." & $VersionMinor & "." & $VersionPatch RodFileVersion* = "1215" # modify this if the rod-format changes! diff --git a/compiler/options.nim b/compiler/options.nim index 36d343d1b..58a340d21 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -16,7 +16,7 @@ const hasFFI* = defined(useFFI) newScopeForIf* = true useCaas* = not defined(noCaas) - noTimeMachine = defined(avoidTimeMachine) and defined(macosx) + noTimeMachine* = defined(avoidTimeMachine) and defined(macosx) type # please make sure we have under 32 options # (improves code efficiency a lot!) diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index e94068776..bbdba8c22 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -10,7 +10,7 @@ ## This module implements the pattern matching features for term rewriting ## macro support. -import strutils, ast, astalgo, types, msgs, idents, renderer, wordrecg +import strutils, ast, astalgo, types, msgs, idents, renderer, wordrecg, trees # 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 @@ -215,6 +215,9 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = result = arLValue of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: result = isAssignable(owner, n.sons[0]) + of nkCallKinds: + # builtin slice keeps lvalue-ness: + if getMagic(n) == mSlice: result = isAssignable(owner, n.sons[1]) else: discard diff --git a/compiler/parser.nim b/compiler/parser.nim index 5c7b86240..6ff0c2dfc 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -28,25 +28,20 @@ import llstream, lexer, idents, strutils, ast, astalgo, msgs type - TParser*{.final.} = object # a TParser object represents a module that + TParser*{.final.} = object # A TParser object represents a module that # is being parsed - currInd: int # current indentation - firstTok, strongSpaces: bool - lex*: TLexer # the lexer that is used for parsing - tok*: TToken # the current token - inPragma: int + currInd: int # current indentation level + firstTok, strongSpaces: bool # Has the first token been read? + # Is strongSpaces on? + lex*: TLexer # The lexer that is used for parsing + tok*: TToken # The current token + inPragma: int # Pragma level inSemiStmtList: int proc parseAll*(p: var TParser): PNode proc closeParser*(p: var TParser) proc parseTopLevelStmt*(p: var TParser): PNode - # implements an iterator. Returns the next top-level statement or - # emtyNode if end of stream. - proc parseString*(s: string, filename: string = "", line: int = 0): PNode - # filename and line could be set optionally, when the string originates - # from a certain source file. This way, the compiler could generate - # correct error messages referring to the original source. # helpers for the other parsers proc isOperator*(tok: TToken): bool @@ -68,15 +63,19 @@ proc optInd*(p: var TParser, n: PNode) proc indAndComment*(p: var TParser, n: PNode) proc setBaseFlags*(n: PNode, base: TNumericalBase) proc parseSymbol*(p: var TParser, allowNil = false): PNode -proc parseTry(p: var TParser): PNode +proc parseTry(p: var TParser; isExpr: bool): PNode proc parseCase(p: var TParser): PNode # implementation -proc getTok(p: var 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, strongSpaces=false) = + ## Open a parser, using the given arguments to set up its internal state. + ## initToken(p.tok) openLexer(p.lex, fileIdx, inputStream) getTok(p) # read the first token @@ -87,13 +86,16 @@ proc openParser*(p: var TParser, filename: string, inputStream: PLLStream, strongSpaces=false) = openParser(p, filename.fileInfoIdx, inputstream, strongSpaces) -proc closeParser(p: var TParser) = +proc closeParser(p: var TParser) = + ## Close a parser, freeing up its resources. closeLexer(p.lex) -proc parMessage(p: TParser, msg: TMsgKind, arg: string = "") = +proc parMessage(p: TParser, msg: TMsgKind, arg = "") = + ## Produce and emit the parser message `arg` to output. lexMessage(p.lex, msg, arg) -proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) = +proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) = + ## Produce and emit a parser message to output about the token `tok` lexMessage(p.lex, msg, prettyTok(tok)) template withInd(p: expr, body: stmt) {.immediate.} = @@ -143,10 +145,15 @@ proc expectIdent(p: TParser) = lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok)) proc eat(p: var TParser, tokType: TTokType) = - if p.tok.tokType == tokType: getTok(p) - else: lexMessage(p.lex, errTokenExpected, TokTypeToStr[tokType]) + ## 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: + lexMessage(p.lex, errTokenExpected, 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) = @@ -192,9 +199,11 @@ proc isSigilLike(tok: TToken): bool {.inline.} = result = tok.tokType == tkOpr and relevantOprChar(tok.ident) == '@' proc isLeftAssociative(tok: TToken): bool {.inline.} = + ## Determines whether the token is left assocative. result = tok.tokType != tkOpr or relevantOprChar(tok.ident) != '^' proc getPrecedence(tok: TToken, strongSpaces: bool): int = + ## Calculates the precedence of the given token. template considerStrongSpaces(x): expr = x + (if strongSpaces: 100 - tok.strongSpaceA.int*10 else: 0) @@ -224,22 +233,26 @@ proc getPrecedence(tok: TToken, strongSpaces: bool): int = else: result = -10 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 p.strongSpaces and p.tok.tokType in {tkOpr, tkDotDot} and p.tok.strongSpaceB == 0 and p.tok.strongSpaceA > 0 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.strongSpaceB > 0 and p.tok.strongSpaceA != p.tok.strongSpaceB: - parMessage(p, errGenerated, "number of spaces around '$#' not consistent"% - prettyTok(p.tok)) + 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, errGenerated, "Number of spaces must be 0,1,2,4 or 8") #| module = stmt ^* (';' / IND{=}) #| @@ -274,7 +287,7 @@ proc colcom(p: var TParser, n: PNode) = skipComment(p, n) proc parseSymbol(p: var TParser, allowNil = false): PNode = - #| symbol = '`' (KEYW|IDENT|operator|'(' ')'|'[' ']'|'{' '}'|'='|literal)+ '`' + #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' #| | IDENT case p.tok.tokType of tkSymbol: @@ -285,31 +298,22 @@ proc parseSymbol(p: var TParser, allowNil = false): PNode = getTok(p) while true: case p.tok.tokType - of tkBracketLe: - add(result, newIdentNodeP(getIdent"[]", p)) - getTok(p) - eat(p, tkBracketRi) - of tkEquals: - add(result, newIdentNodeP(getIdent"=", p)) - getTok(p) - of tkParLe: - add(result, newIdentNodeP(getIdent"()", p)) - getTok(p) - eat(p, tkParRi) - of tkCurlyLe: - add(result, newIdentNodeP(getIdent"{}", p)) - getTok(p) - eat(p, tkCurlyRi) - of tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr, tkDot, tkDotDot: - add(result, newIdentNodeP(p.tok.ident, p)) - getTok(p) - of tkIntLit..tkCharLit: - add(result, newIdentNodeP(getIdent(tokToStr(p.tok)), p)) - getTok(p) - else: + 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(getIdent(accm), p)) + of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCharLit: + result.add(newIdentNodeP(getIdent(tokToStr(p.tok)), p)) + getTok(p) + else: + parMessage(p, errIdentifierExpected, p.tok) eat(p, tkAccent) else: if allowNil and p.tok.tokType == tkNil: @@ -841,7 +845,7 @@ proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode = addSon(result, parseTypeDesc(p)) else: addSon(result, ast.emptyNode) - if (p.tok.tokType != tkEquals) and not (withBothOptional in flags): + if p.tok.tokType != tkEquals and withBothOptional notin flags: parMessage(p, errColonOrEqualsExpected, p.tok) if p.tok.tokType == tkEquals: getTok(p) @@ -982,6 +986,7 @@ proc parseSymbolList(p: var TParser, result: PNode, allowNil = false) = proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, mode: TPrimaryMode): PNode = + #| distinct = 'distinct' optInd typeDesc result = newNodeP(kind, p) getTok(p) optInd(p, result) @@ -999,13 +1004,13 @@ proc parseExpr(p: var TParser): PNode = #| expr = (ifExpr #| | whenExpr #| | caseExpr - #| | tryStmt) + #| | 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) + of tkTry: result = parseTry(p, isExpr=true) else: result = simpleExpr(p) proc parseEnum(p: var TParser): PNode @@ -1108,6 +1113,7 @@ proc parseTypeDefAux(p: var TParser): PNode = 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: @@ -1357,22 +1363,25 @@ proc parseCase(p: var TParser): PNode = if wasIndented: p.currInd = oldInd -proc parseTry(p: var TParser): PNode = +proc parseTry(p: var TParser; isExpr: bool): 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) eat(p, tkColon) skipComment(p, result) addSon(result, parseStmt(p)) var b: PNode = nil - while sameOrNoInd(p): + while sameOrNoInd(p) or isExpr: case p.tok.tokType - of tkExcept: + of tkExcept: b = newNodeP(nkExceptBranch, p) exprList(p, tkColon, b) - of tkFinally: + of tkFinally: b = newNodeP(nkFinally, p) getTokNoInd(p) eat(p, tkColon) @@ -1871,7 +1880,7 @@ proc complexOrSimpleStmt(p: var TParser): PNode = of tkIf: result = parseIfOrWhen(p, nkIfStmt) of tkWhile: result = parseWhile(p) of tkCase: result = parseCase(p) - of tkTry: result = parseTry(p) + of tkTry: result = parseTry(p, isExpr=false) of tkFinally: result = parseExceptBlock(p, nkFinally) of tkExcept: result = parseExceptBlock(p, nkExceptBranch) of tkFor: result = parseFor(p) @@ -1952,7 +1961,8 @@ proc parseStmt(p: var TParser): PNode = if p.tok.tokType != tkSemiColon: break getTok(p) -proc parseAll(p: var TParser): PNode = +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) @@ -1966,6 +1976,8 @@ proc parseAll(p: var TParser): PNode = parMessage(p, errInvalidIndentation) 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: if p.tok.indent != 0: @@ -1984,6 +1996,10 @@ proc parseTopLevelStmt(p: var TParser): PNode = break proc parseString(s: string, filename: string = "", line: int = 0): PNode = + ## Parses a string into an AST, returning the top node. + ## `filename` and `line`, although optional, provide info so that the + ## compiler can generate correct error messages referring to the original + ## source. var stream = llStreamOpen(s) stream.lineOffset = line diff --git a/compiler/patterns.nim b/compiler/patterns.nim index d262790ab..5e21289b5 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -287,7 +287,7 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode = # constraint not fullfilled: if not ok: return nil - markUsed(n, s) + markUsed(n.info, s) if ctx.subMatch: assert m.len == 3 m.sons[1] = result diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index db9fe7cbe..a17773aa4 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -517,7 +517,7 @@ proc pragmaUses(c: PContext, n: PNode) = proc processExc(c: PContext, x: PNode): PNode = if x.kind in {nkAccQuoted, nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice}: - if considerAcc(x).s == "*": + if considerQuotedIdent(x).s == "*": return newSymNode(ast.anyGlobal) result = c.semExpr(c, x) if result.kind != nkSym or sfGlobal notin result.sym.flags: @@ -644,12 +644,13 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, incl(sym.flags, sfNoReturn) of wDynlib: processDynLib(c, it, sym) - of wCompilerproc: + of wCompilerproc: noVal(it) # compilerproc may not get a string! - makeExternExport(sym, "$1", it.info) - incl(sym.flags, sfCompilerProc) - incl(sym.flags, sfUsed) # suppress all those stupid warnings - registerCompilerProc(sym) + if sfFromGeneric notin sym.flags: + makeExternExport(sym, "$1", it.info) + incl(sym.flags, sfCompilerProc) + incl(sym.flags, sfUsed) # suppress all those stupid warnings + registerCompilerProc(sym) of wProcVar: noVal(it) incl(sym.flags, sfProcvar) diff --git a/compiler/pretty.nim b/compiler/pretty.nim index 3a5bfe197..17311f9e6 100644 --- a/compiler/pretty.nim +++ b/compiler/pretty.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2013 Andreas Rumpf +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -149,8 +149,8 @@ proc checkDef(c: PGen; n: PNode) = if n.kind != nkSym: return checkDef(n, n.sym) -proc checkUse*(n: PNode, s: PSym) = - if n.info.fileIndex < 0: return +proc checkUse*(info: TLineInfo; s: PSym) = + if info.fileIndex < 0: return # we simply convert it to what it looks like in the definition # for consistency @@ -159,10 +159,10 @@ proc checkUse*(n: PNode, s: PSym) = if s.kind in {skType, skGenericParam} and sfAnon in s.flags: return let newName = s.name.s - loadFile(n.info) + loadFile(info) - let line = gSourceFiles[n.info.fileIndex].lines[n.info.line-1] - var first = min(n.info.col.int, line.len) + let line = gSourceFiles[info.fileIndex].lines[info.line-1] + var first = min(info.col.int, line.len) if first < 0: return #inc first, skipIgnoreCase(line, "proc ", first) while first > 0 and line[first-1] in Letters: dec first @@ -179,8 +179,8 @@ proc checkUse*(n: PNode, s: PSym) = if x.match(peg"\s* {\ident} \s* '=' \s* y$1 ('#' .*)?"): x = "" - system.shallowCopy(gSourceFiles[n.info.fileIndex].lines[n.info.line-1], x) - gSourceFiles[n.info.fileIndex].dirty = true + system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x) + gSourceFiles[info.fileIndex].dirty = true when false: var cannotRename = initIntSet() @@ -220,53 +220,9 @@ when false: result.add s[i] inc i - proc checkUse(c: PGen; n: PNode) = - if n.info.fileIndex < 0: return - let s = n.sym - # operators stay as they are: - if s.kind in {skResult, skTemp} or s.name.s[0] notin Letters: return - if s.kind in {skType, skGenericParam} and sfAnon in s.flags: return - - if s.id in cannotRename: return - - let newName = if rules.hasKey(s.name.s): rules[s.name.s] - else: beautifyName(s.name.s, n.sym.kind) - - loadFile(n.info) - - let line = gSourceFiles[n.info.fileIndex].lines[n.info.line-1] - var first = min(n.info.col.int, line.len) - if first < 0: return - #inc first, skipIgnoreCase(line, "proc ", first) - while first > 0 and line[first-1] in Letters: dec first - if first < 0: return - if line[first] == '`': inc first - - if {sfImportc, sfExportc} * s.flags != {}: - # careful, we must ensure the resulting name still matches the external - # name: - if newName != s.name.s and newName != s.loc.r.ropeToStr and - lfFullExternalName notin s.loc.flags: - #Message(n.info, errGenerated, - # "cannot rename $# to $# due to external name" % [s.name.s, newName]) - cannotRename.incl(s.id) - return - let last = first+identLen(line, first)-1 - if differ(line, first, last, newName): - # last-first+1 != newName.len or - var x = line.subStr(0, first-1) & newName & line.substr(last+1) - when removeTP: - # the WinAPI module is full of 'TX = X' which after the substitution - # becomes 'X = X'. We remove those lines: - if x.match(peg"\s* {\ident} \s* '=' \s* y$1 ('#' .*)?"): - x = "" - - system.shallowCopy(gSourceFiles[n.info.fileIndex].lines[n.info.line-1], x) - gSourceFiles[n.info.fileIndex].dirty = true - proc check(c: PGen, n: PNode) = case n.kind - of nkSym: checkUse(n, n.sym) + of nkSym: checkUse(n.info, n.sym) of nkBlockStmt, nkBlockExpr, nkBlockType: checkDef(c, n[0]) check(c, n.sons[1]) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 6b62c48c5..0b1312ccc 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -146,28 +146,29 @@ proc makeNimString(s: string): string = for i in countup(0, len(s)-1): add(result, toNimChar(s[i])) add(result, '\"') -proc putComment(g: var TSrcGen, s: string) = +proc putComment(g: var TSrcGen, s: string) = + if s.isNil: return var i = 0 var comIndent = 1 var isCode = (len(s) >= 2) and (s[1] != ' ') var ind = g.lineLen var com = "" - while true: + while true: case s[i] - of '\0': - break - of '\x0D': + of '\0': + break + of '\x0D': put(g, tkComment, com) com = "" inc(i) if s[i] == '\x0A': inc(i) optNL(g, ind) - of '\x0A': + of '\x0A': put(g, tkComment, com) com = "" inc(i) optNL(g, ind) - of '#': + of '#': add(com, s[i]) inc(i) comIndent = 0 @@ -175,10 +176,10 @@ proc putComment(g: var TSrcGen, s: string) = add(com, s[i]) inc(i) inc(comIndent) - of ' ', '\x09': + of ' ', '\x09': add(com, s[i]) inc(i) - else: + else: # we may break the comment into a multi-line comment if the line # gets too long: # compute length of the following word: @@ -195,10 +196,10 @@ proc putComment(g: var TSrcGen, s: string) = optNL(g) proc maxLineLength(s: string): int = - result = 0 + if s.isNil: return 0 var i = 0 var lineLen = 0 - while true: + while true: case s[i] of '\0': break @@ -459,7 +460,7 @@ proc lsub(n: PNode): int = of nkBreakStmt: result = lsub(n.sons[0]) + len("break_") of nkContinueStmt: result = lsub(n.sons[0]) + len("continue_") of nkPragma: result = lcomma(n) + 4 - of nkCommentStmt: result = len(n.comment) + of nkCommentStmt: result = if n.comment.isNil: 0 else: len(n.comment) of nkOfBranch: result = lcomma(n, 0, - 2) + lsub(lastSon(n)) + len("of_:_") of nkImportAs: result = lsub(n.sons[0]) + len("_as_") + lsub(n.sons[1]) of nkElifBranch: result = lsons(n) + len("elif_:_") diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim index 4433ed4ab..09b92cd8a 100644 --- a/compiler/rodutils.nim +++ b/compiler/rodutils.nim @@ -10,7 +10,7 @@ ## Serialization utilities for the compiler. import strutils -proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", nodecl, varargs.} +proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", header: "<stdio.h>", nodecl, varargs.} proc toStrMaxPrecision*(f: BiggestFloat): string = if f != f: diff --git a/compiler/sem.nim b/compiler/sem.nim index 7d129caf4..8025ef70d 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -15,7 +15,8 @@ import magicsys, parser, nversion, nimsets, semfold, importer, procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch, intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting, - evaltempl, patterns, parampatterns, sempass2, pretty, semmacrosanity + evaltempl, patterns, parampatterns, sempass2, pretty, semmacrosanity, + semparallel # implementation @@ -134,7 +135,7 @@ proc isTopLevel(c: PContext): bool {.inline.} = result = c.currentScope.depthLevel <= 2 proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym = - result = newSym(kind, considerAcc(n), getCurrOwner(), n.info) + result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info) proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = # like newSymS, but considers gensym'ed symbols @@ -147,7 +148,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = # template; we must fix it here: see #909 result.owner = getCurrOwner() else: - result = newSym(kind, considerAcc(n), getCurrOwner(), n.info) + result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info) proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, allowed: TSymFlags): PSym @@ -268,11 +269,15 @@ include hlo, seminst, semcall proc semAfterMacroCall(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = + ## Semantically check the output of a macro. + ## This involves processes such as re-checking the macro output for type + ## coherence, making sure that variables declared with 'let' aren't + ## reassigned, and binding the unbound identifiers that the macro output + ## contains. inc(evalTemplateCounter) if evalTemplateCounter > 100: globalError(s.info, errTemplateInstantiationTooNested) - let oldFriend = c.friendModule - c.friendModule = s.owner.getModule + c.friendModules.add(s.owner.getModule) result = n if s.typ.sons[0] == nil: @@ -296,11 +301,13 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym, result = fitNode(c, s.typ.sons[0], result) #GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0])) dec(evalTemplateCounter) - c.friendModule = oldFriend + discard c.friendModules.pop() proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, flags: TExprFlags = {}): PNode = - markUsed(n, sym) + pushInfoContext(nOrig.info) + + markUsed(n.info, sym) if sym == c.p.owner: globalError(n.info, errRecursiveDependencyX, sym.name.s) @@ -310,6 +317,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, result = evalMacroCall(c.module, n, nOrig, sym) if efNoSemCheck notin flags: result = semAfterMacroCall(c, result, sym, flags) + popInfoContext() proc forceBool(c: PContext, n: PNode): PNode = result = fitNode(c, getSysType(tyBool), n) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 04d280ce2..65a2d7ab8 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -168,7 +168,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, pickBest(callOp) if overloadsState == csEmpty and result.state == csEmpty: - localError(n.info, errUndeclaredIdentifier, considerAcc(f).s) + localError(n.info, errUndeclaredIdentifier, considerQuotedIdent(f).s) return elif result.state != csMatch: if nfExprCall in n.flags: @@ -251,7 +251,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], finalCallee) + markUsed(n.sons[0].info, finalCallee) if finalCallee.ast == nil: internalError(n.info, "calleeSym.ast is nil") # XXX: remove this check! if finalCallee.ast.sons[genericParamsPos].kind != nkEmpty: @@ -283,7 +283,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = var m: TCandidate initCandidate(c, m, s, n) var newInst = generateInstance(c, s, m.bindings, n.info) - markUsed(n, s) + markUsed(n.info, s) result = newSymNode(newInst, n.info) proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 987a70a41..abecc1b6d 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -52,7 +52,7 @@ type importTable*: PScope # scope for all imported symbols topLevelScope*: PScope # scope for all top-level symbols p*: PProcCon # procedure context - friendModule*: PSym # current friend module; may access private data; + friendModules*: seq[PSym] # friend modules; may access private data; # this is used so that generic instantiations # can access private object fields instCounter*: int # to prevent endless instantiations @@ -91,6 +91,7 @@ type generics*: seq[TInstantiationPair] # pending list of instantiated generics to compile lastGenericIdx*: int # used for the generics stack hloLoopDetector*: int # used to prevent endless loops in the HLO + inParallelStmt*: int proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = result.genericSym = s @@ -168,7 +169,7 @@ proc newContext(module: PSym): PContext = initLinkedList(result.libs) append(result.optionStack, newOptionEntry()) result.module = module - result.friendModule = module + result.friendModules = @[module] result.converters = @[] result.patterns = @[] result.includedFiles = initIntSet() diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 652e1dd4b..7f97124e1 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -12,7 +12,7 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, flags: TExprFlags = {}): PNode = - markUsed(n, s) + markUsed(n.info, s) pushInfoContext(n.info) result = evalTemplate(n, s, getCurrOwner()) if efNoSemCheck notin flags: result = semAfterMacroCall(c, result, s, flags) @@ -78,7 +78,7 @@ proc inlineConst(n: PNode, s: PSym): PNode {.inline.} = proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = case s.kind of skConst: - markUsed(n, s) + markUsed(n.info, s) case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, tyTuple, tySet, tyUInt..tyUInt64: @@ -101,7 +101,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = of skMacro: result = semMacroExpr(c, n, n, s, flags) of skTemplate: result = semTemplateExpr(c, n, s, flags) of skVar, skLet, skResult, skParam, skForVar: - markUsed(n, s) + markUsed(n.info, s) # if a proc accesses a global variable, it is not side effect free: if sfGlobal in s.flags: incl(c.p.owner.flags, sfSideEffect) @@ -123,13 +123,13 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = n.typ = s.typ return n of skType: - markUsed(n, s) + markUsed(n.info, s) if s.typ.kind == tyStatic and s.typ.n != nil: return s.typ.n result = newSymNode(s, n.info) result.typ = makeTypeDesc(c, s.typ) else: - markUsed(n, s) + markUsed(n.info, s) result = newSymNode(s, n.info) type @@ -166,6 +166,7 @@ proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus = elif (skipTypes(castDest, abstractVarRange).kind in IntegralTypes) and (skipTypes(src, abstractVarRange-{tyTypeDesc}).kind in IntegralTypes): # accept conversion between integral types + discard else: # we use d, s here to speed up that operation a bit: case cmpTypes(c, d, s) @@ -175,21 +176,26 @@ proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus = else: discard -proc isCastable(dst, src: PType): bool = +proc isCastable(dst, src: PType): bool = + ## Checks whether the source type can be casted to the destination type. + ## Casting is very unrestrictive; casts are allowed as long as + ## castDest.size >= src.size, and typeAllowed(dst, skParam) #const # castableTypeKinds = {tyInt, tyPtr, tyRef, tyCstring, tyString, # tySequence, tyPointer, tyNil, tyOpenArray, # tyProc, tySet, tyEnum, tyBool, tyChar} - var ds, ss: BiggestInt - # this is very unrestrictive; cast is allowed if castDest.size >= src.size - ds = computeSize(dst) - ss = computeSize(src) - if ds < 0: + var dstSize, srcSize: BiggestInt + + dstSize = computeSize(dst) + srcSize = computeSize(src) + if dstSize < 0: result = false - elif ss < 0: + elif srcSize < 0: + result = false + elif not typeAllowed(dst, skParam): result = false else: - result = (ds >= ss) or + result = (dstSize >= srcSize) or (skipTypes(dst, abstractInst).kind in IntegralTypes) or (skipTypes(src, abstractInst-{tyTypeDesc}).kind in IntegralTypes) @@ -247,12 +253,13 @@ proc semConv(c: PContext, n: PNode): PNode = let it = op.sons[i] let status = checkConvertible(c, result.typ, it.typ) if status in {convOK, convNotNeedeed}: - markUsed(n, it.sym) + markUsed(n.info, it.sym) markIndirect(c, it.sym) return it localError(n.info, errUseQualifier, op.sons[0].sym.name.s) proc semCast(c: PContext, n: PNode): PNode = + ## Semantically analyze a casting ("cast[type](param)") if optSafeCode in gGlobalOptions: localError(n.info, errCastNotInSafeMode) #incl(c.p.owner.flags, sfSideEffect) checkSonsLen(n, 2) @@ -386,7 +393,7 @@ proc semOpAux(c: PContext, n: PNode) = var a = n.sons[i] if a.kind == nkExprEqExpr and sonsLen(a) == 2: var info = a.sons[0].info - a.sons[0] = newIdentNode(considerAcc(a.sons[0]), info) + a.sons[0] = newIdentNode(considerQuotedIdent(a.sons[0]), info) a.sons[1] = semExprWithType(c, a.sons[1], flags) a.typ = a.sons[1].typ else: @@ -785,6 +792,10 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = n.flags.incl nfExprCall result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags) if result == nil: return errorNode(c, n) + elif result.kind notin nkCallKinds: + # the semExpr() in overloadedCallOpr can even break this condition! + # See bug #904 of how to trigger it: + return result #result = afterCallActions(c, result, nOrig, flags) fixAbstractType(c, result) analyseIfAddressTakenInCall(c, result) @@ -964,12 +975,12 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared}) if s != nil: - markUsed(n.sons[1], s) + markUsed(n.sons[1].info, s) return semSym(c, n, s, flags) n.sons[0] = semExprWithType(c, n.sons[0], flags+{efDetermineType}) #restoreOldStyleType(n.sons[0]) - var i = considerAcc(n.sons[1]) + var i = considerQuotedIdent(n.sons[1]) var ty = n.sons[0].typ var f: PSym = nil result = nil @@ -987,7 +998,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result = newSymNode(f) result.info = n.info result.typ = ty - markUsed(n, f) + markUsed(n.info, f) return of tyTypeParamsHolders: return readTypeParameter(c, ty, i, n.info) @@ -1019,7 +1030,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], f) + markUsed(n.sons[1].info, f) n.sons[0] = makeDeref(n.sons[0]) n.sons[1] = newSymNode(f) # we now have the correct field n.typ = f.typ @@ -1032,7 +1043,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], f) + markUsed(n.sons[1].info, f) n.sons[0] = makeDeref(n.sons[0]) n.sons[1] = newSymNode(f) n.typ = f.typ @@ -1050,7 +1061,7 @@ proc dotTransformation(c: PContext, n: PNode): PNode = addSon(result, n.sons[1]) addSon(result, copyTree(n[0])) else: - var i = considerAcc(n.sons[1]) + var i = considerQuotedIdent(n.sons[1]) result = newNodeI(nkDotCall, n.info) result.flags.incl nfDotField addSon(result, newIdentNode(i, n[1].info)) @@ -1134,7 +1145,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 = considerAcc(a[1]) + var id = considerQuotedIdent(a[1]) 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 @@ -1368,7 +1379,7 @@ proc lookUpForDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PSym = else: localError(n.sons[1].info, errIdentifierExpected, "") of nkAccQuoted: - result = lookUpForDefined(c, considerAcc(n), onlyCurrentScope) + result = lookUpForDefined(c, considerQuotedIdent(n), onlyCurrentScope) of nkSym: result = n.sym else: @@ -1387,11 +1398,6 @@ proc semDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PNode = result.info = n.info result.typ = getSysType(tyBool) -proc setMs(n: PNode, s: PSym): PNode = - result = n - n.sons[0] = newSymNode(s) - n.sons[0].info = n.info - proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym = ## The argument to the proc should be nkCall(...) or similar ## Returns the macro/template symbol @@ -1448,7 +1454,7 @@ proc semExpandToAst(c: PContext, n: PNode): PNode = if expandedSym.kind == skError: return n macroCall.sons[0] = newSymNode(expandedSym, macroCall.info) - markUsed(n, expandedSym) + markUsed(n.info, expandedSym) for i in countup(1, macroCall.len-1): macroCall.sons[i] = semExprWithType(c, macroCall[i], {}) @@ -1583,6 +1589,27 @@ proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode = else: result = semDirectOp(c, n, flags) +proc createFlowVar(c: PContext; t: PType; info: TLineInfo): PType = + result = newType(tyGenericInvokation, c.module) + addSonSkipIntLit(result, magicsys.getCompilerProc("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") + if sym == nil: + localError(info, errSystemNeeds, "nimCreateFlowVar") + var bindings: TIdTable + initIdTable(bindings) + bindings.idTablePut(sym.ast[genericParamsPos].sons[0].typ, t) + result = c.semGenerateInstance(c, sym, bindings, info) + +proc setMs(n: PNode, s: PSym): PNode = + result = n + n.sons[0] = newSymNode(s) + n.sons[0].info = n.info + proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = # this is a hotspot in the compiler! # DON'T forget to update ast.SpecialSemMagics if you add a magic here! @@ -1604,6 +1631,22 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = checkSonsLen(n, 2) result = newStrNodeT(renderTree(n[1], {renderNoComments}), n) result.typ = getSysType(tyString) + of mParallel: + result = setMs(n, s) + var x = n.lastSon + if x.kind == nkDo: x = x.sons[bodyPos] + inc c.inParallelStmt + result.sons[1] = semStmt(c, x) + dec c.inParallelStmt + of mSpawn: + result = setMs(n, s) + result.sons[1] = semExpr(c, n.sons[1]) + if not result[1].typ.isEmptyType: + if c.inParallelStmt > 0: + result.typ = result[1].typ + else: + result.typ = createFlowVar(c, result[1].typ, n.info) + result.add instantiateCreateFlowVarCall(c, result[1].typ, n.info).newSymNode else: result = semDirectOp(c, n, flags) proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = @@ -1842,7 +1885,7 @@ proc semBlock(c: PContext, n: PNode): PNode = if sfGenSym notin labl.flags: addDecl(c, labl) n.sons[0] = newSymNode(labl, n.sons[0].info) - suggestSym(n.sons[0], labl) + suggestSym(n.sons[0].info, labl) n.sons[1] = semExpr(c, n.sons[1]) n.typ = n.sons[1].typ if isEmptyType(n.typ): n.kind = nkBlockStmt @@ -1962,7 +2005,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = var s = qualifiedLookUp(c, n.sons[0], mode) if s != nil: if gCmd == cmdPretty and n.sons[0].kind == nkDotExpr: - pretty.checkUse(n.sons[0].sons[1], s) + pretty.checkUse(n.sons[0].sons[1].info, s) case s.kind of skMacro: if sfImmediate notin s.flags: @@ -2060,6 +2103,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkClosedSymChoice, nkOpenSymChoice: # handling of sym choices is context dependent # the node is left intact for now + discard of nkStaticExpr: result = semStaticExpr(c, n) of nkAsgn: result = semAsgn(c, n) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 79abfaf4d..30e02dcc9 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -380,6 +380,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of mInSet: result = newIntNodeT(ord(inSet(a, b)), n) of mRepr: # BUGFIX: we cannot eval mRepr here for reasons that I forgot. + discard of mIntToStr, mInt64ToStr: result = newStrNodeT($(getOrdValue(a)), n) of mBoolToStr: if getOrdValue(a) == 0: result = newStrNodeT("false", n) @@ -404,10 +405,8 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh, mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, - mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn: + mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn, mParallel: discard - of mRand: - result = newIntNodeT(math.random(a.getInt.int), n) else: internalError(a.info, "evalOp(" & $m & ')') proc getConstIfExpr(c: PSym, n: PNode): PNode = @@ -538,17 +537,18 @@ proc foldArrayAccess(m: PSym, n: PNode): PNode = var idx = getOrdValue(y) case x.kind of nkPar: - if (idx >= 0) and (idx < sonsLen(x)): + 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) - of nkBracket: - if (idx >= 0) and (idx < sonsLen(x)): result = x.sons[int(idx)] + of nkBracket: + idx = idx - x.typ.firstOrd + if idx >= 0 and idx < x.len: result = x.sons[int(idx)] else: localError(n.info, errIndexOutOfBounds) - of nkStrLit..nkTripleStrLit: + of nkStrLit..nkTripleStrLit: result = newNodeIT(nkCharLit, x.info, n.typ) - if (idx >= 0) and (idx < len(x.strVal)): + if idx >= 0 and idx < len(x.strVal): result.intVal = ord(x.strVal[int(idx)]) elif idx == len(x.strVal): discard diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index da4a2a132..934434951 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -69,7 +69,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym): PNode = proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var TIntSet): PNode = result = n - let ident = considerAcc(n) + let ident = considerQuotedIdent(n) var s = searchInScopes(c, ident) if s == nil: if ident.id notin ctx and withinMixin notin flags: @@ -82,7 +82,37 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, else: result = semGenericStmtSymbol(c, n, s) # else: leave as nkIdent + +proc newDot(n, b: PNode): PNode = + result = newNodeI(nkDotExpr, n.info) + result.add(n.sons[0]) + result.add(b) + +proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, + ctx: var TIntSet): PNode = + assert n.kind == nkDotExpr + let luf = if withinMixin notin flags: {checkUndeclared} else: {} + var s = qualifiedLookUp(c, n, luf) + if s != nil: + result = semGenericStmtSymbol(c, n, s) + else: + result = n + let n = n[1] + let ident = considerQuotedIdent(n) + var s = searchInScopes(c, ident) + if s != nil and s.kind in routineKinds: + if withinBind in flags: + result = newDot(result, symChoice(c, n, s, scClosed)) + elif s.name.id in ctx: + result = newDot(result, symChoice(c, n, s, scForceOpen)) + else: + let sym = semGenericStmtSymbol(c, n, s) + if sym.kind == nkSym: + result = newDot(result, symChoice(c, n, s, scForceOpen)) + else: + result = newDot(result, sym) + proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var TIntSet): PNode = result = n @@ -91,10 +121,11 @@ proc semGenericStmt(c: PContext, n: PNode, of nkIdent, nkAccQuoted: result = lookup(c, n, flags, ctx) of nkDotExpr: - let luf = if withinMixin notin flags: {checkUndeclared} else: {} - var s = qualifiedLookUp(c, n, luf) - if s != nil: result = semGenericStmtSymbol(c, n, s) + #let luf = if withinMixin notin flags: {checkUndeclared} else: {} + #var s = qualifiedLookUp(c, n, luf) + #if s != nil: result = semGenericStmtSymbol(c, n, s) # XXX for example: ``result.add`` -- ``add`` needs to be looked up here... + result = fuzzyLookup(c, n, flags, ctx) of nkEmpty, nkSym..nkNilLit: # see tests/compile/tgensymgeneric.nim: # We need to open the gensym'ed symbol again so that the instantiation @@ -114,7 +145,7 @@ proc semGenericStmt(c: PContext, n: PNode, let fn = n.sons[0] var s = qualifiedLookUp(c, fn, {}) if s == nil and withinMixin notin flags and - fn.kind in {nkIdent, nkAccQuoted} and considerAcc(fn).id notin ctx: + fn.kind in {nkIdent, nkAccQuoted} and considerQuotedIdent(fn).id notin ctx: localError(n.info, errUndeclaredIdentifier, fn.renderTree) var first = 0 @@ -141,6 +172,7 @@ proc semGenericStmt(c: PContext, n: PNode, # symbol lookup ... of skUnknown, skParam: # Leave it as an identifier. + discard of skProc, skMethod, skIterators, skConverter: result.sons[0] = symChoice(c, n.sons[0], s, scOption) first = 1 diff --git a/compiler/seminst.nim b/compiler/seminst.nim index f7d5fa6f8..b93d7ca15 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -190,6 +190,9 @@ proc instantiateProcType(c: PContext, pt: TIdTable, proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, info: TLineInfo): PSym = + ## Generates a new instance of a generic procedure. + ## The `pt` parameter is a type-unsafe mapping table used to link generic + ## parameters to their concrete types within the generic instance. # no need to instantiate generic templates/macros: if fn.kind in {skTemplate, skMacro}: return fn # generates an instantiated proc @@ -199,8 +202,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, var n = copyTree(fn.ast) # NOTE: for access of private fields within generics from a different module # we set the friend module: - var oldFriend = c.friendModule - c.friendModule = getModule(fn) + c.friendModules.add(getModule(fn)) #let oldScope = c.currentScope #c.currentScope = fn.scope result = copySym(fn, false) @@ -236,6 +238,6 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, closeScope(c) # close scope for parameters popOwner() #c.currentScope = oldScope - c.friendModule = oldFriend + discard c.friendModules.pop() dec(c.instCounter) if result.kind == skMethod: finishMethod(c, result) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 4caf1fb8e..f943e7006 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2013 Andreas Rumpf +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -131,4 +131,3 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, of mNBindSym: result = semBindSym(c, n) of mLocals: result = semLocals(c, n) else: result = n - diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim new file mode 100644 index 000000000..c594a4788 --- /dev/null +++ b/compiler/semparallel.nim @@ -0,0 +1,465 @@ +# +# +# The Nimrod Compiler +# (c) Copyright 2014 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Semantic checking for 'parallel'. + +# - codegen needs to support mSlice (+) +# - lowerings must not perform unnecessary copies (+) +# - slices should become "nocopy" to openArray (+) +# - need to perform bound checks (+) +# +# - parallel needs to insert a barrier (+) +# - passed arguments need to be ensured to be "const" +# - what about 'f(a)'? --> f shouldn't have side effects anyway +# - passed arrays need to be ensured not to alias +# - passed slices need to be ensured to be disjoint (+) +# - output slices need special logic (+) + +import + ast, astalgo, idents, lowerings, magicsys, guards, sempass2, msgs, + renderer +from trees import getMagic +from strutils import `%` + +discard """ + +one major problem: + spawn f(a[i]) + inc i + spawn f(a[i]) +is valid, but + spawn f(a[i]) + spawn f(a[i]) + inc i +is not! However, + spawn f(a[i]) + if guard: inc i + spawn f(a[i]) +is not valid either! --> We need a flow dependent analysis here. + +However: + while foo: + spawn f(a[i]) + inc i + spawn f(a[i]) + +Is not valid either! --> We should really restrict 'inc' to loop endings? + +The heuristic that we implement here (that has no false positives) is: Usage +of 'i' in a slice *after* we determined the stride is invalid! +""" + +type + TDirection = enum + ascending, descending + MonotonicVar = object + v, alias: PSym # to support the ordinary 'countup' iterator + # we need to detect aliases + lower, upper, stride: PNode + dir: TDirection + blacklisted: bool # blacklisted variables that are not monotonic + AnalysisCtx = object + locals: seq[MonotonicVar] + slices: seq[tuple[x,a,b: PNode, spawnId: int, inLoop: bool]] + guards: TModel # nested guards + args: seq[PSym] # args must be deeply immutable + spawns: int # we can check that at last 1 spawn is used in + # the 'parallel' section + currentSpawnId: int + inLoop: int + +let opSlice = createMagic("slice", mSlice) + +proc initAnalysisCtx(): AnalysisCtx = + result.locals = @[] + result.slices = @[] + result.args = @[] + result.guards = @[] + +proc lookupSlot(c: AnalysisCtx; s: PSym): int = + for i in 0.. <c.locals.len: + if c.locals[i].v == s or c.locals[i].alias == s: return i + return -1 + +proc getSlot(c: var AnalysisCtx; v: PSym): ptr MonotonicVar = + let s = lookupSlot(c, v) + if s >= 0: return addr(c.locals[s]) + let L = c.locals.len + c.locals.setLen(L+1) + c.locals[L].v = v + return addr(c.locals[L]) + +proc gatherArgs(c: var AnalysisCtx; n: PNode) = + for i in 0.. <n.safeLen: + let root = getRoot n[i] + if root != nil: + block addRoot: + for r in items(c.args): + if r == root: break addRoot + c.args.add root + gatherArgs(c, n[i]) + +proc isSingleAssignable(n: PNode): bool = + n.kind == nkSym and (let s = n.sym; + s.kind in {skTemp, skForVar, skLet} and + {sfAddrTaken, sfGlobal} * s.flags == {}) + +proc isLocal(n: PNode): bool = + n.kind == nkSym and (let s = n.sym; + s.kind in {skResult, skTemp, skForVar, skVar, skLet} and + {sfAddrTaken, sfGlobal} * s.flags == {}) + +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") + else: + for i in 0 .. <n.safeLen: checkLocal(c, n.sons[i]) + +template `?`(x): expr = x.renderTree + +proc checkLe(c: AnalysisCtx; a, b: PNode) = + case proveLe(c.guards, a, b) + of impUnknown: + localError(a.info, "cannot prove: " & ?a & " <= " & ?b) + of impYes: discard + of impNo: + localError(a.info, "can prove: " & ?a & " > " & ?b) + +proc checkBounds(c: AnalysisCtx; arr, idx: PNode) = + checkLe(c, arr.lowBound, idx) + checkLe(c, idx, arr.highBound) + +proc addLowerBoundAsFacts(c: var AnalysisCtx) = + for v in c.locals: + if not v.blacklisted: + c.guards.addFactLe(v.lower, newSymNode(v.v)) + +proc addSlice(c: var AnalysisCtx; n: PNode; x, le, ri: PNode) = + checkLocal(c, n) + let le = le.canon + let ri = ri.canon + # perform static bounds checking here; and not later! + let oldState = c.guards.len + addLowerBoundAsFacts(c) + c.checkBounds(x, le) + c.checkBounds(x, ri) + c.guards.setLen(oldState) + c.slices.add((x, le, ri, c.currentSpawnId, c.inLoop > 0)) + +proc overlap(m: TModel; x,y,c,d: PNode) = + # X..Y and C..D overlap iff (X <= D and C <= Y) + case proveLe(m, x, d) + of impUnknown: + localError(x.info, + "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" % + [?x, ?d, ?x, ?y, ?c, ?d]) + of impYes: + case proveLe(m, c, y) + of impUnknown: + localError(x.info, + "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" % + [?c, ?y, ?x, ?y, ?c, ?d]) + of impYes: + localError(x.info, "($#)..($#) not disjoint from ($#)..($#)" % [?x, ?y, ?c, ?d]) + of impNo: discard + of impNo: discard + +proc stride(c: AnalysisCtx; n: PNode): BiggestInt = + if isLocal(n): + let s = c.lookupSlot(n.sym) + if s >= 0 and c.locals[s].stride != nil: + result = c.locals[s].stride.intVal + else: + for i in 0 .. <n.safeLen: result += stride(c, n.sons[i]) + +proc subStride(c: AnalysisCtx; n: PNode): PNode = + # substitute with stride: + 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 + else: + result = n + elif n.safeLen > 0: + result = shallowCopy(n) + for i in 0 .. <n.len: result.sons[i] = subStride(c, n.sons[i]) + else: + result = n + +proc checkSlicesAreDisjoint(c: var AnalysisCtx) = + # this is the only thing that we need to perform after we have traversed + # the whole tree so that the strides are available. + # First we need to add all the computed lower bounds: + 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)) + # Another tricky example is: + # while true: + # spawn f(a[i]) + # spawn f(a[i+1]) + # inc i # inc i, 2 would be correct here + # + # Or even worse: + # while true: + # spawn f(a[i+1 .. i+3]) + # spawn f(a[i+4 .. i+5]) + # inc i, 4 + # Prove that i*k*stride + 3 != i*k'*stride + 5 + # For the correct example this amounts to + # i*k*2 != i*k'*2 + 1 + # which is true. + # For now, we don't try to prove things like that at all, even though it'd + # be feasible for many useful examples. Instead we attach the slice to + # a spawn and if the attached spawns differ, we bail out: + for i in 0 .. high(c.slices): + for j in i+1 .. high(c.slices): + let x = c.slices[i] + let y = c.slices[j] + if x.spawnId != y.spawnId and guards.sameTree(x.x, y.x): + if not x.inLoop or not y.inLoop: + # 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) + 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) + 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 ($#)..($#)" % + [?x.a, ?x.b, ?y.a, ?y.b]) + else: + localError(x.x.info, "cannot prove ($#)..($#) disjoint from ($#)..($#)" % + [?x.a, ?x.b, ?y.a, ?y.b]) + +proc analyse(c: var AnalysisCtx; n: PNode) + +proc analyseSons(c: var AnalysisCtx; n: PNode) = + for i in 0 .. <safeLen(n): analyse(c, n[i]) + +proc min(a, b: PNode): PNode = + if a.isNil: result = b + elif a.intVal < b.intVal: result = a + else: result = b + +proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags + +proc analyseCall(c: var AnalysisCtx; n: PNode; op: PSym) = + if op.magic == mSpawn: + inc c.spawns + let oldSpawnId = c.currentSpawnId + c.currentSpawnId = c.spawns + gatherArgs(c, n[1]) + analyseSons(c, n) + c.currentSpawnId = oldSpawnId + elif op.magic == mInc or (op.name.s == "+=" and op.fromSystem): + if n[1].isLocal: + let incr = n[2].skipConv + if incr.kind in {nkCharLit..nkUInt32Lit} and incr.intVal > 0: + let slot = c.getSlot(n[1].sym) + slot.stride = min(slot.stride, incr) + analyseSons(c, n) + elif op.name.s == "[]" and op.fromSystem: + c.addSlice(n, n[1], n[2][1], n[2][2]) + analyseSons(c, n) + elif op.name.s == "[]=" and op.fromSystem: + c.addSlice(n, n[1], n[2][1], n[2][2]) + analyseSons(c, n) + else: + analyseSons(c, n) + +proc analyseCase(c: var AnalysisCtx; n: PNode) = + analyse(c, n.sons[0]) + let oldFacts = c.guards.len + for i in 1.. <n.len: + let branch = n.sons[i] + setLen(c.guards, oldFacts) + addCaseBranchFacts(c.guards, n, i) + for i in 0 .. <branch.len: + analyse(c, branch.sons[i]) + setLen(c.guards, 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])) + + analyse(c, n.sons[0].sons[1]) + for i in 1.. <n.len: + let branch = n.sons[i] + setLen(c.guards, oldFacts) + for j in 0..i-1: + addFactNeg(c.guards, canon(n.sons[j].sons[0])) + if branch.len > 1: + addFact(c.guards, canon(branch.sons[0])) + for i in 0 .. <branch.len: + analyse(c, branch.sons[i]) + setLen(c.guards, oldFacts) + +proc analyse(c: var AnalysisCtx; n: PNode) = + case n.kind + of nkAsgn, nkFastAsgn: + if n[0].isSingleAssignable and n[1].isLocal: + let slot = c.getSlot(n[1].sym) + slot.alias = n[0].sym + elif n[0].isLocal: + # since we already ensure sfAddrTaken is not in s.flags, we only need to + # prevent direct assignments to the monotonic variable: + let slot = c.getSlot(n[0].sym) + slot.blackListed = true + invalidateFacts(c.guards, n[0]) + analyseSons(c, n) + addAsgnFact(c.guards, n[0], n[1]) + of nkCallKinds: + # direct call: + 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]) + analyseSons(c, n) + of nkReturnStmt, nkRaiseStmt, nkTryStmt: + localError(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: + for it in n: + let value = it.lastSon + if value.kind != nkEmpty: + for j in 0 .. it.len-3: + 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") + analyse(c, value) + of nkCaseStmt: analyseCase(c, n) + of nkIfStmt, nkIfExpr: analyseIf(c, n) + of nkWhileStmt: + analyse(c, n.sons[0]) + # 'while true' loop? + inc c.inLoop + if isTrue(n.sons[0]): + analyseSons(c, n.sons[1]) + else: + # loop may never execute: + let oldState = c.locals.len + let oldFacts = c.guards.len + addFact(c.guards, canon(n.sons[0])) + analyse(c, n.sons[1]) + setLen(c.locals, oldState) + setLen(c.guards, oldFacts) + # we know after the loop the negation holds: + if not hasSubnodeWith(n.sons[1], nkBreakStmt): + addFactNeg(c.guards, canon(n.sons[0])) + dec c.inLoop + of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, + nkMacroDef, nkTemplateDef, nkConstSection, nkPragma: + discard + else: + analyseSons(c, n) + +proc transformSlices(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) + result.add opSlice.newSymNode + result.add n[1] + result.add n[2][1] + result.add n[2][2] + return result + if n.safeLen > 0: + result = shallowCopy(n) + for i in 0 .. < n.len: + result.sons[i] = transformSlices(n.sons[i]) + else: + result = n + +proc transformSpawn(owner: PSym; n, barrier: PNode): PNode +proc transformSpawnSons(owner: PSym; n, barrier: PNode): PNode = + result = shallowCopy(n) + for i in 0 .. < n.len: + result.sons[i] = transformSpawn(owner, n.sons[i], barrier) + +proc transformSpawn(owner: PSym; n, barrier: PNode): PNode = + case n.kind + of nkVarSection: + 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 result.isNil: + result = newNodeI(nkStmtList, n.info) + result.add n + result.add wrapProcForSpawn(owner, m, b.typ, barrier, it[0]) + it.sons[it.len-1] = emptyNode + if result.isNil: result = n + of nkAsgn, nkFastAsgn: + let b = n[1] + if getMagic(b) == mSpawn: + let m = transformSlices(b) + return wrapProcForSpawn(owner, m, b.typ, barrier, n[0]) + result = transformSpawnSons(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) + elif n.safeLen > 0: + result = transformSpawnSons(owner, n, barrier) + else: + result = n + +proc checkArgs(a: var AnalysisCtx; n: PNode) = + discard "too implement" + +proc generateAliasChecks(a: AnalysisCtx; result: PNode) = + discard "too implement" + +proc liftParallel*(owner: PSym; n: PNode): PNode = + # this needs to be called after the 'for' loop elimination + + # first pass: + # - detect monotonic local integer variables + # - detect used slices + # - detect used arguments + #echo "PAR ", renderTree(n) + + var a = initAnalysisCtx() + let body = n.lastSon + analyse(a, body) + if a.spawns == 0: + localError(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 + incl(temp.flags, sfFromGeneric) + let tempNode = newSymNode(temp) + varSection.addVar tempNode + + let barrier = genAddrOf(tempNode) + 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) + diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 6235fb76a..c8ce5e787 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2013 Andreas Rumpf +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -89,7 +89,7 @@ proc initVarViaNew(a: PEffects, n: PNode) = if n.kind != nkSym: return let s = n.sym if {tfNeedsInit, tfNotNil} * s.typ.flags <= {tfNotNil}: - # 'x' is not nil, but that doesn't mean it's not nil children + # 'x' is not nil, but that doesn't mean its "not nil" children # are initialized: initVar(a, n) @@ -478,13 +478,18 @@ proc trackBlock(tracked: PEffects, n: PNode) = else: track(tracked, n) -proc isTrue(n: PNode): bool = +proc isTrue*(n: PNode): bool = n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or n.kind == nkIntLit and n.intVal != 0 proc paramType(op: PType, i: int): PType = if op != nil and i < op.len: result = op.sons[i] +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)) + proc track(tracked: PEffects, n: PNode) = case n.kind of nkSym: @@ -541,6 +546,7 @@ proc track(tracked: PEffects, n: PNode) = track(tracked, n.sons[0]) addAsgnFact(tracked.guards, n.sons[0], n.sons[1]) notNilCheck(tracked, n.sons[1], n.sons[0].typ) + when false: cstringCheck(tracked, n) of nkVarSection: for child in n: let last = lastSon(child) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 2429e4183..3cb9691eb 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -33,7 +33,7 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode = x.info = n.info incl(s.flags, sfUsed) n.sons[0] = x - suggestSym(x, s) + suggestSym(x.info, s) else: localError(n.info, errInvalidControlFlowX, s.name.s) elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0): @@ -66,10 +66,16 @@ proc toCover(t: PType): BiggestInt = result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc})) proc performProcvarCheck(c: PContext, n: PNode, 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 and smoduleId != c.friendModule.id: - localError(n.info, errXCannotBePassedToProcVar, s.name.s) + smoduleId != c.module.id: + block outer: + for module in c.friendModules: + if smoduleId == module.id: + break outer + localError(n.info, errXCannotBePassedToProcVar, s.name.s) proc semProcvarCheck(c: PContext, n: PNode) = let n = n.skipConv @@ -190,7 +196,7 @@ proc semCase(c: PContext, n: PNode): PNode = var typ = commonTypeBegin var hasElse = false case skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc}).kind - of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32: + of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool: chckCovered = true of tyFloat..tyFloat128, tyString, tyError: discard @@ -313,7 +319,7 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym = incl(result.flags, sfGlobal) else: result = semIdentWithPragma(c, kind, n, {}) - suggestSym(n, result) + suggestSym(n.info, result) proc checkNilable(v: PSym) = if sfGlobal in v.flags and {tfNotNil, tfNeedsInit} * v.typ.flags != {}: @@ -655,7 +661,7 @@ proc semFor(c: PContext, n: PNode): PNode = n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator}) var call = n.sons[length-2] let isCallExpr = call.kind in nkCallKinds - if isCallExpr and call.sons[0].sym.magic != mNone: + if isCallExpr and call[0].kind == nkSym and call[0].sym.magic != mNone: if call.sons[0].sym.magic == mOmpParFor: result = semForVars(c, n) result.kind = nkParForStmt @@ -822,6 +828,9 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = getCurrOwner(), s.info) proc semTypeSection(c: PContext, n: PNode): PNode = + ## Processes a type section. This must be done in separate passes, in order + ## to allow the type definitions in the section to reference each other + ## without regard for the order of their definitions. typeSectionLeftSidePass(c, n) typeSectionRightSidePass(c, n) typeSectionFinalPass(c, n) @@ -868,7 +877,7 @@ proc lookupMacro(c: PContext, n: PNode): PSym = result = n.sym if result.kind notin {skMacro, skTemplate}: result = nil else: - result = searchInScopes(c, considerAcc(n), {skMacro, skTemplate}) + result = searchInScopes(c, considerQuotedIdent(n), {skMacro, skTemplate}) proc semProcAnnotation(c: PContext, prc: PNode): PNode = var n = prc.sons[pragmasPos] @@ -879,7 +888,7 @@ proc semProcAnnotation(c: PContext, prc: PNode): PNode = let m = lookupMacro(c, key) if m == nil: if key.kind == nkIdent and key.ident.id == ord(wDelegator): - if considerAcc(prc.sons[namePos]).s == "()": + if considerQuotedIdent(prc.sons[namePos]).s == "()": prc.sons[namePos] = newIdentNode(idDelegator, prc.info) prc.sons[pragmasPos] = copyExcept(n, i) else: diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index ad34eea65..ee8b1ccb8 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -59,7 +59,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, s) + markUsed(n.info, s) else: # semantic checking requires a type; ``fitNode`` deals with it # appropriately @@ -93,7 +93,7 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var TIntSet): PNode = proc semMixinStmt(c: PContext, n: PNode, toMixin: var TIntSet): PNode = for i in 0 .. < n.len: - toMixin.incl(considerAcc(n.sons[i]).id) + toMixin.incl(considerQuotedIdent(n.sons[i]).id) result = newNodeI(nkEmpty, n.info) proc replaceIdentBySym(n: var PNode, s: PNode) = @@ -151,7 +151,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, considerAcc(n), c.owner, n.info) + result = newSym(kind, considerQuotedIdent(n), c.owner, n.info) incl(result.flags, sfGenSym) incl(result.flags, sfShadowed) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 6289ecc85..53fe4e266 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -70,9 +70,10 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = counter = x of nkSym: e = n.sons[i].sym - of nkIdent: + of nkIdent, nkAccQuoted: e = newSymS(skEnumField, n.sons[i], c) - else: illFormedAst(n) + else: + illFormedAst(n[i]) e.typ = result e.position = int(counter) if e.position == 0: hasNull = true @@ -116,7 +117,7 @@ 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(considerAcc(n.sons[2]), n.sons[2].info) + result.n = newIdentNode(considerQuotedIdent(n.sons[2]), n.sons[2].info) else: localError(n.info, errXExpectsOneTypeParam, "varargs") addSonSkipIntLit(result, errorType(c)) @@ -280,7 +281,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = else: result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared}) if result != nil: - markUsed(n, result) + markUsed(n.info, result) if result.kind == skParam and result.typ.kind == tyTypeDesc: # This is a typedesc param. is it already bound? # it's not bound when it's used multiple times in the @@ -385,6 +386,7 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, case kind of skType: # process pragmas later, because result.typ has not been set yet + discard of skField: pragma(c, result, n.sons[1], fieldPragmas) of skVar: pragma(c, result, n.sons[1], varPragmas) of skLet: pragma(c, result, n.sons[1], letPragmas) @@ -441,14 +443,14 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int, elif isRange(b): branch.sons[i] = semCaseBranchRange(c, t, b, covered) else: + # constant sets and arrays are allowed: var r = semConstExpr(c, b) # for ``{}`` we want to trigger the type mismatch in ``fitNode``: - if r.kind != nkCurly or len(r) == 0: + if r.kind notin {nkCurly, nkBracket} or len(r) == 0: checkMinSonsLen(t, 1) branch.sons[i] = skipConv(fitNode(c, t.sons[0].typ, r)) inc(covered) else: - # constant sets have special rules # 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`` @@ -560,7 +562,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, let rec = rectype.sym for i in countup(0, sonsLen(n)-3): var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported}) - suggestSym(n.sons[i], f) + suggestSym(n.sons[i].info, f) f.typ = typ f.position = pos if (rec != nil) and ({sfImportc, sfExportc} * rec.flags != {}) and @@ -825,7 +827,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result = addImplicitGeneric(newTypeS(tyAnything, c)) of tyGenericParam: - markUsed(genericParams, paramType.sym) + markUsed(info, paramType.sym) if tfWildcard in paramType.flags: paramType.flags.excl tfWildcard paramType.sym.kind = skType @@ -862,7 +864,13 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, var counter = 0 for i in countup(1, n.len - 1): var a = n.sons[i] - if a.kind != nkIdentDefs: illFormedAst(a) + if a.kind != nkIdentDefs: + # for some generic instantiations the passed ':env' parameter + # for closures has already been produced (see bug #898). We simply + # 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) var typ: PType = nil @@ -1083,8 +1091,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkCallKinds: if isRange(n): result = semRangeAux(c, n, prev) - elif n[0].kind == nkIdent: - let op = n.sons[0].ident + elif n[0].kind notin nkIdentKinds: + result = semTypeExpr(c, n) + else: + let op = considerQuotedIdent(n.sons[0]) if op.id in {ord(wAnd), ord(wOr)} or op.s == "|": checkSonsLen(n, 3) var @@ -1119,8 +1129,6 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = semAnyRef(c, n, tyRef, prev) else: result = semTypeExpr(c, n) - else: - result = semTypeExpr(c, n) of nkWhenStmt: var whenResult = semWhen(c, n, false) if whenResult.kind == nkStmtList: whenResult.kind = nkStmtListType @@ -1179,7 +1187,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = else: assignType(prev, t) result = prev - markUsed(n, n.sym) + markUsed(n.info, n.sym) else: if n.sym.kind != skError: localError(n.info, errTypeExpected) result = newOrPrevType(tyError, prev, c) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 4b91a067e..e5bf08097 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -62,7 +62,7 @@ type const isNilConversion = isConvertible # maybe 'isIntConv' fits better? -proc markUsed*(n: PNode, s: PSym) +proc markUsed*(info: TLineInfo, s: PSym) proc initCandidateAux(ctx: PContext, c: var TCandidate, callee: PType) {.inline.} = @@ -497,11 +497,11 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, proc shouldSkipDistinct(rules: PNode, callIdent: PIdent): bool = if rules.kind == nkWith: for r in rules: - if r.considerAcc == callIdent: return true + if r.considerQuotedIdent == callIdent: return true return false else: for r in rules: - if r.considerAcc == callIdent: return false + if r.considerQuotedIdent == callIdent: return false return true proc maybeSkipDistinct(t: PType, callee: PSym): PType = @@ -1058,7 +1058,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, c.converters[i]) + markUsed(arg.info, c.converters[i]) var s = newSymNode(c.converters[i]) s.typ = c.converters[i].typ s.info = arg.info @@ -1271,7 +1271,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, result = nil else: # only one valid interpretation found: - markUsed(arg, arg.sons[best].sym) + markUsed(arg.info, arg.sons[best].sym) result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best], argOrig) @@ -1302,7 +1302,7 @@ proc prepareOperand(c: PContext; a: PNode): PNode = proc prepareNamedParam(a: PNode) = if a.sons[0].kind != nkIdent: var info = a.sons[0].info - a.sons[0] = newIdentNode(considerAcc(a.sons[0]), info) + a.sons[0] = newIdentNode(considerQuotedIdent(a.sons[0]), info) proc arrayConstr(c: PContext, n: PNode): PType = result = newTypeS(tyArrayConstr, c) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index fc6ba2f77..c2bdfc5c3 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -63,8 +63,11 @@ proc filterSym(s: PSym): bool {.inline.} = proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} = let fmoduleId = getModule(f).id - result = sfExported in f.flags or fmoduleId == c.module.id or - fmoduleId == c.friendModule.id + result = sfExported in f.flags or fmoduleId == c.module.id + for module in c.friendModules: + if fmoduleId == module.id: + result = true + break proc suggestField(c: PContext, s: PSym, outputs: var int) = if filterSym(s) and fieldVisible(c, s): @@ -243,18 +246,18 @@ var usageSym*: PSym lastLineInfo: TLineInfo -proc findUsages(node: PNode, s: PSym) = - if usageSym == nil and isTracked(node.info, s.name.s.len): +proc findUsages(info: TLineInfo; s: PSym) = + if usageSym == nil and isTracked(info, s.name.s.len): usageSym = s suggestWriteln(symToStr(s, isLocal=false, sectionUsage)) elif s == usageSym: - if lastLineInfo != node.info: - suggestWriteln(symToStr(s, isLocal=false, sectionUsage, node.info)) - lastLineInfo = node.info + if lastLineInfo != info: + suggestWriteln(symToStr(s, isLocal=false, sectionUsage, info)) + lastLineInfo = info -proc findDefinition(node: PNode, s: PSym) = - if node.isNil or s.isNil: return - if isTracked(node.info, s.name.s.len): +proc findDefinition(info: TLineInfo; s: PSym) = + if s.isNil: return + if isTracked(info, s.name.s.len): suggestWriteln(symToStr(s, isLocal=false, sectionDef)) suggestQuit() @@ -313,26 +316,26 @@ proc defFromSourceMap*(i: TLineInfo) = defFromLine(gSourceMaps[i.fileIndex].lines[i.line].entries, i.col) -proc suggestSym*(n: PNode, s: PSym) {.inline.} = +proc suggestSym*(info: TLineInfo; s: PSym) {.inline.} = ## misnamed: should be 'symDeclared' if optUsages in gGlobalOptions: - findUsages(n, s) + findUsages(info, s) if optDef in gGlobalOptions: - findDefinition(n, s) + findDefinition(info, s) if isServing: - addToSourceMap(s, n.info) + addToSourceMap(s, info) -proc markUsed(n: PNode, s: PSym) = +proc markUsed(info: TLineInfo; s: PSym) = incl(s.flags, sfUsed) if {sfDeprecated, sfError} * s.flags != {}: - if sfDeprecated in s.flags: message(n.info, warnDeprecated, s.name.s) - if sfError in s.flags: localError(n.info, errWrongSymbolX, s.name.s) - suggestSym(n, s) - if gCmd == cmdPretty: checkUse(n, s) + if sfDeprecated in s.flags: message(info, warnDeprecated, s.name.s) + if sfError in s.flags: localError(info, errWrongSymbolX, s.name.s) + suggestSym(info, s) + if gCmd == cmdPretty: checkUse(info, s) proc useSym*(sym: PSym): PNode = result = newSymNode(sym) - markUsed(result, sym) + markUsed(result.info, sym) proc suggestExpr*(c: PContext, node: PNode) = var cp = msgs.inCheckpoint(node.info) diff --git a/compiler/transf.nim b/compiler/transf.nim index fb5e321b6..dece1ac18 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -546,7 +546,7 @@ proc flattenTree(root: PNode): PNode = flattenTreeAux(result, root, op) else: result = root - + proc transformCall(c: PTransf, n: PNode): PTransNode = var n = flattenTree(n) var op = getMergeOp(n) @@ -565,6 +565,9 @@ proc transformCall(c: PTransf, n: PNode): PTransNode = inc(j) add(result, a.PTransNode) if len(result) == 2: result = result[1] + elif getMagic(n) == mNBindSym: + # for bindSym(myconst) we MUST NOT perform constant folding: + result = n.PTransNode else: let s = transformSons(c, n).PNode # bugfix: check after 'transformSons' if it's still a method call: diff --git a/compiler/types.nim b/compiler/types.nim index 1f266d64f..786eea14e 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1118,6 +1118,11 @@ proc typeAllowed(t: PType, kind: TSymKind): bool = proc align(address, alignment: BiggestInt): BiggestInt = result = (address + (alignment - 1)) and not (alignment - 1) +const + szNonConcreteType* = -3 + szIllegalRecursion* = -2 + szUnknownSize* = -1 + proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt proc computeRecSizeAux(n: PNode, a, currOffset: var BiggestInt): BiggestInt = var maxAlign, maxSize, b, res: BiggestInt @@ -1151,14 +1156,9 @@ proc computeRecSizeAux(n: PNode, a, currOffset: var BiggestInt): BiggestInt = of nkSym: result = computeSizeAux(n.sym.typ, a) n.sym.offset = int(currOffset) - else: - internalError("computeRecSizeAux()") + else: a = 1 - result = - 1 - -const - szIllegalRecursion* = -2 - szUnknownSize* = -1 + result = szNonConcreteType proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = var res, maxAlign, length, currOffset: BiggestInt diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim index 790bd1047..f3121a0b4 100644 --- a/compiler/typesrenderer.nim +++ b/compiler/typesrenderer.nim @@ -1,4 +1,4 @@ -import renderer, strutils, ast, msgs, types +import renderer, strutils, ast, msgs, types, astalgo const defaultParamSeparator* = "," @@ -92,7 +92,7 @@ proc renderParamTypes(found: var seq[string], n: PNode) = if not typ.isNil: typeStr = typeToString(typ, preferExported) if typeStr.len < 1: return for i in 0 .. <typePos: - assert n[i].kind == nkIdent + assert ((n[i].kind == nkIdent) or (n[i].kind == nkAccQuoted)) found.add(typeStr) else: internalError(n.info, "renderParamTypes(found,n) with " & $n.kind) diff --git a/compiler/vm.nim b/compiler/vm.nim index 836f90967..66595856a 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -127,15 +127,20 @@ proc createStrKeepNode(x: var TFullReg) = elif x.node.kind == nkNilLit: system.reset(x.node[]) x.node.kind = nkStrLit - elif x.node.kind notin {nkStrLit..nkTripleStrLit}: + elif x.node.kind notin {nkStrLit..nkTripleStrLit} or + nfAllConst in x.node.flags: # XXX this is hacky; tests/txmlgen triggers it: x.node = newNode(nkStrLit) - # debug x.node - #assert x.node.kind in {nkStrLit..nkTripleStrLit} + # It not only hackey, it is also wrong for tgentemplate. The primary + # cause of bugs like these is that the VM does not properly distinguish + # between variable defintions (var foo = e) and variable updates (foo = e). template createStr(x) = x.node = newNode(nkStrLit) +template createSet(x) = + x.node = newNode(nkCurly) + proc moveConst(x: var TFullReg, y: TFullReg) = if x.kind != y.kind: myreset(x) @@ -433,7 +438,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if regs[rc].intVal > high(int): stackTrace(c, tos, pc, errIndexOutOfBounds) let idx = regs[rc].intVal.int - # XXX what if the array is not 0-based? -> codegen should insert a sub let src = regs[rb].node if src.kind notin {nkEmpty..nkNilLit} and idx <% src.len: regs[ra].node = src.sons[idx] @@ -499,13 +503,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = else: stackTrace(c, tos, pc, errNilAccess) of opcWrDeref: - # a[] = b + # a[] = c; b unused let ra = instr.regA - let rb = instr.regB + let rc = instr.regC case regs[ra].kind - of rkNodeAddr: putIntoNode(regs[ra].nodeAddr[], regs[rb]) - of rkRegisterAddr: regs[ra].regAddr[] = regs[rb] - of rkNode: putIntoNode(regs[ra].node, regs[rb]) + of rkNodeAddr: putIntoNode(regs[ra].nodeAddr[], regs[rc]) + of rkRegisterAddr: regs[ra].regAddr[] = regs[rc] + of rkNode: putIntoNode(regs[ra].node, regs[rc]) else: stackTrace(c, tos, pc, errNilAccess) of opcAddInt: decodeBC(rkInt) @@ -667,14 +671,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcLtu: decodeBC(rkInt) regs[ra].intVal = ord(regs[rb].intVal <% regs[rc].intVal) - of opcEqRef: + of opcEqRef, opcEqNimrodNode: decodeBC(rkInt) regs[ra].intVal = ord((regs[rb].node.kind == nkNilLit and regs[rc].node.kind == nkNilLit) or regs[rb].node == regs[rc].node) - of opcEqNimrodNode: - decodeBC(rkInt) - regs[ra].intVal = ord(regs[rb].node == regs[rc].node) of opcXor: decodeBC(rkInt) regs[ra].intVal = ord(regs[rb].intVal != regs[rc].intVal) @@ -720,18 +721,22 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].intVal = ord(containsSets(a, b) and not equalSets(a, b)) of opcMulSet: decodeBC(rkNode) + createSet(regs[ra]) move(regs[ra].node.sons, nimsets.intersectSets(regs[rb].node, regs[rc].node).sons) of opcPlusSet: decodeBC(rkNode) + createSet(regs[ra]) move(regs[ra].node.sons, nimsets.unionSets(regs[rb].node, regs[rc].node).sons) of opcMinusSet: decodeBC(rkNode) + createSet(regs[ra]) move(regs[ra].node.sons, nimsets.diffSets(regs[rb].node, regs[rc].node).sons) of opcSymdiffSet: decodeBC(rkNode) + createSet(regs[ra]) move(regs[ra].node.sons, nimsets.symdiffSets(regs[rb].node, regs[rc].node).sons) of opcConcatStr: @@ -742,11 +747,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.strVal.add getstr(regs[i]) of opcAddStrCh: decodeB(rkNode) - createStrKeepNode regs[ra] + #createStrKeepNode regs[ra] regs[ra].node.strVal.add(regs[rb].intVal.chr) of opcAddStrStr: decodeB(rkNode) - createStrKeepNode regs[ra] + #createStrKeepNode regs[ra] regs[ra].node.strVal.add(regs[rb].node.strVal) of opcAddSeqElem: decodeB(rkNode) @@ -897,10 +902,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = c.exceptionInstr = pc let (newPc, newTos) = cleanUpOnException(c, tos) # -1 because of the following 'inc' - if pc-1 < 0: + if newPc-1 < 0: bailOut(c, tos) return - pc = newPc -1 + pc = newPc-1 if tos != newTos: tos = newTos move(regs, tos.slots) @@ -983,7 +988,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = return TFullReg(kind: rkNone) of opcSetLenStr: decodeB(rkNode) - createStrKeepNode regs[ra] + #createStrKeepNode regs[ra] regs[ra].node.strVal.setLen(regs[rb].intVal.int) of opcOf: decodeBC(rkInt) diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index d0c38a2ad..c391d8415 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -207,7 +207,7 @@ const largeInstrs* = { # instructions which use 2 int32s instead of 1: opcSubStr, opcConv, opcCast, opcNewSeq, opcOf} slotSomeTemp* = slotTempUnknown - relativeJumps* = {opcTJmp, opcFJmp, opcJmp} + relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack} template opcode*(x: TInstr): TOpcode {.immediate.} = TOpcode(x.uint32 and 0xff'u32) template regA*(x: TInstr): TRegister {.immediate.} = TRegister(x.uint32 shr 8'u32 and 0xff'u32) diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 0e01f5031..9a213d813 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -15,6 +15,8 @@ proc readOutput(p: PProcess): string = discard p.waitForExit while not output.atEnd: result.add(output.readLine) + result.add("\n") + result.setLen(result.len - "\n".len) proc opGorge*(cmd, input: string): string = var p = startCmd(cmd) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 84577bb22..c1ec637dd 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -9,6 +9,24 @@ ## This module implements the code generator for the VM. +# Important things to remember: +# - The VM does not distinguish between definitions ('var x = y') and +# assignments ('x = y'). For simple data types that fit into a register +# this doesn't matter. However it matters for strings and other complex +# types that use the 'node' field; the reason is that slots are +# re-used in a register based VM. Example: +# +# .. code-block:: nimrod +# let s = a & b # no matter what, create fresh node +# s = a & b # no matter what, keep the node +# +# Also *stores* into non-temporary memory need to perform deep copies: +# a.b = x.y +# We used to generate opcAsgn for the *load* of 'x.y' but this is clearly +# wrong! We need to produce opcAsgn (the copy) for the *store*. This also +# solves the opcLdConst vs opcAsgnConst issue. Of course whether we need +# this copy depends on the involved types. + import unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef, trees, intsets, rodread, magicsys, options, lowerings @@ -84,21 +102,27 @@ proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) = # Takes the `b` register and the immediate `imm`, appies the operation `opc`, # and stores the output value into `a`. # `imm` is signed and must be within [-127, 128] - assert(imm >= -127 and imm <= 128) - let ins = (opc.uint32 or (a.uint32 shl 8'u32) or - (b.uint32 shl 16'u32) or - (imm+byteExcess).uint32 shl 24'u32).TInstr - c.code.add(ins) - c.debug.add(n.info) + if imm >= -127 and imm <= 128: + let ins = (opc.uint32 or (a.uint32 shl 8'u32) or + (b.uint32 shl 16'u32) or + (imm+byteExcess).uint32 shl 24'u32).TInstr + c.code.add(ins) + c.debug.add(n.info) + else: + localError(n.info, errGenerated, + "VM: immediate value does not fit into an int8") proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) = # Applies `opc` to `bx` and stores it into register `a` # `bx` must be signed and in the range [-32767, 32768] - assert(bx >= -32767 and bx <= 32768) - let ins = (opc.uint32 or a.uint32 shl 8'u32 or - (bx+wordExcess).uint32 shl 16'u32).TInstr - c.code.add(ins) - c.debug.add(n.info) + if bx >= -32767 and bx <= 32768: + let ins = (opc.uint32 or a.uint32 shl 8'u32 or + (bx+wordExcess).uint32 shl 16'u32).TInstr + c.code.add(ins) + c.debug.add(n.info) + else: + localError(n.info, errGenerated, + "VM: immediate value does not fit into an int16") proc xjmp(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0): TPosition = #assert opc in {opcJmp, opcFJmp, opcTJmp} @@ -331,6 +355,7 @@ proc canonValue*(n: PNode): PNode = proc rawGenLiteral(c: PCtx; n: PNode): int = result = c.constants.len assert(n.kind != nkCall) + n.flags.incl nfAllConst c.constants.add n.canonValue internalAssert result < 0x7fff @@ -484,11 +509,22 @@ proc genField(n: PNode): TRegister = "too large offset! cannot generate code for: " & s.name.s) result = s.position +proc genIndex(c: PCtx; n: PNode; arr: PType): TRegister = + if arr.skipTypes(abstractInst).kind == tyArray and (let x = firstOrd(arr); + x != 0): + let tmp = c.genx(n) + # freeing the temporary here means we can produce: regA = regA - Imm + c.freeTemp(tmp) + result = c.getTemp(n.typ) + c.gABI(n, opcSubImmInt, result, tmp, x.int) + else: + result = c.genx(n) + proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = case le.kind of nkBracketExpr: let dest = c.genx(le.sons[0], {gfAddrOf}) - let idx = c.genx(le.sons[1]) + let idx = c.genIndex(le.sons[1], le.sons[0].typ) c.gABC(le, opcWrArr, dest, idx, value) c.freeTemp(dest) c.freeTemp(idx) @@ -501,12 +537,12 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = c.freeTemp(dest) of nkDerefExpr, nkHiddenDeref: let dest = c.genx(le.sons[0], {gfAddrOf}) - c.gABC(le, opcWrDeref, dest, value) + c.gABC(le, opcWrDeref, dest, 0, value) c.freeTemp(dest) of nkSym: if le.sym.isGlobal: let dest = c.genx(le, {gfAddrOf}) - c.gABC(le, opcWrDeref, dest, value) + c.gABC(le, opcWrDeref, dest, 0, value) c.freeTemp(dest) else: discard @@ -786,10 +822,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = c.freeTemp(tmp) of mSwap: unused(n, dest) - var d = c.genx(n.sons[1]) - var tmp = c.genx(n.sons[2]) - c.gABC(n, opcSwap, d, tmp) - c.freeTemp(tmp) + var + d1 = c.genx(n.sons[1]) + d2 = c.genx(n.sons[2]) + c.gABC(n, opcSwap, d1, d2) + c.genAsgnPatch(n.sons[1], d1) + c.genAsgnPatch(n.sons[2], d2) of mIsNil: genUnaryABC(c, n, dest, opcIsNil) of mCopyStr: if dest < 0: dest = c.getTemp(n.typ) @@ -968,7 +1006,7 @@ const proc fitsRegister*(t: PType): bool = t.skipTypes(abstractInst-{tyTypeDesc}).kind in { - tyRange, tyEnum, tyBool, tyInt..tyUInt64} + tyRange, tyEnum, tyBool, tyInt..tyUInt64, tyChar} proc requiresCopy(n: PNode): bool = if n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind in atomicTypes: @@ -1065,17 +1103,36 @@ proc checkCanEval(c: PCtx; n: PNode) = not s.isOwnedBy(c.prc.sym) and s.owner != c.module: cannotEval(n) +proc isTemp(c: PCtx; dest: TDest): bool = + result = dest >= 0 and c.prc.slots[dest].kind >= slotTempUnknown + +template needsAdditionalCopy(n): expr = + not c.isTemp(dest) and not fitsRegister(n.typ) + +proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode; + dest, idx, value: TRegister) = + # opcLdObj et al really means "load address". We sometimes have to create a + # copy in order to not introduce false aliasing: + # mylocal = a.b # needs a copy of the data! + if needsAdditionalCopy(n): + var cc = c.getTemp(n.typ) + c.gABC(n, whichAsgnOpc(n), cc, value) + c.gABC(n, opc, dest, idx, cc) + c.freeTemp(cc) + else: + c.gABC(n, opc, dest, idx, value) + proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = case le.kind of nkBracketExpr: let dest = c.genx(le.sons[0], {gfAddrOf}) - let idx = c.genx(le.sons[1]) + let idx = c.genIndex(le.sons[1], le.sons[0].typ) let tmp = c.genx(ri) if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in { tyString, tyCString}: - c.gABC(le, opcWrStrIdx, dest, idx, tmp) + c.preventFalseAlias(le, opcWrStrIdx, dest, idx, tmp) else: - c.gABC(le, opcWrArr, dest, idx, tmp) + c.preventFalseAlias(le, opcWrArr, dest, idx, tmp) c.freeTemp(tmp) of nkDotExpr, nkCheckedFieldExpr: # XXX field checks here @@ -1083,12 +1140,12 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = let dest = c.genx(left.sons[0], {gfAddrOf}) let idx = genField(left.sons[1]) let tmp = c.genx(ri) - c.gABC(left, opcWrObj, dest, idx, tmp) + c.preventFalseAlias(left, opcWrObj, dest, idx, tmp) c.freeTemp(tmp) of nkDerefExpr, nkHiddenDeref: let dest = c.genx(le.sons[0], {gfAddrOf}) let tmp = c.genx(ri) - c.gABC(le, opcWrDeref, dest, tmp) + c.preventFalseAlias(le, opcWrDeref, dest, 0, tmp) c.freeTemp(tmp) of nkSym: let s = le.sym @@ -1097,24 +1154,32 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = withTemp(tmp, le.typ): c.gen(le, tmp, {gfAddrOf}) let val = c.genx(ri) - c.gABC(le, opcWrDeref, tmp, val) + c.preventFalseAlias(le, opcWrDeref, tmp, 0, val) c.freeTemp(val) else: if s.kind == skForVar: c.setSlot s internalAssert s.position > 0 or (s.position == 0 and s.kind in {skParam,skResult}) var dest: TRegister = s.position + ord(s.kind == skParam) - gen(c, ri, dest) + if needsAdditionalCopy(le) and s.kind in {skResult, skVar, skParam}: + var cc = c.getTemp(le.typ) + gen(c, ri, cc) + c.gABC(le, whichAsgnOpc(le), dest, cc) + c.freeTemp(cc) + else: + gen(c, ri, dest) else: let dest = c.genx(le, {gfAddrOf}) genAsgn(c, dest, ri, requiresCopy) proc genLit(c: PCtx; n: PNode; dest: var TDest) = - var opc = opcLdConst + # opcLdConst is now always valid. We produce the necessary copy in the + # assignments now: + #var opc = opcLdConst if dest < 0: dest = c.getTemp(n.typ) - elif c.prc.slots[dest].kind == slotFixedVar: opc = opcAsgnConst + #elif c.prc.slots[dest].kind == slotFixedVar: opc = opcAsgnConst let lit = genLiteral(c, n) - c.gABx(n, opc, dest, lit) + c.gABx(n, opcLdConst, dest, lit) proc genTypeLit(c: PCtx; t: PType; dest: var TDest) = var n = newNode(nkType) @@ -1143,7 +1208,7 @@ proc genGlobalInit(c: PCtx; n: PNode; s: PSym) = let dest = c.getTemp(s.typ) c.gABx(n, opcLdGlobal, dest, s.position) let tmp = c.genx(s.ast) - c.gABC(n, opcWrDeref, dest, tmp) + c.preventFalseAlias(n, opcWrDeref, dest, 0, tmp) c.freeTemp(dest) c.freeTemp(tmp) @@ -1179,17 +1244,22 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = # see tests/t99bott for an example that triggers it: cannotEval(n) +template needsRegLoad(): expr = + gfAddrOf notin flags and fitsRegister(n.typ.skipTypes({tyVar})) + proc genArrAccess2(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; flags: TGenFlags) = let a = c.genx(n.sons[0], flags) - let b = c.genx(n.sons[1], {}) + let b = c.genIndex(n.sons[1], n.sons[0].typ) if dest < 0: dest = c.getTemp(n.typ) - if gfAddrOf notin flags and fitsRegister(n.typ): + if needsRegLoad(): var cc = c.getTemp(n.typ) c.gABC(n, opc, cc, a, b) c.gABC(n, opcNodeToReg, dest, cc) c.freeTemp(cc) else: + #message(n.info, warnUser, "argh") + #echo "FLAGS ", flags, " ", fitsRegister(n.typ), " ", typeToString(n.typ) c.gABC(n, opc, dest, a, b) c.freeTemp(a) c.freeTemp(b) @@ -1198,7 +1268,7 @@ 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]) if dest < 0: dest = c.getTemp(n.typ) - if gfAddrOf notin flags and fitsRegister(n.typ.skipTypes({tyVar})): + if needsRegLoad(): var cc = c.getTemp(n.typ) c.gABC(n, opcLdObj, cc, a, b) c.gABC(n, opcNodeToReg, dest, cc) @@ -1298,7 +1368,7 @@ proc genVarSection(c: PCtx; n: PNode) = if a.sons[2].kind != nkEmpty: let tmp = c.genx(a.sons[0], {gfAddrOf}) let val = c.genx(a.sons[2]) - c.gABC(a, opcWrDeref, tmp, val) + c.preventFalseAlias(a, opcWrDeref, tmp, 0, val) c.freeTemp(val) c.freeTemp(tmp) else: @@ -1306,7 +1376,16 @@ proc genVarSection(c: PCtx; n: PNode) = if a.sons[2].kind == nkEmpty: c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ)) else: - gen(c, a.sons[2], s.position.TRegister) + if not fitsRegister(s.typ): + c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ)) + let le = a.sons[0] + if not fitsRegister(le.typ) and s.kind in {skResult, skVar, skParam}: + var cc = c.getTemp(le.typ) + gen(c, a.sons[2], cc) + c.gABC(le, whichAsgnOpc(le), s.position.TRegister, cc) + c.freeTemp(cc) + else: + gen(c, a.sons[2], s.position.TRegister) else: # assign to a.sons[0]; happens for closures if a.sons[2].kind == nkEmpty: @@ -1334,7 +1413,7 @@ proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) = c.gABx(n, opcLdNullReg, tmp, c.genType(intType)) for x in n: let a = c.genx(x) - c.gABC(n, whichAsgnOpc(x, opcWrArr), dest, tmp, a) + c.preventFalseAlias(n, whichAsgnOpc(x, opcWrArr), dest, tmp, a) c.gABI(n, opcAddImmInt, tmp, tmp, 1) c.freeTemp(a) c.freeTemp(tmp) @@ -1366,7 +1445,8 @@ proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) = if it.kind == nkExprColonExpr and it.sons[0].kind == nkSym: let idx = genField(it.sons[0]) let tmp = c.genx(it.sons[1]) - c.gABC(it, whichAsgnOpc(it.sons[1], opcWrObj), dest, idx, tmp) + c.preventFalseAlias(it.sons[1], whichAsgnOpc(it.sons[1], opcWrObj), + dest, idx, tmp) c.freeTemp(tmp) else: internalError(n.info, "invalid object constructor") @@ -1380,11 +1460,12 @@ proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) = if it.kind == nkExprColonExpr: let idx = genField(it.sons[0]) let tmp = c.genx(it.sons[1]) - c.gABC(it, whichAsgnOpc(it.sons[1], opcWrObj), dest, idx, tmp) + c.preventFalseAlias(it.sons[1], whichAsgnOpc(it.sons[1], opcWrObj), + dest, idx, tmp) c.freeTemp(tmp) else: let tmp = c.genx(it) - c.gABC(it, whichAsgnOpc(it, opcWrObj), dest, i.TRegister, tmp) + c.preventFalseAlias(it, whichAsgnOpc(it, opcWrObj), dest, i.TRegister, tmp) c.freeTemp(tmp) proc genProc*(c: PCtx; s: PSym): int @@ -1622,7 +1703,7 @@ proc genProc(c: PCtx; s: PSym): int = c.gABC(body, opcEof, eofInstr.regA) c.optimizeJumps(result) s.offset = c.prc.maxSlots - #if s.name.s == "addStuff": + #if s.name.s == "calc": # echo renderTree(body) # c.echoCode(result) c.prc = oldPrc |