diff options
author | awr1 <41453959+awr1@users.noreply.github.com> | 2018-09-04 16:33:52 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-09-04 16:33:52 -0500 |
commit | eb668003bf35671d7358e5f54e05820c0f4aef3d (patch) | |
tree | e5c5d6315f8ba4a5dd647bf67a4d0afb609916e7 /compiler | |
parent | 89ad1cc9b18db8320e5b170ee45888cf79d52001 (diff) | |
parent | 4aba2981dd47672744191bd17b39bb149f494637 (diff) | |
download | Nim-eb668003bf35671d7358e5f54e05820c0f4aef3d.tar.gz |
Merge branch 'devel' into experimentalize-reorder
Diffstat (limited to 'compiler')
54 files changed, 1017 insertions, 484 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 21e129d06..694944631 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -628,6 +628,7 @@ type mIsPartOf, mAstToStr, mParallel, mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mNewString, mNewStringOfCap, mParseBiggestFloat, + mMove, mWasMoved, mReset, mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, @@ -656,7 +657,7 @@ type mNHint, mNWarning, mNError, mInstantiationInfo, mGetTypeInfo, mNimvm, mIntDefine, mStrDefine, mRunnableExamples, - mException, mBuiltinType + mException, mBuiltinType, mSymOwner # things that we can evaluate safely at compile time, even if not asked for it: const @@ -1020,13 +1021,16 @@ proc isCallExpr*(n: PNode): bool = proc discardSons*(father: PNode) proc len*(n: PNode): int {.inline.} = - if isNil(n.sons): result = 0 - else: result = len(n.sons) + when defined(nimNoNilSeqs): + result = len(n.sons) + else: + if isNil(n.sons): result = 0 + else: result = len(n.sons) proc safeLen*(n: PNode): int {.inline.} = ## works even for leaves. - if n.kind in {nkNone..nkNilLit} or isNil(n.sons): result = 0 - else: result = len(n.sons) + if n.kind in {nkNone..nkNilLit}: result = 0 + else: result = len(n) proc safeArrLen*(n: PNode): int {.inline.} = ## works for array-like objects (strings passed as openArray in VM). @@ -1036,7 +1040,8 @@ proc safeArrLen*(n: PNode): int {.inline.} = proc add*(father, son: PNode) = assert son != nil - if isNil(father.sons): father.sons = @[] + when not defined(nimNoNilSeqs): + if isNil(father.sons): father.sons = @[] add(father.sons, son) type Indexable = PNode | PType @@ -1088,9 +1093,9 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym, result.id = getID() when debugIds: registerId(result) - #if result.id == 93289: + #if result.id == 77131: # writeStacktrace() - # MessageOut(name.s & " has id: " & toString(result.id)) + # echo name.s proc isMetaType*(t: PType): bool = return t.kind in tyMetaTypes or @@ -1134,19 +1139,16 @@ const # for all kind of hash tables: proc copyStrTable*(dest: var TStrTable, src: TStrTable) = dest.counter = src.counter - if isNil(src.data): return setLen(dest.data, len(src.data)) for i in countup(0, high(src.data)): dest.data[i] = src.data[i] proc copyIdTable*(dest: var TIdTable, src: TIdTable) = dest.counter = src.counter - if isNil(src.data): return newSeq(dest.data, len(src.data)) for i in countup(0, high(src.data)): dest.data[i] = src.data[i] proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) = dest.counter = src.counter - if isNil(src.data): return setLen(dest.data, len(src.data)) for i in countup(0, high(src.data)): dest.data[i] = src.data[i] @@ -1243,7 +1245,8 @@ proc newStrNode*(strVal: string; info: TLineInfo): PNode = proc addSon*(father, son: PNode) = assert son != nil - if isNil(father.sons): father.sons = @[] + when not defined(nimNoNilSeqs): + if isNil(father.sons): father.sons = @[] add(father.sons, son) proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode, @@ -1268,14 +1271,14 @@ proc newType*(kind: TTypeKind, owner: PSym): PType = new(result) result.kind = kind result.owner = owner - result.size = - 1 + result.size = -1 result.align = 2 # default alignment result.id = getID() result.lockLevel = UnspecifiedLockLevel when debugIds: registerId(result) when false: - if result.id == 205734: + if result.id == 76426: echo "KNID ", kind writeStackTrace() @@ -1287,16 +1290,22 @@ proc mergeLoc(a: var TLoc, b: TLoc) = if a.r == nil: a.r = b.r proc newSons*(father: PNode, length: int) = - if isNil(father.sons): - newSeq(father.sons, length) - else: + when defined(nimNoNilSeqs): setLen(father.sons, length) + else: + if isNil(father.sons): + newSeq(father.sons, length) + else: + setLen(father.sons, length) proc newSons*(father: PType, length: int) = - if isNil(father.sons): - newSeq(father.sons, length) - else: + when defined(nimNoNilSeqs): setLen(father.sons, length) + else: + if isNil(father.sons): + newSeq(father.sons, length) + else: + setLen(father.sons, length) proc sonsLen*(n: PType): int = n.sons.len proc len*(n: PType): int = n.sons.len @@ -1464,20 +1473,26 @@ proc propagateToOwner*(owner, elem: PType) = owner.flags.incl tfHasGCedMem proc rawAddSon*(father, son: PType) = - if isNil(father.sons): father.sons = @[] + when not defined(nimNoNilSeqs): + if isNil(father.sons): father.sons = @[] add(father.sons, son) if not son.isNil: propagateToOwner(father, son) proc rawAddSonNoPropagationOfTypeFlags*(father, son: PType) = - if isNil(father.sons): father.sons = @[] + when not defined(nimNoNilSeqs): + if isNil(father.sons): father.sons = @[] add(father.sons, son) proc addSonNilAllowed*(father, son: PNode) = - if isNil(father.sons): father.sons = @[] + when not defined(nimNoNilSeqs): + if isNil(father.sons): father.sons = @[] add(father.sons, son) proc delSon*(father: PNode, idx: int) = - if isNil(father.sons): return + when defined(nimNoNilSeqs): + if father.len == 0: return + else: + if isNil(father.sons): return var length = sonsLen(father) for i in countup(idx, length - 2): father.sons[i] = father.sons[i + 1] setLen(father.sons, length - 1) @@ -1579,15 +1594,17 @@ proc getInt*(a: PNode): BiggestInt = case a.kind of nkCharLit..nkUInt64Lit: result = a.intVal else: + raiseRecoverableError("cannot extract number from invalid AST node") #internalError(a.info, "getInt") - doAssert false, "getInt" + #doAssert false, "getInt" #result = 0 proc getFloat*(a: PNode): BiggestFloat = case a.kind of nkFloatLiterals: result = a.floatVal else: - doAssert false, "getFloat" + raiseRecoverableError("cannot extract number from invalid AST node") + #doAssert false, "getFloat" #internalError(a.info, "getFloat") #result = 0.0 @@ -1601,7 +1618,8 @@ proc getStr*(a: PNode): string = else: result = nil else: - doAssert false, "getStr" + raiseRecoverableError("cannot extract string from invalid AST node") + #doAssert false, "getStr" #internalError(a.info, "getStr") #result = "" @@ -1610,7 +1628,8 @@ proc getStrOrChar*(a: PNode): string = of nkStrLit..nkTripleStrLit: result = a.strVal of nkCharLit..nkUInt64Lit: result = $chr(int(a.intVal)) else: - doAssert false, "getStrOrChar" + raiseRecoverableError("cannot extract string from invalid AST node") + #doAssert false, "getStrOrChar" #internalError(a.info, "getStrOrChar") #result = "" diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 152802ba1..34963ee83 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -206,7 +206,7 @@ proc makeYamlString*(s: string): Rope = const MaxLineLength = 64 result = nil var res = "\"" - for i in countup(0, if s.isNil: -1 else: (len(s)-1)): + for i in 0 ..< s.len: if (i + 1) mod MaxLineLength == 0: add(res, '\"') add(res, "\n") @@ -314,10 +314,7 @@ proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet, indent: int, addf(result, ",$N$1\"floatVal\": $2", [istr, rope(n.floatVal.toStrMaxPrecision)]) of nkStrLit..nkTripleStrLit: - if n.strVal.isNil: - addf(result, ",$N$1\"strVal\": null", [istr]) - else: - addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) + addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) of nkSym: addf(result, ",$N$1\"sym\": $2", [istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth)]) @@ -395,10 +392,7 @@ proc debugTree(conf: ConfigRef; n: PNode, indent: int, maxRecDepth: int; addf(result, ",$N$1\"floatVal\": $2", [istr, rope(n.floatVal.toStrMaxPrecision)]) of nkStrLit..nkTripleStrLit: - if n.strVal.isNil: - addf(result, ",$N$1\"strVal\": null", [istr]) - else: - addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) + addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) of nkSym: addf(result, ",$N$1\"sym\": $2_$3", [istr, rope(n.sym.name.s), rope(n.sym.id)]) @@ -759,10 +753,6 @@ proc idNodeTableGet(t: TIdNodeTable, key: PIdObj): PNode = if index >= 0: result = t.data[index].val else: result = nil -proc idNodeTableGetLazy*(t: TIdNodeTable, key: PIdObj): PNode = - if not isNil(t.data): - result = idNodeTableGet(t, key) - proc idNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) = var h: Hash h = key.id and high(data) @@ -789,10 +779,6 @@ proc idNodeTablePut(t: var TIdNodeTable, key: PIdObj, val: PNode) = idNodeTableRawInsert(t.data, key, val) inc(t.counter) -proc idNodeTablePutLazy*(t: var TIdNodeTable, key: PIdObj, val: PNode) = - if isNil(t.data): initIdNodeTable(t) - idNodeTablePut(t, key, val) - iterator pairs*(t: TIdNodeTable): tuple[key: PIdObj, val: PNode] = for i in 0 .. high(t.data): if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 2621574a6..33b07a5a7 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -124,15 +124,19 @@ proc openArrayLoc(p: BProc, n: PNode): Rope = of tyString, tySequence: if skipTypes(n.typ, abstractInst).kind == tyVar and not compileToCpp(p.module): - result = "(*$1)$3, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p), dataField(p)] + var t: TLoc + t.r = "(*$1)" % [a.rdLoc] + result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)] else: - result = "$1$3, ($1 ? $1->$2 : 0)" % [a.rdLoc, lenField(p), dataField(p)] + result = "$1$3, $2" % [a.rdLoc, lenExpr(p, a), dataField(p)] of tyArray: result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))] of tyPtr, tyRef: case lastSon(a.t).kind of tyString, tySequence: - result = "(*$1)$3, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p), dataField(p)] + var t: TLoc + t.r = "(*$1)" % [a.rdLoc] + result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)] of tyArray: result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))] else: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index b30d216f2..56ecf5ba3 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -65,13 +65,13 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = of tyString: # with the new semantics for 'nil' strings, we can map "" to nil and # save tons of allocations: - if n.strVal.len == 0 and optNilSeqs notin p.options: + if n.strVal.len == 0 and optNilSeqs notin p.options and + p.config.selectedGc != gcDestructors: result = genNilStringLiteral(p.module, n.info) else: result = genStringLiteral(p.module, n) else: - if n.strVal.isNil: result = rope("NIM_NIL") - else: result = makeCString(n.strVal) + result = makeCString(n.strVal) of nkFloatLit, nkFloat64Lit: result = rope(n.floatVal.toStrMaxPrecision) of nkFloat32Lit: @@ -165,7 +165,7 @@ proc canMove(n: PNode): bool = # result = false proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = - if dest.storage == OnStack or not usesNativeGC(p.config): + if dest.storage == OnStack or not usesWriteBarrier(p.config): linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) elif dest.storage == OnHeap: # location is on heap @@ -255,9 +255,13 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = # tfShallow flag for the built-in string type too! So we check only # here for this flag, where it is reasonably safe to do so # (for objects, etc.): - if needToCopy notin flags or + if p.config.selectedGC == gcDestructors: + linefmt(p, cpsStmts, + "$1.len = $2.len; $1.p = $2.p;$n", + rdLoc(dest), rdLoc(src)) + elif needToCopy notin flags or tfShallow in skipTypes(dest.t, abstractVarRange).flags: - if dest.storage == OnStack or not usesNativeGC(p.config): + if dest.storage == OnStack or not usesWriteBarrier(p.config): linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", addrLoc(p.config, dest), addrLoc(p.config, src), rdLoc(dest)) @@ -280,17 +284,21 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = of tyRef: genRefAssign(p, dest, src, flags) of tySequence: - if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode): + if p.config.selectedGC == gcDestructors: + genGenericAsgn(p, dest, src, flags) + elif (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode): genRefAssign(p, dest, src, flags) else: linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n", addrLoc(p.config, dest), rdLoc(src), genTypeInfo(p.module, dest.t, dest.lode.info)) of tyString: - if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode): + if p.config.selectedGC == gcDestructors: + genGenericAsgn(p, dest, src, flags) + elif (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode): genRefAssign(p, dest, src, flags) else: - if dest.storage == OnStack or not usesNativeGC(p.config): + if dest.storage == OnStack or not usesWriteBarrier(p.config): linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc) elif dest.storage == OnHeap: # we use a temporary to care for the dreaded self assignment: @@ -453,6 +461,13 @@ proc binaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) = initLocExpr(p, e.sons[2], b) lineCg(p, cpsStmts, frmt, rdLoc(a), rdLoc(b)) +proc binaryStmtAddr(p: BProc, e: PNode, d: var TLoc, frmt: string) = + var a, b: TLoc + if d.k != locNone: internalError(p.config, e.info, "binaryStmtAddr") + initLocExpr(p, e.sons[1], a) + initLocExpr(p, e.sons[2], b) + lineCg(p, cpsStmts, frmt, addrLoc(p.config, a), rdLoc(b)) + proc unaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) = var a: TLoc if d.k != locNone: internalError(p.config, e.info, "unaryStmt") @@ -889,8 +904,8 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) = of tySequence, tyString: linefmt(p, cpsStmts, "if ($2-$1 != -1 && " & - "(!$3 || (NU)($1) >= (NU)($3->$4) || (NU)($2) >= (NU)($3->$4))) #raiseIndexError();$n", - rdLoc(a), rdLoc(b), rdLoc(arr), lenField(p)) + "((NU)($1) >= (NU)$3 || (NU)($2) >= (NU)$3)) #raiseIndexError();$n", + rdLoc(a), rdLoc(b), lenExpr(p, arr)) else: discard proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = @@ -914,12 +929,12 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = if optBoundsCheck in p.options: if ty.kind == tyString and (not defined(nimNoZeroTerminator) or optLaxStrings in p.options): linefmt(p, cpsStmts, - "if (!$2 || (NU)($1) > (NU)($2->$3)) #raiseIndexError();$n", - rdLoc(b), rdLoc(a), lenField(p)) + "if ((NU)($1) > (NU)$2) #raiseIndexError();$n", + rdLoc(b), lenExpr(p, a)) else: linefmt(p, cpsStmts, - "if (!$2 || (NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n", - rdLoc(b), rdLoc(a), lenField(p)) + "if ((NU)($1) >= (NU)$2) #raiseIndexError();$n", + rdLoc(b), lenExpr(p, a)) if d.k == locNone: d.storage = OnHeap if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: a.r = ropecg(p.module, "(*$1)", a.r) @@ -1010,6 +1025,12 @@ proc genEcho(p: BProc, n: PNode) = proc gcUsage(conf: ConfigRef; n: PNode) = if conf.selectedGC == gcNone: message(conf, n.info, warnGcMem, n.renderTree) +proc strLoc(p: BProc; d: TLoc): Rope = + if p.config.selectedGc == gcDestructors: + result = addrLoc(p.config, d) + else: + result = rdLoc(d) + proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = # <Nim code> # s = 'Hello ' & name & ', how do you feel?' & 'z' @@ -1037,13 +1058,14 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, e.sons[i + 1], a) if skipTypes(e.sons[i + 1].typ, abstractVarRange).kind == tyChar: inc(L) - add(appends, ropecg(p.module, "#appendChar($1, $2);$n", tmp.r, rdLoc(a))) + add(appends, ropecg(p.module, "#appendChar($1, $2);$n", strLoc(p, tmp), rdLoc(a))) else: if e.sons[i + 1].kind in {nkStrLit..nkTripleStrLit}: inc(L, len(e.sons[i + 1].strVal)) else: - addf(lens, "($1 ? $1->$2 : 0) + ", [rdLoc(a), lenField(p)]) - add(appends, ropecg(p.module, "#appendString($1, $2);$n", tmp.r, rdLoc(a))) + add(lens, lenExpr(p, a)) + add(lens, " + ") + add(appends, ropecg(p.module, "#appendString($1, $2);$n", strLoc(p, tmp), rdLoc(a))) linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", tmp.r, lens, rope(L)) add(p.s(cpsStmts), appends) if d.k == locNone: @@ -1076,19 +1098,24 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) = if skipTypes(e.sons[i + 2].typ, abstractVarRange).kind == tyChar: inc(L) add(appends, ropecg(p.module, "#appendChar($1, $2);$n", - rdLoc(dest), rdLoc(a))) + strLoc(p, dest), rdLoc(a))) else: if e.sons[i + 2].kind in {nkStrLit..nkTripleStrLit}: inc(L, len(e.sons[i + 2].strVal)) else: - addf(lens, "($1 ? $1->$2 : 0) + ", [rdLoc(a), lenField(p)]) + add(lens, lenExpr(p, a)) + add(lens, " + ") add(appends, ropecg(p.module, "#appendString($1, $2);$n", - rdLoc(dest), rdLoc(a))) - initLoc(call, locCall, e, OnHeap) - call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, rope(L)]) - genAssignment(p, dest, call, {}) + strLoc(p, dest), rdLoc(a))) + if p.config.selectedGC == gcDestructors: + linefmt(p, cpsStmts, "#prepareAdd($1, $2$3);$n", + addrLoc(p.config, dest), lens, rope(L)) + else: + initLoc(call, locCall, e, OnHeap) + call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, rope(L)]) + genAssignment(p, dest, call, {}) + gcUsage(p.config, e) add(p.s(cpsStmts), appends) - gcUsage(p.config, e) proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = # seq &= x --> @@ -1151,7 +1178,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) = addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) let args = [getTypeDesc(p.module, typ), ti, sizeExpr] - if a.storage == OnHeap and usesNativeGC(p.config): + if a.storage == OnHeap and usesWriteBarrier(p.config): # use newObjRC1 as an optimization if canFormAcycle(a.t): linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", a.rdLoc) @@ -1182,7 +1209,7 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) = genTypeInfo(p.module, seqtype, dest.lode.info), length] var call: TLoc initLoc(call, locExpr, dest.lode, OnHeap) - if dest.storage == OnHeap and usesNativeGC(p.config): + if dest.storage == OnHeap and usesWriteBarrier(p.config): if canFormAcycle(dest.t): linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", dest.rdLoc) else: @@ -1201,10 +1228,16 @@ proc genNewSeq(p: BProc, e: PNode) = var a, b: TLoc initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) - let lenIsZero = optNilSeqs notin p.options and - e[2].kind == nkIntLit and e[2].intVal == 0 - genNewSeqAux(p, a, b.rdLoc, lenIsZero) - gcUsage(p.config, e) + if p.config.selectedGC == gcDestructors: + let seqtype = skipTypes(e.sons[1].typ, abstractVarRange) + linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n", + a.rdLoc, b.rdLoc, getTypeDesc(p.module, seqtype.lastSon), + getSeqPayloadType(p.module, seqtype)) + else: + let lenIsZero = optNilSeqs notin p.options and + e[2].kind == nkIntLit and e[2].intVal == 0 + genNewSeqAux(p, a, b.rdLoc, lenIsZero) + gcUsage(p.config, e) proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = let seqtype = skipTypes(e.typ, abstractVarRange) @@ -1448,7 +1481,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage) of tyString, tySequence: putIntoDest(p, b, e, - "$1$3, ($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p), dataField(p)], a.storage) + "$1$3, $2" % [rdLoc(a), lenExpr(p, a), dataField(p)], a.storage) of tyArray: putIntoDest(p, b, e, "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))], a.storage) @@ -1492,28 +1525,19 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = if op == mHigh: unaryExpr(p, e, d, "($1 ? (#nimCStrLen($1)-1) : -1)") else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)") of tyString: - if not p.module.compileToCpp: - if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->Sup.len-1) : -1)") - else: unaryExpr(p, e, d, "($1 ? $1->Sup.len : 0)") - else: - if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->len-1) : -1)") - else: unaryExpr(p, e, d, "($1 ? $1->len : 0)") + var a: TLoc + initLocExpr(p, e.sons[1], a) + var x = lenExpr(p, a) + if op == mHigh: x = "($1-1)" % [x] + putIntoDest(p, d, e, x) of tySequence: + # we go through a temporary here because people write bullshit code. var a, tmp: TLoc initLocExpr(p, e[1], a) getIntTemp(p, tmp) - var frmt: FormatStr - if not p.module.compileToCpp: - if op == mHigh: - frmt = "$1 = ($2 ? ($2->Sup.len-1) : -1);$n" - else: - frmt = "$1 = ($2 ? $2->Sup.len : 0);$n" - else: - if op == mHigh: - frmt = "$1 = ($2 ? ($2->len-1) : -1);$n" - else: - frmt = "$1 = ($2 ? $2->len : 0);$n" - lineCg(p, cpsStmts, frmt, tmp.r, rdLoc(a)) + var x = lenExpr(p, a) + if op == mHigh: x = "($1-1)" % [x] + lineCg(p, cpsStmts, "$1 = $2;$n", tmp.r, x) putIntoDest(p, d, e, tmp.r) of tyArray: # YYY: length(sideeffect) is optimized away incorrectly? @@ -1522,6 +1546,9 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: internalError(p.config, e.info, "genArrayLen()") proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = + if p.config.selectedGc == gcDestructors: + genCall(p, e, d) + return var a, b, call: TLoc assert(d.k == locNone) var x = e.sons[1] @@ -1542,16 +1569,19 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = gcUsage(p.config, e) proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) = - var a, b, call: TLoc - if d.k != locNone: internalError(p.config, e.info, "genSetLengthStr") - initLocExpr(p, e.sons[1], a) - initLocExpr(p, e.sons[2], b) + if p.config.selectedGc == gcDestructors: + binaryStmtAddr(p, e, d, "#setLengthStrV2($1, $2);$n") + else: + var a, b, call: TLoc + if d.k != locNone: internalError(p.config, e.info, "genSetLengthStr") + initLocExpr(p, e.sons[1], a) + initLocExpr(p, e.sons[2], b) - initLoc(call, locCall, e, OnHeap) - call.r = ropecg(p.module, "#setLengthStr($1, $2)", [ - rdLoc(a), rdLoc(b)]) - genAssignment(p, a, call, {}) - gcUsage(p.config, e) + initLoc(call, locCall, e, OnHeap) + call.r = ropecg(p.module, "#setLengthStr($1, $2)", [ + rdLoc(a), rdLoc(b)]) + genAssignment(p, a, call, {}) + gcUsage(p.config, e) proc genSwap(p: BProc, e: PNode, d: var TLoc) = # swap(a, b) --> @@ -1803,11 +1833,11 @@ proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = if a.kind in {nkStrLit..nkTripleStrLit} and a.strVal == "": initLocExpr(p, e.sons[2], x) putIntoDest(p, d, e, - ropecg(p.module, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p))) + ropecg(p.module, "($1 == 0)", lenExpr(p, x))) elif b.kind in {nkStrLit..nkTripleStrLit} and b.strVal == "": initLocExpr(p, e.sons[1], x) putIntoDest(p, d, e, - ropecg(p.module, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p))) + ropecg(p.module, "($1 == 0)", lenExpr(p, x))) else: binaryExpr(p, e, d, "#eqStrings($1, $2)") @@ -1868,14 +1898,21 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mConStrStr: genStrConcat(p, e, d) of mAppendStrCh: - var dest, b, call: TLoc - initLoc(call, locCall, e, OnHeap) - initLocExpr(p, e.sons[1], dest) - initLocExpr(p, e.sons[2], b) - call.r = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)]) - genAssignment(p, dest, call, {}) + if p.config.selectedGC == gcDestructors: + binaryStmtAddr(p, e, d, "#nimAddCharV1($1, $2);$n") + else: + var dest, b, call: TLoc + initLoc(call, locCall, e, OnHeap) + initLocExpr(p, e.sons[1], dest) + initLocExpr(p, e.sons[2], b) + call.r = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)]) + genAssignment(p, dest, call, {}) of mAppendStrStr: genStrAppend(p, e, d) - of mAppendSeqElem: genSeqElemAppend(p, e, d) + of mAppendSeqElem: + if p.config.selectedGc == gcDestructors: + genCall(p, e, d) + else: + genSeqElemAppend(p, e, d) of mEqStr: genStrEquals(p, e, d) of mLeStr: binaryExpr(p, e, d, "(#cmpStrings($1, $2) <= 0)") of mLtStr: binaryExpr(p, e, d, "(#cmpStrings($1, $2) < 0)") @@ -1924,8 +1961,9 @@ 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, - mParseBiggestFloat: + of mCopyStr, mCopyStrLast: + genCall(p, e, d) + of mNewString, mNewStringOfCap, mExit, mParseBiggestFloat: var opr = e.sons[0].sym if lfNoDecl notin opr.loc.flags: discard cgsym(p.module, $opr.loc.r) @@ -2509,7 +2547,7 @@ proc genConstExpr(p: BProc, n: PNode): Rope = var t = skipTypes(n.typ, abstractInst) if t.kind == tySequence: result = genConstSeq(p, n, n.typ) - elif t.kind == tyProc and t.callConv == ccClosure and not n.sons.isNil and + elif t.kind == tyProc and t.callConv == ccClosure and n.len > 0 and n.sons[0].kind == nkNilLit and n.sons[1].kind == nkNilLit: # this hack fixes issue that nkNilLit is expanded to {NIM_NIL,NIM_NIL} # this behaviour is needed since closure_var = nil must be @@ -2522,6 +2560,13 @@ proc genConstExpr(p: BProc, n: PNode): Rope = result = genConstSimpleList(p, n) of nkObjConstr: result = genConstObjConstr(p, n) + of nkStrLit..nkTripleStrLit: + if p.config.selectedGc == gcDestructors: + result = genStringLiteralV2Const(p.module, n) + else: + var d: TLoc + initLocExpr(p, n, d) + result = rdLoc(d) else: var d: TLoc initLocExpr(p, n, d) diff --git a/compiler/ccgliterals.nim b/compiler/ccgliterals.nim index cfe71375e..34677ec06 100644 --- a/compiler/ccgliterals.nim +++ b/compiler/ccgliterals.nim @@ -53,20 +53,36 @@ proc genStringLiteralV1(m: BModule; n: PNode): Rope = proc genStringLiteralDataOnlyV2(m: BModule, s: string): Rope = result = getTempName(m) - addf(m.s[cfsData], " static const NIM_CHAR $1[$2] = $3;$n", - [result, rope(len(s)+1), makeCString(s)]) + addf(m.s[cfsData], "static const struct {$n" & + " NI cap; void* allocator; NIM_CHAR data[$2];$n" & + "} $1 = { $2, NIM_NIL, $3 };$n", + [result, rope(len(s)), makeCString(s)]) proc genStringLiteralV2(m: BModule; n: PNode): Rope = let id = nodeTableTestOrSet(m.dataCache, n, m.labels) if id == m.labels: + discard cgsym(m, "NimStrPayload") + discard cgsym(m, "NimStringV2") # string literal not found in the cache: let pureLit = genStringLiteralDataOnlyV2(m, n.strVal) result = getTempName(m) - addf(m.s[cfsData], "static const #NimStringV2 $1 = {$2, $2, $3};$n", - [result, rope(len(n.strVal)+1), pureLit]) + addf(m.s[cfsData], "static const NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n", + [result, rope(len(n.strVal)), pureLit]) else: result = m.tmpBase & rope(id) +proc genStringLiteralV2Const(m: BModule; n: PNode): Rope = + let id = nodeTableTestOrSet(m.dataCache, n, m.labels) + var pureLit: Rope + if id == m.labels: + discard cgsym(m, "NimStrPayload") + discard cgsym(m, "NimStringV2") + # string literal not found in the cache: + pureLit = genStringLiteralDataOnlyV2(m, n.strVal) + else: + pureLit = m.tmpBase & rope(id) + result = "{$1, (NimStrPayload*)&$2}" % [rope(len(n.strVal)), pureLit] + # ------ Version selector --------------------------------------------------- proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo): Rope = diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index a7a2b3fee..69e6558bb 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -16,7 +16,7 @@ const # above X strings a hash-switch for strings is generated proc registerGcRoot(p: BProc, v: PSym) = - if p.config.selectedGC in {gcMarkAndSweep, gcGenerational, gcV2, gcRefc} and + if p.config.selectedGC in {gcMarkAndSweep, gcDestructors, gcV2, gcRefc} and containsGarbageCollectedRef(v.loc.t): # we register a specialized marked proc here; this has the advantage # that it works out of the box for thread local storage then :-) diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index 349cf2707..c69bb2c80 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -7,8 +7,7 @@ # distribution, for details about the copyright. # -## Generates traversal procs for the C backend. Traversal procs are only an -## optimization; the GC works without them too. +## Generates traversal procs for the C backend. # included from cgen.nim @@ -61,6 +60,7 @@ proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} = else: result = accessor +proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) = if typ == nil: return @@ -93,8 +93,18 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) = let typ = getUniqueType(typ) for i in countup(0, sonsLen(typ) - 1): genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", accessor, i.rope), typ.sons[i]) - of tyRef, tyString, tySequence: + of tyRef: lineCg(p, cpsStmts, c.visitorFrmt, accessor) + of tySequence: + if tfHasAsgn notin typ.flags: + lineCg(p, cpsStmts, c.visitorFrmt, accessor) + elif containsGarbageCollectedRef(typ.lastSon): + # destructor based seqs are themselves not traced but their data is, if + # they contain a GC'ed type: + genTraverseProcSeq(c, accessor, typ) + of tyString: + if tfHasAsgn notin typ.flags: + lineCg(p, cpsStmts, c.visitorFrmt, accessor) of tyProc: if typ.callConv == ccClosure: lineCg(p, cpsStmts, c.visitorFrmt, ropecg(c.p.module, "$1.ClE_0", accessor)) @@ -107,8 +117,11 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) = var i: TLoc getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo(), tyInt), i) let oldCode = p.s(cpsStmts) - lineF(p, cpsStmts, "for ($1 = 0; $1 < ($2 ? $2->$3 : 0); $1++) {$n", - [i.r, accessor, lenField(c.p)]) + var a: TLoc + a.r = accessor + + lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", + [i.r, lenExpr(c.p, a)]) let oldLen = p.s(cpsStmts).len genTraverseProc(c, "$1$3[$2]" % [accessor, i.r, dataField(c.p)], typ.sons[0]) if p.s(cpsStmts).len == oldLen: diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index a16255f6e..dd79f4846 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -282,7 +282,7 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope = of tyString: case detectStrVersion(m) of 2: - discard cgsym(m, "string") + discard cgsym(m, "NimStringV2") result = typeNameOrLiteral(m, typ, "NimStringV2") else: discard cgsym(m, "NimStringDesc") @@ -324,6 +324,10 @@ proc getForwardStructFormat(m: BModule): string = if m.compileToCpp: result = "$1 $2;$n" else: result = "typedef $1 $2 $2;$n" +proc seqStar(m: BModule): string = + if m.config.selectedGC == gcDestructors: result = "" + else: result = "*" + proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope = result = cacheGetType(m.forwTypeCache, sig) if result != nil: return @@ -355,8 +359,11 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope = result = getTypeForward(m, t, hashType(t)) pushType(m, t) of tySequence: - result = getTypeForward(m, t, hashType(t)) & "*" - pushType(m, t) + if m.config.selectedGC == gcDestructors: + result = getTypeDescAux(m, t, check) + else: + result = getTypeForward(m, t, hashType(t)) & seqStar(m) + pushType(m, t) else: result = getTypeDescAux(m, t, check) @@ -442,7 +449,9 @@ proc genRecordFieldsAux(m: BModule, n: PNode, of nkRecCase: if n.sons[0].kind != nkSym: internalError(m.config, n.info, "genRecordFieldsAux") add(result, genRecordFieldsAux(m, n.sons[0], accessExpr, rectype, check)) - let uname = rope(mangle(n.sons[0].sym.name.s) & 'U') + # prefix mangled name with "_U" to avoid clashes with other field names, + # since identifiers are not allowed to start with '_' + let uname = rope("_U" & mangle(n.sons[0].sym.name.s)) let ae = if accessExpr != nil: "$1.$2" % [accessExpr, uname] else: uname var unionBody: Rope = nil @@ -487,7 +496,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode, if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags: addf(result, "$1 $2[SEQ_DECL_SIZE];$n", [getTypeDescAux(m, fieldType.elemType, check), sname]) - elif fieldType.kind in {tySequence, tyOpt}: + elif fieldType.kind == tySequence and m.config.selectedGC != gcDestructors: # we need to use a weak dependency here for trecursive_table. addf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname]) elif field.bitsize != 0: @@ -601,6 +610,15 @@ proc resolveStarsInCppType(typ: PType, idx, stars: int): PType = result = if result.kind == tyGenericInst: result.sons[1] else: result.elemType +proc getSeqPayloadType(m: BModule; t: PType): Rope = + result = getTypeForward(m, t, hashType(t)) & "_Content" + when false: + var check = initIntSet() + # XXX remove this duplication: + appcg(m, m.s[cfsSeqTypes], + "struct $2_Content { NI cap; void* allocator; $1 data[SEQ_DECL_SIZE]; };$n", + [getTypeDescAux(m, t.sons[0], check), result]) + proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = # returns only the type's name var t = origTyp.skipTypes(irrelevantForBackend) @@ -641,7 +659,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = of tySequence: # no restriction! We have a forward declaration for structs let name = getTypeForward(m, et, hashType et) - result = name & "*" & star + result = name & seqStar(m) & star m.typeCache[sig] = result pushType(m, et) else: @@ -705,20 +723,29 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = [structOrUnion(t), result]) m.forwTypeCache[sig] = result assert(cacheGetType(m.typeCache, sig) == nil) - m.typeCache[sig] = result & "*" + m.typeCache[sig] = result & seqStar(m) if not isImportedType(t): if skipTypes(t.sons[0], typedescInst).kind != tyEmpty: const cppSeq = "struct $2 : #TGenericSeq {$n" cSeq = "struct $2 {$n" & " #TGenericSeq Sup;$n" - appcg(m, m.s[cfsSeqTypes], - (if m.compileToCpp: cppSeq else: cSeq) & - " $1 data[SEQ_DECL_SIZE];$n" & + if m.config.selectedGC == gcDestructors: + appcg(m, m.s[cfsTypes], + "typedef struct{ NI cap;void* allocator;$1 data[SEQ_DECL_SIZE];}$2_Content;$n" & + "struct $2 {$n" & + " NI len; $2_Content* p;$n" & "};$n", [getTypeDescAux(m, t.sons[0], check), result]) + else: + appcg(m, m.s[cfsSeqTypes], + (if m.compileToCpp: cppSeq else: cSeq) & + " $1 data[SEQ_DECL_SIZE];$n" & + "};$n", [getTypeDescAux(m, t.sons[0], check), result]) + elif m.config.selectedGC == gcDestructors: + internalError(m.config, "cannot map the empty seq type to a C type") else: result = rope("TGenericSeq") - add(result, "*") + add(result, seqStar(m)) of tyArray: var n: BiggestInt = lengthOrd(m.config, t) if n <= 0: n = 1 # make an array of at least one element @@ -1177,7 +1204,13 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope = else: let x = fakeClosureType(m, t.owner) genTupleInfo(m, x, x, result, info) - of tySequence, tyRef, tyOptAsRef: + of tySequence: + if tfHasAsgn notin t.flags: + genTypeInfoAux(m, t, t, result, info) + if m.config.selectedGC >= gcMarkAndSweep: + let markerProc = genTraverseProc(m, origType, sig) + addf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [result, markerProc]) + of tyRef, tyOptAsRef: genTypeInfoAux(m, t, t, result, info) if m.config.selectedGC >= gcMarkAndSweep: let markerProc = genTraverseProc(m, origType, sig) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 01a930de6..dea8b1e8a 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -230,22 +230,31 @@ proc getTempName(m: BModule): Rope = result = m.tmpBase & rope(m.labels) inc m.labels +proc rdLoc(a: TLoc): Rope = + # 'read' location (deref if indirect) + result = a.r + if lfIndirect in a.flags: result = "(*$1)" % [result] + proc lenField(p: BProc): Rope = result = rope(if p.module.compileToCpp: "len" else: "Sup.len") +proc lenExpr(p: BProc; a: TLoc): Rope = + if p.config.selectedGc == gcDestructors: + result = rdLoc(a) & ".len" + else: + result = "($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)] + proc dataField(p: BProc): Rope = - result = rope"->data" + if p.config.selectedGc == gcDestructors: + result = rope".p->data" + else: + result = rope"->data" include ccgliterals include ccgtypes # ------------------------------ Manager of temporaries ------------------ -proc rdLoc(a: TLoc): Rope = - # 'read' location (deref if indirect) - result = a.r - if lfIndirect in a.flags: result = "(*$1)" % [result] - proc addrLoc(conf: ConfigRef; a: TLoc): Rope = result = a.r if lfIndirect notin a.flags and mapType(conf, a.t) != ctArray: @@ -325,7 +334,9 @@ proc resetLoc(p: BProc, loc: var TLoc) = proc constructLoc(p: BProc, loc: TLoc, isTemp = false) = let typ = loc.t - if not isComplexValueType(typ): + if p.config.selectedGc == gcDestructors and skipTypes(typ, abstractInst).kind in {tyString, tySequence}: + linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", rdLoc(loc)) + elif not isComplexValueType(typ): linefmt(p, cpsStmts, "$1 = ($2)0;$n", rdLoc(loc), getTypeDesc(p.module, typ)) else: @@ -694,9 +705,10 @@ proc containsResult(n: PNode): bool = for i in 0..<n.safeLen: if containsResult(n[i]): return true +const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt} + + declarativeDefs + proc easyResultAsgn(n: PNode): PNode = - const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt} + - declarativeDefs case n.kind of nkStmtList, nkStmtListExpr: var i = 0 @@ -712,6 +724,105 @@ proc easyResultAsgn(n: PNode): PNode = if result != nil: incl n.flags, nfPreventCg else: discard +type + InitResultEnum = enum Unknown, InitSkippable, InitRequired + +proc allPathsAsgnResult(n: PNode): InitResultEnum = + # Exceptions coming from calls don't have not be considered here: + # + # proc bar(): string = raise newException(...) + # + # proc foo(): string = + # # optimized out: 'reset(result)' + # result = bar() + # + # try: + # a = foo() + # except: + # echo "a was not written to" + # + template allPathsInBranch(it) = + let a = allPathsAsgnResult(it) + case a + of InitRequired: return InitRequired + of InitSkippable: discard + of Unknown: + # sticky, but can be overwritten by InitRequired: + result = Unknown + + result = Unknown + case n.kind + of nkStmtList, nkStmtListExpr: + for it in n: + result = allPathsAsgnResult(it) + if result != Unknown: return result + of nkAsgn, nkFastAsgn: + if n[0].kind == nkSym and n[0].sym.kind == skResult: + if not containsResult(n[1]): result = InitSkippable + else: result = InitRequired + elif containsResult(n): + result = InitRequired + of nkReturnStmt: + if n.len > 0: + result = allPathsAsgnResult(n[0]) + of nkIfStmt, nkIfExpr: + var exhaustive = false + result = InitSkippable + for it in n: + # Every condition must not use 'result': + if it.len == 2 and containsResult(it[0]): + return InitRequired + if it.len == 1: exhaustive = true + allPathsInBranch(it.lastSon) + # if the 'if' statement is not exhaustive and yet it touched 'result' + # in some way, say Unknown. + if not exhaustive: result = Unknown + of nkCaseStmt: + if containsResult(n[0]): return InitRequired + result = InitSkippable + var exhaustive = skipTypes(n[0].typ, + abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString} + for i in 1..<n.len: + let it = n[i] + allPathsInBranch(it.lastSon) + if it.kind == nkElse: exhaustive = true + if not exhaustive: result = Unknown + of nkWhileStmt: + # some dubious code can assign the result in the 'while' + # condition and that would be fine. Everything else isn't: + result = allPathsAsgnResult(n[0]) + if result == Unknown: + result = allPathsAsgnResult(n[1]) + # we cannot assume that the 'while' loop is really executed at least once: + if result == InitSkippable: result = Unknown + of harmless: + result = Unknown + of nkGotoState, nkBreakState: + # give up for now. + result = InitRequired + of nkSym: + # some path reads from 'result' before it was written to! + if n.sym.kind == skResult: result = InitRequired + of nkTryStmt: + # We need to watch out for the following problem: + # try: + # result = stuffThatRaises() + # except: + # discard "result was not set" + # + # So ... even if the assignment to 'result' is the very first + # assignment this is not good enough! The only pattern we allow for + # is 'finally: result = x' + result = InitSkippable + for it in n: + if it.kind == nkFinally: + result = allPathsAsgnResult(it.lastSon) + else: + allPathsInBranch(it.lastSon) + else: + for i in 0..<safeLen(n): + allPathsInBranch(n[i]) + proc genProcAux(m: BModule, prc: PSym) = var p = newProc(prc, m) var header = genProcHeader(m, prc) @@ -738,7 +849,16 @@ proc genProcAux(m: BModule, prc: PSym) = else: fillResult(p.config, resNode) assignParam(p, res) - if sfNoInit notin prc.flags: resetLoc(p, res.loc) + # We simplify 'unsureAsgn(result, nil); unsureAsgn(result, x)' + # to 'unsureAsgn(result, x)' + # Sketch why this is correct: If 'result' points to a stack location + # the 'unsureAsgn' is a nop. If it points to a global variable the + # global is either 'nil' or points to valid memory and so the RC operation + # succeeds without touching not-initialized memory. + if sfNoInit in prc.flags: discard + elif allPathsAsgnResult(prc.getBody) == InitSkippable: discard + else: + resetLoc(p, res.loc) if skipTypes(res.typ, abstractInst).kind == tyArray: #incl(res.loc.flags, lfIndirect) res.loc.storage = OnUnknown diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index 9e4885b66..e3e9c2236 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -239,7 +239,7 @@ proc toStmtList(n: PNode): PNode = proc addGotoOut(n: PNode, gotoOut: PNode): PNode = # Make sure `n` is a stmtlist, and ends with `gotoOut` result = toStmtList(n) - if result.len != 0 and result.sons[^1].kind != nkGotoState: + if result.len == 0 or result.sons[^1].kind != nkGotoState: result.add(gotoOut) proc newTempVar(ctx: var Ctx, typ: PType): PSym = @@ -678,7 +678,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = n[0] = ex result.add(n) - of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv: + of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv, nkObjDownConv: var ns = false for i in 0 ..< n.len: n[i] = ctx.lowerStmtListExprs(n[i], ns) @@ -687,9 +687,9 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = needsSplit = true result = newNodeI(nkStmtListExpr, n.info) result.typ = n.typ - let (st, ex) = exprToStmtList(n[1]) + let (st, ex) = exprToStmtList(n[^1]) result.add(st) - n[1] = ex + n[^1] = ex result.add(n) of nkAsgn, nkFastAsgn: diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim new file mode 100644 index 000000000..9d2334af5 --- /dev/null +++ b/compiler/cmdlinehelper.nim @@ -0,0 +1,85 @@ +## Helpers for binaries that use compiler passes, eg: nim, nimsuggest, nimfix + +# TODO: nimfix should use this; currently out of sync + +import + compiler/[options, idents, nimconf, scriptconfig, extccomp, commands, msgs, lineinfos, modulegraphs, condsyms], + std/os + +type + NimProg* = ref object + suggestMode*: bool + supportsStdinFile*: bool + processCmdLine*: proc(pass: TCmdLinePass, cmd: string; config: ConfigRef) + mainCommand*: proc(graph: ModuleGraph) + +proc initDefinesProg*(self: NimProg, conf: ConfigRef, name: string) = + condsyms.initDefines(conf.symbols) + defineSymbol conf.symbols, name + +proc processCmdLineAndProjectPath*(self: NimProg, conf: ConfigRef) = + self.processCmdLine(passCmd1, "", conf) + if self.supportsStdinFile and conf.projectName == "-": + conf.projectName = "stdinfile" + conf.projectFull = "stdinfile" + conf.projectPath = canonicalizePath(conf, getCurrentDir()) + conf.projectIsStdin = true + elif conf.projectName != "": + try: + conf.projectFull = canonicalizePath(conf, conf.projectName) + except OSError: + conf.projectFull = conf.projectName + let p = splitFile(conf.projectFull) + let dir = if p.dir.len > 0: p.dir else: getCurrentDir() + conf.projectPath = canonicalizePath(conf, dir) + conf.projectName = p.name + else: + conf.projectPath = canonicalizePath(conf, getCurrentDir()) + +proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: ConfigRef): bool = + loadConfigs(DefaultConfig, cache, conf) # load all config files + if self.suggestMode: + conf.command = "nimsuggest" + + proc runNimScriptIfExists(path: string)= + if fileExists(path): + runNimScript(cache, path, freshDefines = false, conf) + + # Caution: make sure this stays in sync with `loadConfigs` + if optSkipSystemConfigFile notin conf.globalOptions: + runNimScriptIfExists(getSystemConfigPath(conf, DefaultConfigNims)) + + if optSkipUserConfigFile notin conf.globalOptions: + runNimScriptIfExists(getUserConfigPath(DefaultConfigNims)) + + if optSkipParentConfigFiles notin conf.globalOptions: + for dir in parentDirs(conf.projectPath, fromRoot = true, inclusive = false): + runNimScriptIfExists(dir / DefaultConfigNims) + + if optSkipProjConfigFile notin conf.globalOptions: + runNimScriptIfExists(conf.projectPath / DefaultConfigNims) + block: + let scriptFile = conf.projectFull.changeFileExt("nims") + if not self.suggestMode: + runNimScriptIfExists(scriptFile) + # 'nim foo.nims' means to just run the NimScript file and do nothing more: + if fileExists(scriptFile) and scriptFile.cmpPaths(conf.projectFull) == 0: + return false + else: + if scriptFile.cmpPaths(conf.projectFull) != 0: + runNimScriptIfExists(scriptFile) + else: + # 'nimsuggest foo.nims' means to just auto-complete the NimScript file + discard + + # now process command line arguments again, because some options in the + # command line can overwite the config file's settings + extccomp.initVars(conf) + self.processCmdLine(passCmd2, "", conf) + if conf.command == "": + rawMessage(conf, errGenerated, "command missing") + + let graph = newModuleGraph(cache, conf) + graph.suggestMode = self.suggestMode + self.mainCommand(graph) + return true diff --git a/compiler/commands.nim b/compiler/commands.nim index 1e5384f16..b47ccf610 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -131,7 +131,10 @@ proc splitSwitch(conf: ConfigRef; switch: string, cmd, arg: var string, pass: TC else: break inc(i) if i >= len(switch): arg = "" - elif switch[i] in {':', '=', '['}: arg = substr(switch, i + 1) + # cmd:arg => (cmd,arg) + elif switch[i] in {':', '='}: arg = substr(switch, i + 1) + # cmd[sub]:rest => (cmd,[sub]:rest) + elif switch[i] == '[': arg = substr(switch, i) else: invalidCmdLineOption(conf, pass, switch, info) proc processOnOffSwitch(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass, @@ -167,14 +170,20 @@ proc expectNoArg(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass, info: TLineInfo; orig: string; conf: ConfigRef) = - var id = "" # arg = "X]:on|off" + var id = "" # arg = key:val or [key]:val; with val=on|off var i = 0 var n = hintMin - while i < len(arg) and (arg[i] != ']'): + var isBracket = false + if i < len(arg) and arg[i] == '[': + isBracket = true + inc(i) + while i < len(arg) and (arg[i] notin {':', '=', ']'}): add(id, arg[i]) inc(i) - if i < len(arg) and (arg[i] == ']'): inc(i) - else: invalidCmdLineOption(conf, pass, orig, info) + if isBracket: + if i < len(arg) and arg[i] == ']': inc(i) + else: invalidCmdLineOption(conf, pass, orig, info) + if i < len(arg) and (arg[i] in {':', '='}): inc(i) else: invalidCmdLineOption(conf, pass, orig, info) if state == wHint: @@ -215,7 +224,8 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo of "refc": result = conf.selectedGC == gcRefc of "v2": result = conf.selectedGC == gcV2 of "markandsweep": result = conf.selectedGC == gcMarkAndSweep - of "generational": result = conf.selectedGC == gcGenerational + of "generational": result = false + of "destructors": result = conf.selectedGC == gcDestructors of "go": result = conf.selectedGC == gcGo of "none": result = conf.selectedGC == gcNone of "stack", "regions": result = conf.selectedGC == gcRegions @@ -436,9 +446,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "markandsweep": conf.selectedGC = gcMarkAndSweep defineSymbol(conf.symbols, "gcmarkandsweep") - of "generational": - conf.selectedGC = gcGenerational - defineSymbol(conf.symbols, "gcgenerational") + of "destructors": + conf.selectedGC = gcDestructors + defineSymbol(conf.symbols, "gcdestructors") of "go": conf.selectedGC = gcGo defineSymbol(conf.symbols, "gogc") @@ -641,7 +651,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; else: localError(conf, info, "invalid option for --symbolFiles: " & arg) of "skipcfg": expectNoArg(conf, switch, arg, pass, info) - incl(conf.globalOptions, optSkipConfigFile) + incl(conf.globalOptions, optSkipSystemConfigFile) of "skipprojcfg": expectNoArg(conf, switch, arg, pass, info) incl(conf.globalOptions, optSkipProjConfigFile) @@ -742,11 +752,11 @@ proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef) = proc processSwitch*(pass: TCmdLinePass; p: OptParser; config: ConfigRef) = # hint[X]:off is parsed as (p.key = "hint[X]", p.val = "off") - # we fix this here + # we transform it to (key = hint, val = [X]:off) var bracketLe = strutils.find(p.key, '[') if bracketLe >= 0: var key = substr(p.key, 0, bracketLe - 1) - var val = substr(p.key, bracketLe + 1) & ':' & p.val + var val = substr(p.key, bracketLe) & ':' & p.val processSwitch(key, val, pass, gCmdLineInfo, config) else: processSwitch(p.key, p.val, pass, gCmdLineInfo, config) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index ba1c42a74..a22b613f0 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -13,6 +13,7 @@ import strtabs, platform, strutils, idents from options import Feature +from lineinfos import HintsToStr, WarningsToStr const catNone = "false" @@ -74,6 +75,8 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimNoZeroTerminator") defineSymbol("nimNotNil") defineSymbol("nimVmExportFixed") + defineSymbol("nimHasSymOwnerInMacro") + defineSymbol("nimNewRuntime") defineSymbol("nimIncrSeqV3") defineSymbol("nimAshr") defineSymbol("nimNoNilSeqs") @@ -82,3 +85,8 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasNilSeqs") for f in low(Feature)..high(Feature): defineSymbol("nimHas" & $f) + + for s in WarningsToStr: + defineSymbol("nimHasWarning" & s) + for s in HintsToStr: + defineSymbol("nimHasHint" & s) diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index 0395728c2..bd735560a 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -100,12 +100,12 @@ Rule Pattern Transformed into finally: `=destroy`(x) 1.2 var x: sink T; stmts var x: sink T; stmts; ensureEmpty(x) 2 x = f() `=sink`(x, f()) -3 x = lastReadOf z `=sink`(x, z) +3 x = lastReadOf z `=sink`(x, z); wasMoved(z) 4.1 y = sinkParam `=sink`(y, sinkParam) 4.2 x = y `=`(x, y) # a copy 5.1 f_sink(g()) f_sink(g()) 5.2 f_sink(y) f_sink(copy y); # copy unless we can see it's the last read -5.3 f_sink(move y) f_sink(y); reset(y) # explicit moves empties 'y' +5.3 f_sink(move y) f_sink(y); wasMoved(y) # explicit moves empties 'y' 5.4 f_noSink(g()) var tmp = bitwiseCopy(g()); f(tmp); `=destroy`(tmp) Remarks: Rule 1.2 is not yet implemented because ``sink`` is currently @@ -258,8 +258,10 @@ proc registerDropBit(c: var Con; s: PSym) = c.toDropBit[s.id] = result # generate: # if not sinkParam_AliveBit: `=destroy`(sinkParam) - c.destroys.add newTree(nkIfStmt, - newTree(nkElifBranch, newSymNode result, genDestroy(c, s.typ, newSymNode s))) + let t = s.typ.skipTypes({tyGenericInst, tyAlias, tySink}) + if t.destructor != nil: + c.destroys.add newTree(nkIfStmt, + newTree(nkElifBranch, newSymNode result, genDestroy(c, t, newSymNode s))) proc p(n: PNode; c: var Con): PNode @@ -282,6 +284,11 @@ proc destructiveMoveSink(n: PNode; c: var Con): PNode = newIntTypeNode(nkIntLit, 0, getSysType(c.graph, n.info, tyBool))) result.add n +proc genMagicCall(n: PNode; c: var Con; magicname: string; m: TMagic): PNode = + result = newNodeI(nkCall, n.info) + result.add(newSymNode(createMagic(c.graph, magicname, m))) + result.add n + proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = if ri.kind in constrExprs: result = genSink(c, ri.typ, dest) @@ -290,8 +297,10 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = recurse(ri, ri2) result.add ri2 elif ri.kind == nkSym and isHarmlessVar(ri.sym, c): - result = genSink(c, ri.typ, dest) - result.add p(ri, c) + # Rule 3: `=sink`(x, z); wasMoved(z) + var snk = genSink(c, ri.typ, dest) + snk.add p(ri, c) + result = newTree(nkStmtList, snk, genMagicCall(ri, c, "wasMoved", mWasMoved)) elif ri.kind == nkSym and isSinkParam(ri.sym): result = genSink(c, ri.typ, dest) result.add destructiveMoveSink(ri, c) @@ -313,11 +322,9 @@ proc passCopyToSink(n: PNode; c: var Con): PNode = result.add newTree(nkAsgn, tmp, p(n, c)) result.add tmp -proc genReset(n: PNode; c: var Con): PNode = - result = newNodeI(nkCall, n.info) - result.add(newSymNode(createMagic(c.graph, "reset", mReset))) - # The mReset builtin does not take the address: - result.add n +proc genWasMoved(n: PNode; c: var Con): PNode = + # The mWasMoved builtin does not take the address. + result = genMagicCall(n, c, "wasMoved", mWasMoved) proc destructiveMoveVar(n: PNode; c: var Con): PNode = # generate: (let tmp = v; reset(v); tmp) @@ -334,7 +341,7 @@ proc destructiveMoveVar(n: PNode; c: var Con): PNode = add(v, vpart) result.add v - result.add genReset(n, c) + result.add genWasMoved(n, c) result.add tempAsNode proc p(n: PNode; c: var Con): PNode = diff --git a/compiler/docgen.nim b/compiler/docgen.nim index b35452365..23d156e05 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -14,7 +14,7 @@ import ast, strutils, strtabs, options, msgs, os, ropes, idents, wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast, - packages/docutils/rst, packages/docutils/rstgen, times, + packages/docutils/rst, packages/docutils/rstgen, packages/docutils/highlite, sempass2, json, xmltree, cgi, typesrenderer, astalgo, modulepaths, lineinfos, sequtils @@ -31,6 +31,7 @@ type isPureRst: bool conf*: ConfigRef cache*: IdentCache + exampleCounter: int PDoc* = ref TDocumentor ## Alias to type less. @@ -284,11 +285,56 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe dispA(d.conf, result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}", [rope(esc(d.target, literal))]) +proc testExample(d: PDoc; ex: PNode) = + if d.conf.errorCounter > 0: return + let outputDir = d.conf.getNimcacheDir / "runnableExamples" + createDir(outputDir) + inc d.exampleCounter + let outp = outputDir / extractFilename(d.filename.changeFileExt"" & + "_examples" & $d.exampleCounter & ".nim") + #let nimcache = outp.changeFileExt"" & "_nimcache" + renderModule(ex, d.filename, outp, conf = d.conf) + let backend = if isDefined(d.conf, "js"): "js" + elif isDefined(d.conf, "cpp"): "cpp" + elif isDefined(d.conf, "objc"): "objc" + else: "c" + if os.execShellCmd(os.getAppFilename() & " " & backend & + " --nimcache:" & outputDir & " -r " & outp) != 0: + quit "[Examples] failed: see " & outp + else: + # keep generated source file `outp` to allow inspection. + rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp]) + removeFile(outp.changeFileExt(ExeExt)) + +proc extractImports(n: PNode; result: PNode) = + if n.kind in {nkImportStmt, nkImportExceptStmt, nkFromStmt}: + result.add copyTree(n) + n.kind = nkEmpty + return + for i in 0..<n.safeLen: extractImports(n[i], result) + +proc prepareExamples(d: PDoc; n: PNode) = + var runnableExamples = newTree(nkStmtList, + newTree(nkImportStmt, newStrNode(nkStrLit, d.filename))) + runnableExamples.info = n.info + let imports = newTree(nkStmtList) + var savedLastSon = copyTree n.lastSon + extractImports(savedLastSon, imports) + for imp in imports: runnableExamples.add imp + runnableExamples.add newTree(nkBlockStmt, newNode(nkEmpty), copyTree savedLastSon) + testExample(d, runnableExamples) + +proc isRunnableExample(n: PNode): bool = + # Templates and generics don't perform symbol lookups. + result = n.kind == nkSym and n.sym.magic == mRunnableExamples or + n.kind == nkIdent and n.ident.s == "runnableExamples" + proc getAllRunnableExamples(d: PDoc; n: PNode; dest: var Rope) = case n.kind of nkCallKinds: - if n[0].kind == nkSym and n[0].sym.magic == mRunnableExamples and + if isRunnableExample(n[0]) and n.len >= 2 and n.lastSon.kind == nkStmtList: + prepareExamples(d, n) dispA(d.conf, dest, "\n<p><strong class=\"examples_text\">$1</strong></p>\n", "\n\\textbf{$1}\n", [rope"Examples:"]) inc d.listingCounter @@ -627,6 +673,10 @@ proc generateDoc*(d: PDoc, n: PNode) = of nkImportStmt: for i in 0 .. sonsLen(n)-1: traceDeps(d, n.sons[i]) of nkFromStmt, nkImportExceptStmt: traceDeps(d, n.sons[0]) + of nkCallKinds: + var comm: Rope = nil + getAllRunnableExamples(d, n, comm) + if comm > nil: add(d.modDesc, comm) else: discard proc add(d: PDoc; j: JsonNode) = @@ -787,14 +837,13 @@ proc getOutFile2(conf: ConfigRef; filename, ext, dir: string): string = proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) = var content = genOutFile(d) - var success = true if optStdout in d.conf.globalOptions: writeRope(stdout, content) else: let outfile = getOutFile2(d.conf, filename, outExt, "htmldocs") - success = writeRope(content, outfile) - if not success: - rawMessage(d.conf, if useWarning: warnCannotOpenFile else: errCannotOpenFile, filename) + createDir(outfile.parentDir) + if not writeRope(content, outfile): + rawMessage(d.conf, if useWarning: warnCannotOpenFile else: errCannotOpenFile, outfile) proc writeOutputJson*(d: PDoc, filename, outExt: string, useWarning = false) = diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index 068c47bb3..22fef0d47 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -21,12 +21,15 @@ type module: PSym PGen = ref TGen +template shouldProcess(g): bool = + (g.module.owner.id == g.doc.conf.mainPackageId and optWholeProject in g.doc.conf.globalOptions) or + sfMainModule in g.module.flags + template closeImpl(body: untyped) {.dirty.} = var g = PGen(p) let useWarning = sfMainModule notin g.module.flags #echo g.module.name.s, " ", g.module.owner.id, " ", gMainPackageId - if (g.module.owner.id == g.doc.conf.mainPackageId and optWholeProject in g.doc.conf.globalOptions) or - sfMainModule in g.module.flags: + if shouldProcess(g): body try: generateIndex(g.doc) @@ -35,27 +38,29 @@ template closeImpl(body: untyped) {.dirty.} = proc close(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = closeImpl: - writeOutput(g.doc, toFilename(graph.config, FileIndex g.module.position), HtmlExt, useWarning) + writeOutput(g.doc, toFullPath(graph.config, FileIndex g.module.position), HtmlExt, useWarning) proc closeJson(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = closeImpl: - writeOutputJson(g.doc, toFilename(graph.config, FileIndex g.module.position), ".json", useWarning) + writeOutputJson(g.doc, toFullPath(graph.config, FileIndex g.module.position), ".json", useWarning) proc processNode(c: PPassContext, n: PNode): PNode = result = n var g = PGen(c) - generateDoc(g.doc, n) + if shouldProcess(g): + generateDoc(g.doc, n) proc processNodeJson(c: PPassContext, n: PNode): PNode = result = n var g = PGen(c) - generateJson(g.doc, n) + if shouldProcess(g): + generateJson(g.doc, n) proc myOpen(graph: ModuleGraph; module: PSym): PPassContext = var g: PGen new(g) g.module = module - var d = newDocumentor(toFilename(graph.config, FileIndex module.position), graph.cache, graph.config) + var d = newDocumentor(toFullPath(graph.config, FileIndex module.position), graph.cache, graph.config) d.hasToc = true g.doc = d result = g diff --git a/compiler/importer.nim b/compiler/importer.nim index c013b93ab..73d2e6599 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -24,9 +24,15 @@ proc readExceptSet*(c: PContext, n: PNode): IntSet = result.incl(ident.id) proc importPureEnumField*(c: PContext; s: PSym) = - var check = strTableGet(c.importTable.symbols, s.name) + let check = strTableGet(c.importTable.symbols, s.name) if check == nil: - strTableAdd(c.pureEnumFields, s) + let checkB = strTableGet(c.pureEnumFields, s.name) + if checkB == nil: + strTableAdd(c.pureEnumFields, s) + else: + # mark as ambigous: + incl(c.ambiguousSymbols, checkB.id) + incl(c.ambiguousSymbols, s.id) proc rawImportSymbol(c: PContext, s: PSym) = # This does not handle stubs, because otherwise loading on demand would be @@ -164,7 +170,16 @@ proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym = suggestSym(c.config, n.info, result, c.graph.usageSym, false) importStmtResult.add newStrNode(toFullPath(c.config, f), n.info) +proc transformImportAs(c: PContext; n: PNode): PNode = + if n.kind == nkInfix and considerQuotedIdent(c, n[0]).s == "as": + result = newNodeI(nkImportAs, n.info) + result.add n.sons[1] + result.add n.sons[2] + else: + result = n + proc impMod(c: PContext; it: PNode; importStmtResult: PNode) = + let it = transformImportAs(c, it) let m = myImportModule(c, it, importStmtResult) if m != nil: var emptySet: IntSet @@ -174,26 +189,33 @@ proc impMod(c: PContext; it: PNode; importStmtResult: PNode) = #importForwarded(c, m.ast, emptySet) proc evalImport(c: PContext, n: PNode): PNode = - #result = n result = newNodeI(nkImportStmt, n.info) for i in countup(0, sonsLen(n) - 1): let it = n.sons[i] if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket: let sep = it[0] let dir = it[1] - let a = newNodeI(nkInfix, it.info) - a.add sep - a.add dir - a.add sep # dummy entry, replaced in the loop + var imp = newNodeI(nkInfix, it.info) + imp.add sep + imp.add dir + imp.add sep # dummy entry, replaced in the loop for x in it[2]: - a.sons[2] = x - impMod(c, a, result) + # transform `a/b/[c as d]` to `/a/b/c as d` + if x.kind == nkInfix and x.sons[0].ident.s == "as": + let impAs = copyTree(x) + imp.sons[2] = x.sons[1] + impAs.sons[1] = imp + impMod(c, imp, result) + else: + imp.sons[2] = x + impMod(c, imp, result) else: impMod(c, it, result) proc evalFrom(c: PContext, n: PNode): PNode = result = newNodeI(nkImportStmt, n.info) checkMinSonsLen(n, 2, c.config) + n.sons[0] = transformImportAs(c, n.sons[0]) var m = myImportModule(c, n.sons[0], result) if m != nil: n.sons[0] = newSymNode(m) @@ -205,6 +227,7 @@ proc evalFrom(c: PContext, n: PNode): PNode = proc evalImportExcept*(c: PContext, n: PNode): PNode = result = newNodeI(nkImportStmt, n.info) checkMinSonsLen(n, 2, c.config) + n.sons[0] = transformImportAs(c, n.sons[0]) var m = myImportModule(c, n.sons[0], result) if m != nil: n.sons[0] = newSymNode(m) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 462c622aa..1b00ddbfa 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -243,7 +243,8 @@ proc mangleName(m: BModule, s: PSym): Rope = x.add("HEX" & toHex(ord(c), 2)) inc i result = rope(x) - if s.name.s != "this" and s.kind != skField: + # From ES5 on reserved words can be used as object field names + if s.kind != skField: if optHotCodeReloading in m.config.options: # When hot reloading is enabled, we must ensure that the names # of functions and types will be preserved across rebuilds: @@ -271,9 +272,7 @@ proc escapeJSString(s: string): string = result.add("\"") proc makeJSString(s: string, escapeNonAscii = true): Rope = - if s.isNil: - result = "null".rope - elif escapeNonAscii: + if escapeNonAscii: result = strutils.escape(s).rope else: result = escapeJSString(s).rope @@ -1369,7 +1368,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = let length = int(lengthOrd(p.config, t)) let e = elemType(t) let jsTyp = arrayTypeForElemType(e) - if not jsTyp.isNil: + if jsTyp.len > 0: result = "new $1($2)" % [rope(jsTyp), rope(length)] elif length > 32: useMagic(p, "arrayConstr") diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index c5a641713..41f3806d4 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -223,6 +223,9 @@ type proc `==`*(a, b: FileIndex): bool {.borrow.} +proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} = + raise newException(ERecoverableError, msg) + const InvalidFileIDX* = FileIndex(-1) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 1b5bee008..ec9c130e3 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -244,7 +244,7 @@ else: template fixSpelling(n: PNode; ident: PIdent; op: untyped) = discard proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) = - var err = "Error: ambiguous identifier: '" & s.name.s & "'" + var err = "ambiguous identifier: '" & s.name.s & "'" var ti: TIdentIter var candidate = initIdentIter(ti, c.importTable.symbols, s.name) var i = 0 @@ -259,7 +259,7 @@ proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) = proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string) = var err = "undeclared identifier: '" & name & "'" if c.recursiveDep.len > 0: - err.add "\nThis might be caused by a recursive module dependency: " + err.add "\nThis might be caused by a recursive module dependency:\n" err.add c.recursiveDep # prevent excessive errors for 'nim check' c.recursiveDep = "" diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index d40b9d732..aeeb489c0 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -120,7 +120,8 @@ proc skipIntLit*(t: PType): PType {.inline.} = result = t proc addSonSkipIntLit*(father, son: PType) = - if isNil(father.sons): father.sons = @[] + when not defined(nimNoNilSeqs): + if isNil(father.sons): father.sons = @[] let s = son.skipIntLit add(father.sons, s) propagateToOwner(father, s) diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim index e5cbf3a2c..118002fcf 100644 --- a/compiler/modulepaths.nim +++ b/compiler/modulepaths.nim @@ -126,13 +126,6 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string = of nkInfix: let n0 = n[0] let n1 = n[1] - if n0.kind == nkIdent and n0.ident.s == "as": - # XXX hack ahead: - n.kind = nkImportAs - n.sons[0] = n.sons[1] - n.sons[1] = n.sons[2] - n.sons.setLen(2) - return getModuleName(conf, n.sons[0]) when false: if n1.kind == nkPrefix and n1[0].kind == nkIdent and n1[0].ident.s == "$": if n0.kind == nkIdent and n0.ident.s == "/": diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 1d7939142..b7b7c8474 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -102,9 +102,6 @@ proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo = proc newLineInfo*(conf: ConfigRef; filename: string, line, col: int): TLineInfo {.inline.} = result = newLineInfo(fileInfoIdx(conf, filename), line, col) -proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} = - raise newException(ERecoverableError, msg) - proc concat(strings: openarray[string]): string = var totalLen = 0 @@ -176,7 +173,7 @@ proc getHash*(conf: ConfigRef; fileIdx: FileIndex): string = proc toFullPathConsiderDirty*(conf: ConfigRef; fileIdx: FileIndex): string = if fileIdx.int32 < 0: result = "???" - elif not conf.m.fileInfos[fileIdx.int32].dirtyFile.isNil: + elif conf.m.fileInfos[fileIdx.int32].dirtyFile.len > 0: result = conf.m.fileInfos[fileIdx.int32].dirtyFile else: result = conf.m.fileInfos[fileIdx.int32].fullPath diff --git a/compiler/nim.nim b/compiler/nim.nim index 90049bdfb..0fed72dc7 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -21,7 +21,7 @@ when defined(i386) and defined(windows) and defined(vcc): import commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes, extccomp, strutils, os, osproc, platform, main, parseopt, - nodejs, scriptconfig, idents, modulegraphs, lineinfos + nodejs, scriptconfig, idents, modulegraphs, lineinfos, cmdlinehelper when hasTinyCBackend: import tccgen @@ -57,69 +57,43 @@ proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) = rawMessage(config, errGenerated, errArgsNeedRunOption) proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = - condsyms.initDefines(conf.symbols) + let self = NimProg( + supportsStdinFile: true, + processCmdLine: processCmdLine, + mainCommand: mainCommand + ) + self.initDefinesProg(conf, "nim_compiler") if paramCount() == 0: writeCommandLineUsage(conf, conf.helpWritten) - else: - # Process command line arguments: - processCmdLine(passCmd1, "", conf) - if conf.projectName == "-": - conf.projectName = "stdinfile" - conf.projectFull = "stdinfile" - conf.projectPath = canonicalizePath(conf, getCurrentDir()) - conf.projectIsStdin = true - elif conf.projectName != "": - try: - conf.projectFull = canonicalizePath(conf, conf.projectName) - except OSError: - conf.projectFull = conf.projectName - let p = splitFile(conf.projectFull) - let dir = if p.dir.len > 0: p.dir else: getCurrentDir() - conf.projectPath = canonicalizePath(conf, dir) - conf.projectName = p.name + return + + self.processCmdLineAndProjectPath(conf) + if not self.loadConfigsAndRunMainCommand(cache, conf): return + if optHints in conf.options and hintGCStats in conf.notes: echo(GC_getStatistics()) + #echo(GC_getStatistics()) + if conf.errorCounter != 0: return + when hasTinyCBackend: + if conf.cmd == cmdRun: + tccgen.run(conf.arguments) + if optRun in conf.globalOptions: + if conf.cmd == cmdCompileToJS: + var ex: string + if conf.outFile.len > 0: + ex = conf.outFile.prependCurDir.quoteShell + else: + ex = quoteShell( + completeCFilePath(conf, changeFileExt(conf.projectFull, "js").prependCurDir)) + execExternalProgram(conf, findNodeJs() & " " & ex & ' ' & conf.arguments) else: - conf.projectPath = canonicalizePath(conf, getCurrentDir()) - loadConfigs(DefaultConfig, cache, conf) # load all config files - let scriptFile = conf.projectFull.changeFileExt("nims") - if fileExists(scriptFile): - runNimScript(cache, scriptFile, freshDefines=false, conf) - # 'nim foo.nims' means to just run the NimScript file and do nothing more: - if scriptFile == conf.projectFull: return - elif fileExists(conf.projectPath / "config.nims"): - # directory wide NimScript file - runNimScript(cache, conf.projectPath / "config.nims", freshDefines=false, conf) - # now process command line arguments again, because some options in the - # command line can overwite the config file's settings - extccomp.initVars(conf) - processCmdLine(passCmd2, "", conf) - if conf.command == "": - rawMessage(conf, errGenerated, "command missing") - mainCommand(newModuleGraph(cache, conf)) - if optHints in conf.options and hintGCStats in conf.notes: echo(GC_getStatistics()) - #echo(GC_getStatistics()) - if conf.errorCounter == 0: - when hasTinyCBackend: - if conf.cmd == cmdRun: - tccgen.run(conf.arguments) - if optRun in conf.globalOptions: - if conf.cmd == cmdCompileToJS: - var ex: string - if conf.outFile.len > 0: - ex = conf.outFile.prependCurDir.quoteShell - else: - ex = quoteShell( - completeCFilePath(conf, changeFileExt(conf.projectFull, "js").prependCurDir)) - execExternalProgram(conf, findNodeJs() & " " & ex & ' ' & conf.arguments) - else: - var binPath: string - if conf.outFile.len > 0: - # If the user specified an outFile path, use that directly. - binPath = conf.outFile.prependCurDir - else: - # Figure out ourselves a valid binary name. - binPath = changeFileExt(conf.projectFull, ExeExt).prependCurDir - var ex = quoteShell(binPath) - execExternalProgram(conf, ex & ' ' & conf.arguments) + var binPath: string + if conf.outFile.len > 0: + # If the user specified an outFile path, use that directly. + binPath = conf.outFile.prependCurDir + else: + # Figure out ourselves a valid binary name. + binPath = changeFileExt(conf.projectFull, ExeExt).prependCurDir + var ex = quoteShell(binPath) + execExternalProgram(conf, ex & ' ' & conf.arguments) when declared(GC_setMaxPause): GC_setMaxPause 2_000 diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim index 9a23535bf..c5521735b 100644 --- a/compiler/nimblecmd.nim +++ b/compiler/nimblecmd.nim @@ -140,18 +140,19 @@ when isMainModule: doAssert v"#aaaqwe" < v"1.1" # We cannot assume that a branch is newer. doAssert v"#a111" < v"#head" + let conf = newConfigRef() var rr = newStringTable() - addPackage rr, "irc-#a111" - addPackage rr, "irc-#head" - addPackage rr, "irc-0.1.0" - addPackage rr, "irc" - addPackage rr, "another" - addPackage rr, "another-0.1" + addPackage conf, rr, "irc-#a111", unknownLineInfo() + addPackage conf, rr, "irc-#head", unknownLineInfo() + addPackage conf, rr, "irc-0.1.0", unknownLineInfo() + #addPackage conf, rr, "irc", unknownLineInfo() + #addPackage conf, rr, "another", unknownLineInfo() + addPackage conf, rr, "another-0.1", unknownLineInfo() - addPackage rr, "ab-0.1.3" - addPackage rr, "ab-0.1" - addPackage rr, "justone" + addPackage conf, rr, "ab-0.1.3", unknownLineInfo() + addPackage conf, rr, "ab-0.1", unknownLineInfo() + addPackage conf, rr, "justone-1.0", unknownLineInfo() doAssert toSeq(rr.chosen) == - @["irc-#head", "another-0.1", "ab-0.1.3", "justone"] + @["irc-#head", "another-0.1", "ab-0.1.3", "justone-1.0"] diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 1a8a0acb5..5f6889a6f 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -175,9 +175,9 @@ proc parseAssignment(L: var TLexer, tok: var TToken; confTok(L, tok, config, condStack) if tok.tokType == tkBracketLe: # BUGFIX: val, not s! - # BUGFIX: do not copy '['! confTok(L, tok, config, condStack) checkSymbol(L, tok) + add(val, '[') add(val, tokToStr(tok)) confTok(L, tok, config, condStack) if tok.tokType == tkBracketRi: confTok(L, tok, config, condStack) @@ -219,10 +219,10 @@ proc readConfigFile( closeLexer(L) return true -proc getUserConfigPath(filename: string): string = +proc getUserConfigPath*(filename: string): string = result = joinPath([getConfigDir(), "nim", filename]) -proc getSystemConfigPath(conf: ConfigRef; filename: string): string = +proc getSystemConfigPath*(conf: ConfigRef; filename: string): string = # try standard configuration file (installation did not distribute files # the UNIX way) let p = getPrefixDir(conf) @@ -241,7 +241,7 @@ proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) = if readConfigFile(configPath, cache, conf): add(configFiles, configPath) - if optSkipConfigFile notin conf.globalOptions: + if optSkipSystemConfigFile notin conf.globalOptions: readConfigFile(getSystemConfigPath(conf, cfg)) if optSkipUserConfigFile notin conf.globalOptions: @@ -263,4 +263,5 @@ proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) = readConfigFile(projectConfig) for filename in configFiles: + # delayed to here so that `hintConf` is honored rawMessage(conf, hintConf, filename) diff --git a/compiler/options.nim b/compiler/options.nim index f2701dadd..4a484e6e2 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -12,6 +12,7 @@ import prefixmatches from terminal import isatty +from times import utc, fromUnix, local, getTime, format, DateTime const hasTinyCBackend* = defined(tinyc) @@ -54,10 +55,10 @@ type # please make sure we have under 32 options optGenMapping, # generate a mapping file optRun, # run the compiled project optCheckNep1, # check that the names adhere to NEP-1 - optSkipConfigFile, # skip the general config file - optSkipProjConfigFile, # skip the project's config file - optSkipUserConfigFile, # skip the users's config file - optSkipParentConfigFiles, # skip parent dir's config files + optSkipSystemConfigFile, # skip the system's cfg/nims config file + optSkipProjConfigFile, # skip the project's cfg/nims config file + optSkipUserConfigFile, # skip the users's cfg/nims config file + optSkipParentConfigFiles, # skip parent dir's cfg/nims config files optNoMain, # do not generate a "main" proc optUseColors, # use colors for hints, warnings, and errors optThreads, # support for multi-threading @@ -104,8 +105,8 @@ type cmdJsonScript # compile a .json build file TStringSeq* = seq[string] TGCMode* = enum # the selected GC - gcNone, gcBoehm, gcGo, gcRegions, gcMarkAndSweep, gcRefc, - gcV2, gcGenerational + gcNone, gcBoehm, gcGo, gcRegions, gcMarkAndSweep, gcDestructors, + gcRefc, gcV2 IdeCmd* = enum ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod, @@ -260,6 +261,24 @@ const optPatterns, optNilCheck, optMoveCheck} DefaultGlobalOptions* = {optThreadAnalysis} +proc getSrcTimestamp(): DateTime = + try: + result = utc(fromUnix(parseInt(getEnv("SOURCE_DATE_EPOCH", + "not a number")))) + except ValueError: + # Environment variable malformed. + # https://reproducible-builds.org/specs/source-date-epoch/: "If the + # value is malformed, the build process SHOULD exit with a non-zero + # error code", which this doesn't do. This uses local time, because + # that maintains compatibility with existing usage. + result = utc getTime() + +proc getDateStr*(): string = + result = format(getSrcTimestamp(), "yyyy-MM-dd") + +proc getClockStr*(): string = + result = format(getSrcTimestamp(), "HH:mm:ss") + template newPackageCache*(): untyped = newStringTable(when FileSystemCaseSensitive: modeCaseInsensitive @@ -374,7 +393,7 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool = else: discard proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in {cmdDoc, cmdIdeTools} -proc usesNativeGC*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc +proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc template compilationCachePresent*(conf: ConfigRef): untyped = conf.symbolFiles in {v2Sf, writeOnlySf} @@ -392,6 +411,7 @@ const TexExt* = "tex" IniExt* = "ini" DefaultConfig* = "nim.cfg" + DefaultConfigNims* = "config.nims" DocConfig* = "nimdoc.cfg" DocTexConfig* = "nimdoc.tex.cfg" diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim index 2efab58b0..7414aeb71 100644 --- a/compiler/packagehandling.nim +++ b/compiler/packagehandling.nim @@ -33,7 +33,8 @@ proc getPackageName*(conf: ConfigRef; path: string): string = result = file.splitFile.name break packageSearch # we also store if we didn't find anything: - if result.isNil: result = "" + when not defined(nimNoNilSeqs): + if result.isNil: result = "" for d in myParentDirs(path): #echo "set cache ", d, " |", result, "|", parents conf.packageCache[d] = result diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 0ef87720d..910ee799c 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -71,6 +71,7 @@ const letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect, wThread, wRaises, wLocks, wTags, wGcSafe} + forVarPragmas* = {wInject, wGensym} allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode = @@ -95,7 +96,9 @@ const errIntLiteralExpected = "integer literal expected" proc invalidPragma*(c: PContext; n: PNode) = - localError(c.config, n.info, "invalid pragma: " % renderTree(n, {renderNoComments})) + localError(c.config, n.info, "invalid pragma: " & renderTree(n, {renderNoComments})) +proc illegalCustomPragma*(c: PContext, n: PNode, s: PSym) = + localError(c.config, n.info, "cannot attach a custom pragma to '" & s.name.s & "'") proc pragmaAsm*(c: PContext, n: PNode): char = result = '\0' @@ -336,6 +339,20 @@ proc pragmaToOptions(w: TSpecialWord): TOptions {.inline.} = of wPatterns: {optPatterns} else: {} +proc processExperimental(c: PContext; n: PNode) = + if n.kind notin nkPragmaCallKinds or n.len != 2: + c.features.incl oldExperimentalFeatures + else: + n[1] = c.semConstExpr(c, n[1]) + case n[1].kind + of nkStrLit, nkRStrLit, nkTripleStrLit: + try: + c.features.incl parseEnum[Feature](n[1].strVal) + except ValueError: + localError(c.config, n[1].info, "unknown experimental feature") + else: + localError(c.config, n.info, errStringLiteralExpected) + proc tryProcessOption(c: PContext, n: PNode, resOptions: var TOptions): bool = result = true if n.kind notin nkPragmaCallKinds or n.len != 2: result = false @@ -343,6 +360,9 @@ proc tryProcessOption(c: PContext, n: PNode, resOptions: var TOptions): bool = elif n.sons[0].kind != nkIdent: result = false else: let sw = whichKeyword(n.sons[0].ident) + if sw == wExperimental: + processExperimental(c, n) + return true let opts = pragmaToOptions(sw) if opts != {}: onOff(c, n, opts, resOptions) @@ -381,6 +401,7 @@ proc processPush(c: PContext, n: PNode, start: int) = x.defaultCC = y.defaultCC x.dynlib = y.dynlib x.notes = c.config.notes + x.features = c.features c.optionStack.add(x) for i in countup(start, sonsLen(n) - 1): if not tryProcessOption(c, n.sons[i], c.config.options): @@ -400,6 +421,7 @@ proc processPop(c: PContext, n: PNode) = else: c.config.options = c.optionStack[^1].options c.config.notes = c.optionStack[^1].notes + c.features = c.optionStack[^1].features c.optionStack.setLen(c.optionStack.len - 1) proc processDefine(c: PContext, n: PNode) = @@ -710,9 +732,7 @@ proc semCustomPragma(c: PContext, n: PNode): PNode = elif n.kind == nkExprColonExpr: result.kind = n.kind # pragma(arg) -> pragma: arg -proc processExperimental(c: PContext; n: PNode; s: PSym) = - if not isTopLevel(c): - localError(c.config, n.info, "'experimental' pragma only valid as toplevel statement") +proc processExperimental(c: PContext; n: PNode) = if n.kind notin nkPragmaCallKinds or n.len != 2: c.features.incl oldExperimentalFeatures else: @@ -1065,7 +1085,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, else: it.sons[1] = c.semExpr(c, it.sons[1]) of wExperimental: - processExperimental(c, it, sym) + if not isTopLevel(c): + localError(c.config, n.info, "'experimental' pragma only valid as toplevel statement or in a 'push' environment") + processExperimental(c, it) of wThis: if it.kind in nkPragmaCallKinds and it.len == 2: c.selfName = considerQuotedIdent(c, it[1]) @@ -1090,9 +1112,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, else: sym.flags.incl sfUsed of wLiftLocals: discard else: invalidPragma(c, it) - else: + elif sym.kind in {skField,skProc,skFunc,skConverter,skMethod,skType}: n.sons[i] = semCustomPragma(c, it) - + else: + illegalCustomPragma(c, it, sym) proc implicitPragmas*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = diff --git a/compiler/renderer.nim b/compiler/renderer.nim index c3e151f5a..a8f3f4afc 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -175,7 +175,7 @@ proc put(g: var TSrcGen, kind: TTokType, s: string) = g.pendingWhitespace = s.len proc putComment(g: var TSrcGen, s: string) = - if s.isNil: return + if s.len == 0: return var i = 0 let hi = len(s) - 1 var isCode = (len(s) >= 2) and (s[1] != ' ') @@ -216,7 +216,7 @@ proc putComment(g: var TSrcGen, s: string) = optNL(g) proc maxLineLength(s: string): int = - if s.isNil: return 0 + if s.len == 0: return 0 var i = 0 let hi = len(s) - 1 var lineLen = 0 @@ -752,7 +752,8 @@ proc gproc(g: var TSrcGen, n: PNode) = gsub(g, n.sons[genericParamsPos]) g.inGenericParams = oldInGenericParams gsub(g, n.sons[paramsPos]) - gsub(g, n.sons[pragmasPos]) + if renderNoPragmas notin g.flags: + gsub(g, n.sons[pragmasPos]) if renderNoBody notin g.flags: if n.sons[bodyPos].kind != nkEmpty: put(g, tkSpaces, Space) @@ -1297,17 +1298,16 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = putWithSpace(g, tkContinue, "continue") gsub(g, n, 0) of nkPragma: - if renderNoPragmas notin g.flags: - if g.inPragma <= 0: - inc g.inPragma - #if not previousNL(g): - put(g, tkSpaces, Space) - put(g, tkCurlyDotLe, "{.") - gcomma(g, n, emptyContext) - put(g, tkCurlyDotRi, ".}") - dec g.inPragma - else: - gcomma(g, n, emptyContext) + if g.inPragma <= 0: + inc g.inPragma + #if not previousNL(g): + put(g, tkSpaces, Space) + put(g, tkCurlyDotLe, "{.") + gcomma(g, n, emptyContext) + put(g, tkCurlyDotRi, ".}") + dec g.inPragma + else: + gcomma(g, n, emptyContext) of nkImportStmt, nkExportStmt: if n.kind == nkImportStmt: putWithSpace(g, tkImport, "import") diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index 659206a40..184b60733 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -171,7 +171,7 @@ proc runNimScript*(cache: IdentCache; scriptName: string; incl(m.flags, sfMainModule) graph.vm = setupVM(m, cache, scriptName, graph) - graph.compileSystemModule() + graph.compileSystemModule() # TODO: see why this unsets hintConf in conf.notes discard graph.processModule(m, llStreamOpen(scriptName, fmRead)) # ensure we load 'system.nim' again for the real non-config stuff! diff --git a/compiler/sem.nim b/compiler/sem.nim index 6128c02d1..7a83c3079 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -607,28 +607,6 @@ proc myProcess(context: PPassContext, n: PNode): PNode = #if c.config.cmd == cmdIdeTools: findSuggest(c, n) rod.storeNode(c.graph, c.module, result) -proc testExamples(c: PContext) = - let outputDir = c.config.getNimcacheDir / "runnableExamples" - createDir(outputDir) - let inp = toFullPath(c.config, c.module.info) - let outp = outputDir / extractFilename(inp.changeFileExt"" & "_examples.nim") - let nimcache = outp.changeFileExt"" & "_nimcache" - renderModule(c.runnableExamples, inp, outp, conf = c.config) - let backend = if isDefined(c.config, "js"): "js" - elif isDefined(c.config, "cpp"): "cpp" - elif isDefined(c.config, "objc"): "objc" - else: "c" - if os.execShellCmd(os.getAppFilename() & " " & backend & " --nimcache:" & nimcache & " -r " & outp) != 0: - quit "[Examples] failed: see " & outp - else: - # keep generated source file `outp` to allow inspection. - rawMessage(c.config, hintSuccess, ["runnableExamples: " & outp]) - removeFile(outp.changeFileExt(ExeExt)) - try: - removeDir(nimcache) - except OSError: - discard - proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = var c = PContext(context) if c.config.cmd == cmdIdeTools and not c.suggestionsMade: @@ -644,7 +622,6 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = popOwner(c) popProcCon(c) storeRemaining(c.graph, c.module) - if c.runnableExamples != nil: testExamples(c) const semPass* = makePass(myOpen, myProcess, myClose, isFrontend = true) diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index a05ef7a28..8b2e20efc 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -197,13 +197,10 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = case t.kind of tyNone, tyEmpty, tyVoid: discard of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, - tyPtr, tyString, tyRef, tyOpt: + tyPtr, tyRef, tyOpt: defaultOp(c, t, body, x, y) - of tyArray, tySequence: + of tyArray: if {tfHasAsgn, tfUncheckedArray} * t.flags == {tfHasAsgn}: - if t.kind == tySequence: - # XXX add 'nil' handling here - body.add newSeqCall(c.c, x, y) let i = declareCounter(c, body, firstOrd(c.c.config, t)) let whileLoop = genWhileLoop(c, i, x) let elemType = t.lastSon @@ -213,6 +210,27 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add whileLoop else: defaultOp(c, t, body, x, y) + of tySequence: + # note that tfHasAsgn is propagated so we need the check on + # 'selectedGC' here to determine if we have the new runtime. + if c.c.config.selectedGC == gcDestructors: + discard considerOverloadedOp(c, t, body, x, y) + elif tfHasAsgn in t.flags: + body.add newSeqCall(c.c, x, y) + let i = declareCounter(c, body, firstOrd(c.c.config, t)) + let whileLoop = genWhileLoop(c, i, x) + let elemType = t.lastSon + liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType), + y.at(i, elemType)) + addIncStmt(c, whileLoop.sons[1], i) + body.add whileLoop + else: + defaultOp(c, t, body, x, y) + of tyString: + if tfHasAsgn in t.flags: + discard considerOverloadedOp(c, t, body, x, y) + else: + defaultOp(c, t, body, x, y) of tyObject, tyDistinct: if not considerOverloadedOp(c, t, body, x, y): if t.sons[0] != nil: diff --git a/compiler/semcall.nim b/compiler/semcall.nim index dc71f2567..53f7045dd 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -138,7 +138,9 @@ proc effectProblem(f, a: PType; result: var string) = proc renderNotLValue(n: PNode): string = result = $n - if n.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv} and n.len == 2: + if n.kind == nkHiddenCallConv and n.len > 1: + result = $n[0] & "(" & result & ")" + elif n.kind in {nkHiddenStdConv, nkHiddenSubConv} and n.len == 2: result = typeToString(n.typ.skipTypes(abstractVar)) & "(" & result & ")" proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): @@ -164,8 +166,20 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): prefer = preferModuleInfo break + when false: + # we pretend procs are attached to the type of the first + # argument in order to remove plenty of candidates. This is + # comparable to what C# does and C# is doing fine. + var filterOnlyFirst = false + for err in errors: + if err.firstMismatch > 1: + filterOnlyFirst = true + break + var candidates = "" for err in errors: + when false: + if filterOnlyFirst and err.firstMismatch == 1: continue if err.sym.kind in routineKinds and err.sym.ast != nil: add(candidates, renderTree(err.sym.ast, {renderNoBody, renderNoComments, renderNoPragmas})) @@ -175,15 +189,18 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): if err.firstMismatch != 0 and n.len > 1: let cond = n.len > 2 if cond: - candidates.add(" first type mismatch at position: " & $err.firstMismatch & - "\n required type: ") + candidates.add(" first type mismatch at position: " & $abs(err.firstMismatch)) + if err.firstMismatch >= 0: candidates.add("\n required type: ") + else: candidates.add("\n unknown named parameter: " & $n[-err.firstMismatch][0]) var wanted, got: PType = nil - if err.firstMismatch < err.sym.typ.len: + if err.firstMismatch < 0: + discard + elif err.firstMismatch < err.sym.typ.len: wanted = err.sym.typ.sons[err.firstMismatch] if cond: candidates.add typeToString(wanted) else: if cond: candidates.add "none" - if err.firstMismatch < n.len: + if err.firstMismatch > 0 and err.firstMismatch < n.len: if cond: candidates.add "\n but expression '" candidates.add renderTree(n[err.firstMismatch]) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 4189a5214..6d6627690 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -22,6 +22,7 @@ type defaultCC*: TCallingConvention dynlib*: PLib notes*: TNoteKinds + features*: set[Feature] otherPragmas*: PNode # every pragma can be pushed POptionEntry* = ref TOptionEntry @@ -140,7 +141,6 @@ type # the generic type has been constructed completely. See # tests/destructor/topttree.nim for an example that # would otherwise fail. - runnableExamples*: PNode template config*(c: PContext): ConfigRef = c.graph.config diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 91bee54ac..ce953f17c 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -800,7 +800,9 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode = analyseIfAddressTakenInCall(c, result) if callee.magic != mNone: result = magicsAfterOverloadResolution(c, result, flags) - if result.typ != nil: liftTypeBoundOps(c, result.typ, n.info) + if result.typ != nil and + not (result.typ.kind == tySequence and result.typ.sons[0].kind == tyEmpty): + liftTypeBoundOps(c, result.typ, n.info) #result = patchResolvedTypeBoundOp(c, result) if c.matchedConcept == nil: result = evalAtCompileTime(c, result) @@ -1389,8 +1391,8 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = n.sons[1] = semConstExpr(c, n.sons[1]) if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal, tyAlias, tySink}).kind in {tyInt..tyInt64}: - var idx = getOrdValue(n.sons[1]) - if idx >= 0 and idx < sonsLen(arr): n.typ = arr.sons[int(idx)] + let idx = getOrdValue(n.sons[1]) + if idx >= 0 and idx < len(arr): n.typ = arr.sons[int(idx)] else: localError(c.config, n.info, "invalid index value for tuple subscript") result = n else: @@ -1565,6 +1567,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = n.sons[1] = fitNode(c, le, rhs, n.info) liftTypeBoundOps(c, lhs.typ, lhs.info) + #liftTypeBoundOps(c, n.sons[0].typ, n.sons[0].info) fixAbstractType(c, n) asgnToResultVar(c, n, n.sons[0], n.sons[1]) @@ -1950,13 +1953,6 @@ proc setMs(n: PNode, s: PSym): PNode = n.sons[0] = newSymNode(s) n.sons[0].info = n.info -proc extractImports(n: PNode; result: PNode) = - if n.kind in {nkImportStmt, nkImportExceptStmt, nkFromStmt}: - result.add copyTree(n) - n.kind = nkEmpty - return - for i in 0..<n.safeLen: extractImports(n[i], result) - 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! @@ -2030,16 +2026,17 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = result = magicsAfterOverloadResolution(c, result, flags) of mRunnableExamples: if c.config.cmd == cmdDoc and n.len >= 2 and n.lastSon.kind == nkStmtList: - if sfMainModule in c.module.flags: - let inp = toFullPath(c.config, c.module.info) - if c.runnableExamples == nil: - c.runnableExamples = newTree(nkStmtList, - newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp)))) - let imports = newTree(nkStmtList) - var saved_lastSon = copyTree n.lastSon - extractImports(saved_lastSon, imports) - for imp in imports: c.runnableExamples.add imp - c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree saved_lastSon) + when false: + if sfMainModule in c.module.flags: + let inp = toFullPath(c.config, c.module.info) + if c.runnableExamples == nil: + c.runnableExamples = newTree(nkStmtList, + newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp)))) + let imports = newTree(nkStmtList) + var savedLastSon = copyTree n.lastSon + extractImports(savedLastSon, imports) + for imp in imports: c.runnableExamples.add imp + c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree savedLastSon) result = setMs(n, s) else: result = c.graph.emptyNode @@ -2082,7 +2079,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = typ = commonType(typ, it.sons[1].typ) result = n # when nimvm is not elimited until codegen else: - var e = semConstExpr(c, it.sons[0]) + let e = forceBool(c, semConstExpr(c, it.sons[0])) if e.kind != nkIntLit: # can happen for cascading errors, assume false # InternalError(n.info, "semWhen") diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 444940144..27a6af1f4 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -11,7 +11,7 @@ # and evaluation phase import - strutils, options, ast, astalgo, trees, treetab, nimsets, times, + strutils, options, ast, astalgo, trees, treetab, nimsets, nversion, platform, math, msgs, os, condsyms, idents, renderer, types, commands, magicsys, modulegraphs, strtabs, lineinfos @@ -450,21 +450,38 @@ proc rangeCheck(n: PNode, value: BiggestInt; g: ModuleGraph) = localError(g.config, n.info, "cannot convert " & $value & " to " & typeToString(n.typ)) -proc foldConv*(n, a: PNode; g: ModuleGraph; check = false): PNode = +proc foldConv(n, a: PNode; g: ModuleGraph; check = false): PNode = + let dstTyp = skipTypes(n.typ, abstractRange) + let srcTyp = skipTypes(a.typ, abstractRange) + # XXX range checks? - case skipTypes(n.typ, abstractRange).kind - of tyInt..tyInt64, tyUInt..tyUInt64: - case skipTypes(a.typ, abstractRange).kind + case dstTyp.kind + of tyInt..tyInt64, tyUint..tyUInt64: + case srcTyp.kind of tyFloat..tyFloat64: result = newIntNodeT(int(getFloat(a)), n, g) - of tyChar: result = newIntNodeT(getOrdValue(a), n, g) + of tyChar: + result = newIntNodeT(getOrdValue(a), n, g) + of tyUInt8..tyUInt32, tyInt8..tyInt32: + let fromSigned = srcTyp.kind in tyInt..tyInt64 + let toSigned = dstTyp.kind in tyInt..tyInt64 + + let mask = lastOrd(g.config, dstTyp, fixedUnsigned=true) + + var val = + if toSigned: + a.getOrdValue mod mask + else: + a.getOrdValue and mask + + result = newIntNodeT(val, n, g) else: result = a result.typ = n.typ if check and result.kind in {nkCharLit..nkUInt64Lit}: rangeCheck(n, result.intVal, g) of tyFloat..tyFloat64: - case skipTypes(a.typ, abstractRange).kind + case srcTyp.kind of tyInt..tyInt64, tyEnum, tyBool, tyChar: result = newFloatNodeT(toBiggestFloat(getOrdValue(a)), n, g) else: @@ -549,19 +566,6 @@ proc newSymNodeTypeDesc*(s: PSym; info: TLineInfo): PNode = proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode = result = nil - - proc getSrcTimestamp(): DateTime = - try: - result = utc(fromUnix(parseInt(getEnv("SOURCE_DATE_EPOCH", - "not a number")))) - except ValueError: - # Environment variable malformed. - # https://reproducible-builds.org/specs/source-date-epoch/: "If the - # value is malformed, the build process SHOULD exit with a non-zero - # error code", which this doesn't do. This uses local time, because - # that maintains compatibility with existing usage. - result = local(getTime()) - case n.kind of nkSym: var s = n.sym @@ -571,10 +575,8 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode = of skConst: case s.magic of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n, g) - of mCompileDate: result = newStrNodeT(format(getSrcTimestamp(), - "yyyy-MM-dd"), n, g) - of mCompileTime: result = newStrNodeT(format(getSrcTimestamp(), - "HH:mm:ss"), n, g) + of mCompileDate: result = newStrNodeT(getDateStr(), n, g) + of mCompileTime: result = newStrNodeT(getClockStr(), n, g) of mCpuEndian: result = newIntNodeT(ord(CPU[g.config.target.targetCPU].endian), n, g) of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[g.config.target.targetOS].name), n, g) of mHostCPU: result = newStrNodeT(platform.CPU[g.config.target.targetCPU].name.toLowerAscii, n, g) @@ -742,7 +744,8 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode = of nkHiddenStdConv, nkHiddenSubConv, nkConv: var a = getConstExpr(m, n.sons[1], g) if a == nil: return - result = foldConv(n, a, g, check=n.kind == nkHiddenStdConv) + # XXX: we should enable `check` for other conversion types too + result = foldConv(n, a, g, check=n.kind == nkHiddenSubConv) of nkCast: var a = getConstExpr(m, n.sons[1], g) if a == nil: return diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index e3c750f5b..7be0610a2 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -215,7 +215,7 @@ proc semGenericStmt(c: PContext, n: PNode, checkMinSonsLen(n, 1, c.config) let fn = n.sons[0] var s = qualifiedLookUp(c, fn, {}) - if s == nil and + if s == nil and {withinMixin, withinConcept}*flags == {} and fn.kind in {nkIdent, nkAccQuoted} and considerQuotedIdent(c, fn).id notin ctx.toMixin: @@ -225,7 +225,7 @@ proc semGenericStmt(c: PContext, n: PNode, var mixinContext = false if s != nil: incl(s.flags, sfUsed) - mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles} + mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles, mRunnableExamples} let sc = symChoice(c, fn, s, if s.isMixedIn: scForceOpen else: scOpen) case s.kind of skMacro: diff --git a/compiler/seminst.nim b/compiler/seminst.nim index f9d7c3754..4bf1e6ef2 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -326,7 +326,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, # no need to instantiate generic templates/macros: internalAssert c.config, fn.kind notin {skMacro, skTemplate} # generates an instantiated proc - if c.instCounter > 1000: internalError(c.config, fn.ast.info, "nesting too deep") + if c.instCounter > 50: + globalError(c.config, info, "generic instantiation too nested") inc(c.instCounter) # careful! we copy the whole AST including the possibly nil body! var n = copyTree(fn.ast) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index bdea07ea8..0a9de674b 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -323,13 +323,13 @@ proc catches(tracked: PEffects, e: PType) = dec L else: inc i - if not isNil(tracked.exc.sons): + if tracked.exc.len > 0: setLen(tracked.exc.sons, L) else: assert L == 0 proc catchesAll(tracked: PEffects) = - if not isNil(tracked.exc.sons): + if tracked.exc.len > 0: setLen(tracked.exc.sons, tracked.bottom) proc track(tracked: PEffects, n: PNode) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 170ac799e..fb01127fc 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -567,6 +567,8 @@ proc symForVar(c: PContext, n: PNode): PSym = let m = if n.kind == nkPragmaExpr: n.sons[0] else: n result = newSymG(skForVar, m, c) styleCheckDef(c.config, result) + if n.kind == nkPragmaExpr: + pragma(c, result, n.sons[1], forVarPragmas) proc semForVars(c: PContext, n: PNode): PNode = result = n @@ -802,7 +804,7 @@ proc semRaise(c: PContext, n: PNode): PNode = if not isImportedException(typ, c.config): if typ.kind != tyRef or typ.lastSon.kind != tyObject: localError(c.config, n.info, errExprCannotBeRaised) - if not isException(typ.lastSon): + if typ.len > 0 and not isException(typ.lastSon): localError(c.config, n.info, "raised object of type $1 does not inherit from Exception", [typeToString(typ)]) @@ -1056,8 +1058,8 @@ proc checkForMetaFields(c: PContext; n: PNode) = case t.kind of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyLent, tyPtr, tyRef, tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink: - let start = int ord(t.kind in {tyGenericInvocation, tyGenericInst}) - for i in start ..< t.sons.len: + let start = ord(t.kind in {tyGenericInvocation, tyGenericInst}) + for i in start ..< t.len: checkMeta(t.sons[i]) else: checkMeta(t) @@ -1376,7 +1378,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.lastSon elif obj.kind == tyGenericInvocation: obj = obj.sons[0] else: break - if obj.kind in {tyObject, tyDistinct}: + if obj.kind in {tyObject, tyDistinct, tySequence, tyString}: if obj.destructor.isNil: obj.destructor = s else: @@ -1398,7 +1400,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = if t.kind == tyGenericBody: t = t.lastSon elif t.kind == tyGenericInvocation: t = t.sons[0] else: break - if t.kind in {tyObject, tyDistinct, tyEnum}: + if t.kind in {tyObject, tyDistinct, tyEnum, tySequence, tyString}: if t.deepCopy.isNil: t.deepCopy = s else: localError(c.config, n.info, errGenerated, @@ -1427,7 +1429,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = elif objB.kind in {tyGenericInvocation, tyGenericInst}: objB = objB.sons[0] else: break - if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB): + if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, objB): let opr = if s.name.s == "=": addr(obj.assignment) else: addr(obj.sink) if opr[].isNil: opr[] = s @@ -1592,7 +1594,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if proto.typ.callConv != s.typ.callConv or proto.typ.flags < s.typ.flags: localError(c.config, n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX % ("'" & proto.name.s & "' from " & c.config$proto.info)) - if sfForward notin proto.flags: + if sfForward notin proto.flags and proto.magic == mNone: wrongRedefinition(c, n.info, proto.name.s) excl(proto.flags, sfForward) closeScope(c) # close scope with wrong parameter symbols @@ -1609,7 +1611,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, n.sons[pragmasPos] = proto.ast.sons[pragmasPos] if n.sons[namePos].kind != nkSym: internalError(c.config, n.info, "semProcAux") n.sons[namePos].sym = proto - if importantComments(c.config) and not isNil(proto.ast.comment): + if importantComments(c.config) and proto.ast.comment.len > 0: n.comment = proto.ast.comment proto.ast = n # needed for code generation popOwner(c) @@ -1658,7 +1660,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, openScope(c) n.sons[bodyPos] = semGenericStmt(c, n.sons[bodyPos]) closeScope(c) - fixupInstantiatedSymbols(c, s) + if s.magic == mNone: + fixupInstantiatedSymbols(c, s) if s.kind == skMethod: semMethodPrototype(c, s, n) if sfImportc in s.flags: # so we just ignore the body after semantic checking for importc: diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 2952831e9..396696422 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -210,7 +210,7 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = if s != nil and s.owner == c.owner and sfGenSym in s.flags: styleCheckUse(n.info, s) replaceIdentBySym(c.c, n, newSymNode(s, n.info)) - else: + elif not (n.kind == nkSym and sfGenSym in n.sym.flags): let local = newGenSym(k, ident, c) addPrelimDecl(c.c, local) styleCheckDef(c.c.config, n.info, local) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index dd1e96bde..86f3a17ab 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -145,9 +145,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType = localError(c.config, n.info, errXExpectsOneTypeParam % "set") addSonSkipIntLit(result, errorType(c)) -proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string, - prev: PType): PType = - result = newOrPrevType(kind, prev, c) +proc semContainerArg(c: PContext; n: PNode, kindStr: string; result: PType) = if sonsLen(n) == 2: var base = semTypeNode(c, n.sons[1], nil) if base.kind == tyVoid: @@ -157,6 +155,11 @@ proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string, localError(c.config, n.info, errXExpectsOneTypeParam % kindStr) addSonSkipIntLit(result, errorType(c)) +proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string, + prev: PType): PType = + result = newOrPrevType(kind, prev, c) + semContainerArg(c, n, kindStr, result) + proc semVarargs(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tyVarargs, prev, c) if sonsLen(n) == 2 or sonsLen(n) == 3: @@ -1507,7 +1510,24 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of mRange: result = semRange(c, n, prev) of mSet: result = semSet(c, n, prev) of mOrdinal: result = semOrdinal(c, n, prev) - of mSeq: result = semContainer(c, n, tySequence, "seq", prev) + of mSeq: + if c.config.selectedGc == gcDestructors: + let s = c.graph.sysTypes[tySequence] + assert s != nil + assert prev == nil + result = copyType(s, s.owner, keepId=false) + # XXX figure out why this has children already... + result.sons.setLen 0 + result.n = nil + if c.config.selectedGc == gcDestructors: + result.flags = {tfHasAsgn} + else: + result.flags = {} + semContainerArg(c, n, "seq", result) + else: + result = semContainer(c, n, tySequence, "seq", prev) + if c.config.selectedGc == gcDestructors: + incl result.flags, tfHasAsgn of mOpt: result = semContainer(c, n, tyOpt, "opt", prev) of mVarargs: result = semVarargs(c, n, prev) of mTypeDesc, mTypeTy: @@ -1687,6 +1707,9 @@ proc processMagicType(c: PContext, m: PSym) = of mString: setMagicType(c.config, m, tyString, c.config.target.ptrSize) rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar)) + when false: + if c.config.selectedGc == gcDestructors: + incl m.typ.flags, tfHasAsgn of mCstring: setMagicType(c.config, m, tyCString, c.config.target.ptrSize) rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar)) @@ -1726,6 +1749,10 @@ proc processMagicType(c: PContext, m: PSym) = setMagicType(c.config, m, tySet, 0) of mSeq: setMagicType(c.config, m, tySequence, 0) + if c.config.selectedGc == gcDestructors: + incl m.typ.flags, tfHasAsgn + assert c.graph.sysTypes[tySequence] == nil + c.graph.sysTypes[tySequence] = m.typ of mOpt: setMagicType(c.config, m, tyOpt, 0) of mOrdinal: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 932163055..407e34619 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1921,6 +1921,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, result.typ.n = arg return + let oldInheritancePenalty = m.inheritancePenalty var r = typeRel(m, f, a) # This special typing rule for macros and templates is not documented @@ -2002,7 +2003,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, if arg.typ == nil: result = arg elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or - m.inheritancePenalty > 0: + m.inheritancePenalty > oldInheritancePenalty: result = implicitConv(nkHiddenSubConv, f, arg, m, c) elif arg.typ.isEmptyContainer: result = arg.copyTree @@ -2131,6 +2132,10 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, styleCheckUse(arg.info, arg.sons[best].sym) result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best], argOrig) + when false: + if m.calleeSym != nil and m.calleeSym.name.s == "[]": + echo m.c.config $ arg.info, " for ", m.calleeSym.name.s, " ", m.c.config $ m.calleeSym.info + writeMatches(m) proc setSon(father: PNode, at: int, son: PNode) = let oldLen = father.len @@ -2226,12 +2231,20 @@ proc matchesAux(c: PContext, n, nOrig: PNode, if a >= formalLen-1 and f < formalLen and m.callee.n[f].typ.isVarargsUntyped: formal = m.callee.n.sons[f].sym incl(marker, formal.position) - if container.isNil: - container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info)) - setSon(m.call, formal.position + 1, container) + + if n.sons[a].kind == nkHiddenStdConv: + doAssert n.sons[a].sons[0].kind == nkEmpty and + n.sons[a].sons[1].kind == nkArgList and + n.sons[a].sons[1].len == 0 + # Steal the container and pass it along + setSon(m.call, formal.position + 1, n.sons[a].sons[1]) else: - incrIndexType(container.typ) - addSon(container, n.sons[a]) + if container.isNil: + container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info)) + setSon(m.call, formal.position + 1, container) + else: + incrIndexType(container.typ) + addSon(container, n.sons[a]) elif n.sons[a].kind == nkExprEqExpr: # named param # check if m.callee has such a param: @@ -2239,11 +2252,13 @@ proc matchesAux(c: PContext, n, nOrig: PNode, if n.sons[a].sons[0].kind != nkIdent: localError(c.config, n.sons[a].info, "named parameter has to be an identifier") m.state = csNoMatch + m.firstMismatch = -a return formal = getSymFromList(m.callee.n, n.sons[a].sons[0].ident, 1) if formal == nil: # no error message! m.state = csNoMatch + m.firstMismatch = -a return if containsOrIncl(marker, formal.position): # already in namedParams, so no match @@ -2261,6 +2276,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, n.sons[a].sons[1], n.sons[a].sons[1]) if arg == nil: m.state = csNoMatch + m.firstMismatch = a return checkConstraint(n.sons[a].sons[1]) if m.baseTypeMatch: @@ -2379,6 +2395,11 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = if m.magic in {mArrGet, mArrPut}: m.state = csMatch m.call = n + # Note the following doesn't work as it would produce ambiguities. + # Instead we patch system.nim, see bug #8049. + when false: + inc m.genericMatches + inc m.exactMatches return var marker = initIntSet() matchesAux(c, n, nOrig, m, marker) @@ -2390,7 +2411,10 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = if not containsOrIncl(marker, formal.position): if formal.ast == nil: if formal.typ.kind == tyVarargs: - var container = newNodeIT(nkBracket, n.info, arrayConstr(c, n.info)) + # For consistency with what happens in `matchesAux` select the + # container node kind accordingly + let cnKind = if formal.typ.isVarargsUntyped: nkArgList else: nkBracket + var container = newNodeIT(cnKind, n.info, arrayConstr(c, n.info)) setSon(m.call, formal.position + 1, implicitConv(nkHiddenStdConv, formal.typ, container, m, c)) else: diff --git a/compiler/suggest.nim b/compiler/suggest.nim index b52632c67..f99a2d432 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -47,7 +47,7 @@ template origModuleName(m: PSym): string = m.name.s proc findDocComment(n: PNode): PNode = if n == nil: return nil - if not isNil(n.comment): return n + if n.comment.len > 0: return n if n.kind in {nkStmtList, nkStmtListExpr, nkObjectTy, nkRecList} and n.len > 0: result = findDocComment(n.sons[0]) if result != nil: return @@ -434,7 +434,7 @@ proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym; ## misnamed: should be 'symDeclared' when defined(nimsuggest): if conf.suggestVersion == 0: - if s.allUsages.isNil: + if s.allUsages.len == 0: s.allUsages = @[info] else: s.addNoDup(info) diff --git a/compiler/tccgen.nim b/compiler/tccgen.nim index ea0fb590f..2301ad404 100644 --- a/compiler/tccgen.nim +++ b/compiler/tccgen.nim @@ -39,24 +39,24 @@ proc setupEnvironment = addIncludePath(gTinyC, libpath) when defined(windows): - addSysincludePath(gTinyC, nimrodDir / "tinyc/win32/include") - addSysincludePath(gTinyC, nimrodDir / "tinyc/include") + addSysincludePath(gTinyC, nimDir / "tinyc/win32/include") + addSysincludePath(gTinyC, nimDir / "tinyc/include") when defined(windows): defineSymbol(gTinyC, "_WIN32", nil) # we need Mingw's headers too: var gccbin = getConfigVar("gcc.path") % ["nim", nimDir] addSysincludePath(gTinyC, gccbin /../ "include") - #addFile(nimrodDir / r"tinyc\win32\wincrt1.o") - addFile(nimrodDir / r"tinyc\win32\alloca86.o") - addFile(nimrodDir / r"tinyc\win32\chkstk.o") - #addFile(nimrodDir / r"tinyc\win32\crt1.o") + #addFile(nimDir / r"tinyc\win32\wincrt1.o") + addFile(nimDir / r"tinyc\win32\alloca86.o") + addFile(nimDir / r"tinyc\win32\chkstk.o") + #addFile(nimDir / r"tinyc\win32\crt1.o") - #addFile(nimrodDir / r"tinyc\win32\dllcrt1.o") - #addFile(nimrodDir / r"tinyc\win32\dllmain.o") - addFile(nimrodDir / r"tinyc\win32\libtcc1.o") + #addFile(nimDir / r"tinyc\win32\dllcrt1.o") + #addFile(nimDir / r"tinyc\win32\dllmain.o") + addFile(nimDir / r"tinyc\win32\libtcc1.o") - #addFile(nimrodDir / r"tinyc\win32\lib\crt1.c") - #addFile(nimrodDir / r"tinyc\lib\libtcc1.c") + #addFile(nimDir / r"tinyc\win32\lib\crt1.c") + #addFile(nimDir / r"tinyc\lib\libtcc1.c") else: addSysincludePath(gTinyC, "/usr/include") when defined(amd64): diff --git a/compiler/transf.nim b/compiler/transf.nim index 3a276dc38..84297aa6a 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -1050,8 +1050,8 @@ proc transformStmt*(g: ModuleGraph; module: PSym, n: PNode): PNode = when useEffectSystem: trackTopLevelStmt(g, module, result) #if n.info ?? "temp.nim": # echo renderTree(result, {renderIds}) - if c.needsDestroyPass: - result = injectDestructorCalls(g, module, result) + #if c.needsDestroyPass: + # result = injectDestructorCalls(g, module, result) incl(result.flags, nfTransf) proc transformExpr*(g: ModuleGraph; module: PSym, n: PNode): PNode = @@ -1063,6 +1063,6 @@ proc transformExpr*(g: ModuleGraph; module: PSym, n: PNode): PNode = liftDefer(c, result) # expressions are not to be injected with destructor calls as that # the list of top level statements needs to be collected before. - if c.needsDestroyPass: - result = injectDestructorCalls(g, module, result) + #if c.needsDestroyPass: + # result = injectDestructorCalls(g, module, result) incl(result.flags, nfTransf) diff --git a/compiler/treetab.nim b/compiler/treetab.nim index e6eb8c666..f15974f61 100644 --- a/compiler/treetab.nim +++ b/compiler/treetab.nim @@ -29,8 +29,7 @@ proc hashTree(n: PNode): Hash = if (n.floatVal >= - 1000000.0) and (n.floatVal <= 1000000.0): result = result !& toInt(n.floatVal) of nkStrLit..nkTripleStrLit: - if not n.strVal.isNil: - result = result !& hash(n.strVal) + result = result !& hash(n.strVal) else: for i in countup(0, sonsLen(n) - 1): result = result !& hashTree(n.sons[i]) diff --git a/compiler/types.nim b/compiler/types.nim index d065ae29a..d0eec35cf 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -278,6 +278,8 @@ proc analyseObjectWithTypeField(t: PType): TTypeFieldResult = proc isGCRef(t: PType): bool = result = t.kind in GcTypeKinds or (t.kind == tyProc and t.callConv == ccClosure) + if result and t.kind in {tyString, tySequence} and tfHasAsgn in t.flags: + result = false proc containsGarbageCollectedRef*(typ: PType): bool = # returns true if typ contains a reference, sequence or string (all the @@ -759,9 +761,10 @@ proc initSameTypeClosure: TSameTypeClosure = discard proc containsOrIncl(c: var TSameTypeClosure, a, b: PType): bool = - result = not isNil(c.s) and c.s.contains((a.id, b.id)) + result = c.s.len > 0 and c.s.contains((a.id, b.id)) if not result: - if isNil(c.s): c.s = @[] + when not defined(nimNoNilSeqs): + if isNil(c.s): c.s = @[] c.s.add((a.id, b.id)) proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool @@ -1341,14 +1344,23 @@ proc computeSizeAux(conf: ConfigRef; typ: PType, a: var BiggestInt): BiggestInt if typ.callConv == ccClosure: result = 2 * conf.target.ptrSize else: result = conf.target.ptrSize a = conf.target.ptrSize - of tyString, tyNil: + of tyString: + if tfHasAsgn in typ.flags: + result = conf.target.ptrSize * 2 + else: + result = conf.target.ptrSize + of tyNil: result = conf.target.ptrSize a = result of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent, tyOpenArray: let base = typ.lastSon if base == typ or (base.kind == tyTuple and base.size==szIllegalRecursion): result = szIllegalRecursion - else: result = conf.target.ptrSize + else: + if typ.kind == tySequence and tfHasAsgn in typ.flags: + result = conf.target.ptrSize * 2 + else: + result = conf.target.ptrSize a = result of tyArray: let elemSize = computeSizeAux(conf, typ.sons[1], a) diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim index 4d75d5d05..0c4fe01e1 100644 --- a/compiler/typesrenderer.nim +++ b/compiler/typesrenderer.nim @@ -29,7 +29,6 @@ proc renderPlainSymbolName*(n: PNode): string = else: result = "" #internalError(n.info, "renderPlainSymbolName() with " & $n.kind) - assert(not result.isNil) proc renderType(n: PNode): string = ## Returns a string with the node type or the empty string. @@ -80,7 +79,6 @@ proc renderType(n: PNode): string = for i in 1 ..< len(n): result.add(renderType(n[i]) & ',') result[len(result)-1] = ']' else: result = "" - assert(not result.isNil) proc renderParamTypes(found: var seq[string], n: PNode) = diff --git a/compiler/vm.nim b/compiler/vm.nim index c49b66b82..e38642de8 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -81,14 +81,16 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) = msgWriteln(c.config, s) proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, - msg: string, n: PNode = nil) = + msg: string, lineInfo: TLineInfo) = msgWriteln(c.config, "stack trace: (most recent call last)") stackTraceAux(c, tos, pc) # XXX test if we want 'globalError' for every mode - let lineInfo = if n == nil: c.debug[pc] else: n.info if c.mode == emRepl: globalError(c.config, lineInfo, msg) else: localError(c.config, lineInfo, msg) +proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, msg: string) = + stackTrace(c, tos, pc, msg, c.debug[pc]) + proc bailOut(c: PCtx; tos: PStackFrame) = stackTrace(c, tos, c.exceptionInstr, "unhandled exception: " & c.currentExceptionA.sons[3].skipColon.strVal) @@ -242,7 +244,8 @@ template getstr(a: untyped): untyped = (if a.kind == rkNode: a.node.strVal else: $chr(int(a.intVal))) proc pushSafePoint(f: PStackFrame; pc: int) = - if f.safePoints.isNil: f.safePoints = @[] + when not defined(nimNoNilSeqs): + if f.safePoints.isNil: f.safePoints = @[] f.safePoints.add(pc) proc popSafePoint(f: PStackFrame) = @@ -255,7 +258,7 @@ proc cleanUpOnException(c: PCtx; tos: PStackFrame): let raisedType = c.currentExceptionA.typ.skipTypes(abstractPtrs) var f = tos while true: - while f.safePoints.isNil or f.safePoints.len == 0: + while f.safePoints.len == 0: f = f.next if f.isNil: return (-1, nil) var pc2 = f.safePoints[f.safePoints.high] @@ -270,7 +273,7 @@ proc cleanUpOnException(c: PCtx; tos: PStackFrame): abstractPtrs) else: nil #echo typeToString(exceptType), " ", typeToString(raisedType) - if exceptType.isNil or inheritanceDiff(exceptType, raisedType) <= 0: + if exceptType.isNil or inheritanceDiff(raisedType, exceptType) <= 0: # mark exception as handled but keep it in B for # the getCurrentException() builtin: c.currentExceptionB = c.currentExceptionA @@ -297,7 +300,6 @@ proc cleanUpOnException(c: PCtx; tos: PStackFrame): discard f.safePoints.pop proc cleanUpOnReturn(c: PCtx; f: PStackFrame): int = - if f.safePoints.isNil: return -1 for s in f.safePoints: var pc = s while c.code[pc].opcode == opcExcept: @@ -531,9 +533,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeBC(rkInt) let idx = regs[rc].intVal.int let s = regs[rb].node.strVal - if s.isNil: - stackTrace(c, tos, pc, errNilAccess) - elif idx <% s.len: + if idx <% s.len: regs[ra].intVal = s[idx].ord elif idx == s.len and optLaxStrings in c.config.options: regs[ra].intVal = 0 @@ -820,7 +820,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].intVal = ord((regs[rb].node.kind == nkNilLit and regs[rc].node.kind == nkNilLit) or regs[rb].node == regs[rc].node) - of opcEqNimrodNode: + of opcEqNimNode: decodeBC(rkInt) regs[ra].intVal = ord(exprStructuralEquivalent(regs[rb].node, regs[rc].node, @@ -920,6 +920,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.flags.incl nfIsRef else: stackTrace(c, tos, pc, "node is not a symbol") + of opcSymOwner: + decodeB(rkNode) + let a = regs[rb].node + if a.kind == nkSym: + regs[ra].node = if a.sym.owner.isNil: newNode(nkNilLit) + else: newSymNode(a.sym.skipGenericOwner) + regs[ra].node.flags.incl nfIsRef + else: + stackTrace(c, tos, pc, "node is not a symbol") of opcEcho: let rb = instr.regB if rb == 1: @@ -1220,7 +1229,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = # Note that `nfIsRef` + `nkNilLit` represents an allocated # reference with the value `nil`, so `isNil` should be false! (node.kind == nkNilLit and nfIsRef notin node.flags) or - (node.kind in {nkStrLit..nkTripleStrLit} and node.strVal.isNil) or (not node.typ.isNil and node.typ.kind == tyProc and node.typ.callConv == ccClosure and node.sons[0].kind == nkNilLit and node.sons[1].kind == nkNilLit)) @@ -1325,6 +1333,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = ensureKind(rkNode) if regs[rb].kind == rkNode and regs[rb].node.typ != nil: regs[ra].node = opMapTypeToAst(c.cache, regs[rb].node.typ, c.debug[pc]) + elif regs[rb].kind == rkNode and regs[rb].node.kind == nkSym and regs[rb].node.sym.typ != nil: + regs[ra].node = opMapTypeToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc]) else: stackTrace(c, tos, pc, "node has no type") of 1: @@ -1332,6 +1342,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = ensureKind(rkInt) if regs[rb].kind == rkNode and regs[rb].node.typ != nil: regs[ra].intVal = ord(regs[rb].node.typ.kind) + elif regs[rb].kind == rkNode and regs[rb].node.kind == nkSym and regs[rb].node.sym.typ != nil: + regs[ra].intVal = ord(regs[rb].node.sym.typ.kind) #else: # stackTrace(c, tos, pc, "node has no type") of 2: @@ -1339,6 +1351,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = ensureKind(rkNode) if regs[rb].kind == rkNode and regs[rb].node.typ != nil: regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.typ, c.debug[pc]) + elif regs[rb].kind == rkNode and regs[rb].node.kind == nkSym and regs[rb].node.sym.typ != nil: + regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc]) else: stackTrace(c, tos, pc, "node has no type") else: @@ -1346,6 +1360,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = ensureKind(rkNode) if regs[rb].kind == rkNode and regs[rb].node.typ != nil: regs[ra].node = opMapTypeImplToAst(c.cache, regs[rb].node.typ, c.debug[pc]) + elif regs[rb].kind == rkNode and regs[rb].node.kind == nkSym and regs[rb].node.sym.typ != nil: + regs[ra].node = opMapTypeImplToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc]) else: stackTrace(c, tos, pc, "node has no type") of opcNStrVal: @@ -1380,15 +1396,17 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = c.debug[pc], c.config)[0] else: globalError(c.config, c.debug[pc], "VM is not built with 'gorge' support") - of opcNError: + of opcNError, opcNWarning, opcNHint: decodeB(rkNode) let a = regs[ra].node let b = regs[rb].node - stackTrace(c, tos, pc, a.strVal, if b.kind == nkNilLit: nil else: b) - of opcNWarning: - message(c.config, c.debug[pc], warnUser, regs[ra].node.strVal) - of opcNHint: - message(c.config, c.debug[pc], hintUser, regs[ra].node.strVal) + let info = if b.kind == nkNilLit: c.debug[pc] else: b.info + if instr.opcode == opcNError: + stackTrace(c, tos, pc, a.strVal, info) + elif instr.opcode == opcNWarning: + message(c.config, info, warnUser, a.strVal) + elif instr.opcode == opcNHint: + message(c.config, info, hintUser, a.strVal) of opcParseExprToAst: decodeB(rkNode) # c.debug[pc].line.int - countLines(regs[rb].strVal) ? @@ -1396,9 +1414,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let ast = parseString(regs[rb].node.strVal, c.cache, c.config, toFullPath(c.config, c.debug[pc]), c.debug[pc].line.int, proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) = - if error.isNil and msg <= errMax: + if error.len == 0 and msg <= errMax: error = formatMsg(conf, info, msg, arg)) - if not error.isNil: + if error.len > 0: c.errorFlag = error elif sonsLen(ast) != 1: c.errorFlag = formatMsg(c.config, c.debug[pc], errGenerated, @@ -1411,9 +1429,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let ast = parseString(regs[rb].node.strVal, c.cache, c.config, toFullPath(c.config, c.debug[pc]), c.debug[pc].line.int, proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) = - if error.isNil and msg <= errMax: + if error.len == 0 and msg <= errMax: error = formatMsg(conf, info, msg, arg)) - if not error.isNil: + if error.len > 0: c.errorFlag = error else: regs[ra].node = ast @@ -1726,7 +1744,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = inc pc let typ = c.types[c.code[pc].regBx - wordExcess] createStrKeepNode(regs[ra]) - if regs[ra].node.strVal.isNil: regs[ra].node.strVal = newStringOfCap(1000) + when not defined(nimNoNilSeqs): + if regs[ra].node.strVal.isNil: regs[ra].node.strVal = newStringOfCap(1000) storeAny(regs[ra].node.strVal, typ, regs[rb].regToNode, c.config) of opcToNarrowInt: decodeBC(rkInt) diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 1abd9ae4a..d642043dc 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -62,7 +62,7 @@ type opcBitandInt, opcBitorInt, opcBitxorInt, opcAddu, opcSubu, opcMulu, opcDivu, opcModu, opcEqInt, opcLeInt, opcLtInt, opcEqFloat, opcLeFloat, opcLtFloat, opcLeu, opcLtu, - opcEqRef, opcEqNimrodNode, opcSameNodeType, + opcEqRef, opcEqNimNode, opcSameNodeType, opcXor, opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt, opcEqStr, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet, opcMulSet, opcPlusSet, opcMinusSet, opcSymdiffSet, opcConcatStr, @@ -141,7 +141,8 @@ type opcSetType, # dest.typ = types[Bx] opcTypeTrait, opcMarshalLoad, opcMarshalStore, - opcToNarrowInt + opcToNarrowInt, + opcSymOwner TBlock* = object label*: PSym diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index a36f559ca..e87347ec8 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1118,6 +1118,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mStaticExec: genBinaryABCD(c, n, dest, opcGorge) of mNLen: genUnaryABI(c, n, dest, opcLenSeq, nimNodeFlag) of mGetImpl: genUnaryABC(c, n, dest, opcGetImpl) + of mSymOwner: genUnaryABC(c, n, dest, opcSymOwner) of mNChild: genBinaryABC(c, n, dest, opcNChild) of mNSetChild: genVoidABC(c, n, dest, opcNSetChild) of mNDel: genVoidABC(c, n, dest, opcNDel) @@ -1178,7 +1179,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mNBindSym: genBindSym(c, n, dest) of mStrToIdent: genUnaryABC(c, n, dest, opcStrToIdent) of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent) - of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode) + of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimNode) of mSameNodeType: genBinaryABC(c, n, dest, opcSameNodeType) of mNLineInfo: case n[0].sym.name.s @@ -1192,10 +1193,10 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = else: internalAssert c.config, false of mNHint: unused(c, n, dest) - genUnaryStmt(c, n, opcNHint) + genBinaryStmt(c, n, opcNHint) of mNWarning: unused(c, n, dest) - genUnaryStmt(c, n, opcNWarning) + genBinaryStmt(c, n, opcNWarning) of mNError: if n.len <= 1: # query error condition: diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim index eb01b3514..149d2e08f 100644 --- a/compiler/vmmarshal.nim +++ b/compiler/vmmarshal.nim @@ -127,7 +127,7 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet; storeAny(s, t.lastSon, a, stored, conf) s.add("]") of tyString, tyCString: - if a.kind == nkNilLit or a.strVal.isNil: s.add("null") + if a.kind == nkNilLit: s.add("null") else: s.add(escapeJson(a.strVal)) of tyInt..tyInt64, tyUInt..tyUInt64: s.add($a.intVal) of tyFloat..tyFloat128: s.add($a.floatVal) |