diff options
Diffstat (limited to 'compiler')
39 files changed, 650 insertions, 308 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index bb0f95acd..40a05e6bf 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -627,7 +627,7 @@ type mIsPartOf, mAstToStr, mParallel, mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mNewString, mNewStringOfCap, mParseBiggestFloat, - mMove, mWasMoved, + mMove, mWasMoved, mDestroy, mReset, mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, @@ -656,14 +656,15 @@ type mNHint, mNWarning, mNError, mInstantiationInfo, mGetTypeInfo, mNimvm, mIntDefine, mStrDefine, mRunnableExamples, - mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf + mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf, + mSymIsInstantiationOf # things that we can evaluate safely at compile time, even if not asked for it: const ctfeWhitelist* = {mNone, mUnaryLt, mSucc, mPred, mInc, mDec, mOrd, mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq, - mArrGet, mArrPut, mAsgn, + mArrGet, mArrPut, mAsgn, mDestroy, mIncl, mExcl, mCard, mChr, mAddI, mSubI, mMulI, mDivI, mModI, mAddF64, mSubF64, mMulF64, mDivF64, @@ -756,8 +757,6 @@ type OnUnknown, # location is unknown (stack, heap or static) OnStatic, # in a static section OnStack, # location is on hardware stack - OnStackShadowDup, # location is on the stack but also replicated - # on the shadow stack OnHeap # location is on heap or global # (reference counting needed) TLocFlags* = set[TLocFlag] @@ -767,7 +766,6 @@ type flags*: TLocFlags # location's flags lode*: PNode # Node where the location came from; can be faked r*: Rope # rope value of location (code generators) - dup*: Rope # duplicated location for precise stack scans # ---------------- end of backend information ------------------------------ @@ -898,6 +896,8 @@ type loc*: TLoc typeInst*: PType # for generic instantiations the tyGenericInst that led to this # type. + uniqueId*: int # due to a design mistake, we need to keep the real ID here as it + # required by the --incremental:on mode. TPair* = object key*, val*: RootRef @@ -1268,6 +1268,7 @@ proc newType*(kind: TTypeKind, owner: PSym): PType = result.size = -1 result.align = -1 # default alignment result.id = getID() + result.uniqueId = result.id result.lockLevel = UnspecifiedLockLevel when debugIds: registerId(result) @@ -1341,15 +1342,12 @@ proc copyType*(t: PType, owner: PSym, keepId: bool): PType = proc exactReplica*(t: PType): PType = copyType(t, t.owner, true) -proc copySym*(s: PSym, keepId: bool = false): PSym = +proc copySym*(s: PSym): PSym = result = newSym(s.kind, s.name, s.owner, s.info, s.options) #result.ast = nil # BUGFIX; was: s.ast which made problems result.typ = s.typ - if keepId: - result.id = s.id - else: - result.id = getID() - when debugIds: registerId(result) + result.id = getID() + when debugIds: registerId(result) result.flags = s.flags result.magic = s.magic if s.kind == skModule: diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index b716882dc..b2671d81e 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -254,21 +254,26 @@ proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet, indent: int, if n == nil: result = rope("null") elif containsOrIncl(marker, n.id): - result = "\"$1 @$2\"" % [rope(n.name.s), rope( - strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))] + result = "\"$1\"" % [rope(n.name.s)] else: var ast = treeToYamlAux(conf, n.ast, marker, indent + 2, maxRecDepth - 1) result = ropeConstr(indent, [rope("kind"), makeYamlString($n.kind), rope("name"), makeYamlString(n.name.s), - rope("typ"), typeToYamlAux(conf, n.typ, marker, - indent + 2, maxRecDepth - 1), + #rope("typ"), typeToYamlAux(conf, n.typ, marker, + # indent + 2, maxRecDepth - 1), rope("info"), lineInfoToStr(conf, n.info), rope("flags"), flagsToStr(n.flags), rope("magic"), makeYamlString($n.magic), rope("ast"), ast, rope("options"), flagsToStr(n.options), rope("position"), - rope(n.position)]) + rope(n.position), + rope("k"), makeYamlString($n.loc.k), + rope("storage"), makeYamlString($n.loc.storage), + rope("flags"), makeYamlString($n.loc.flags), + rope("r"), n.loc.r, + rope("lode"), treeToYamlAux(conf, n.loc.lode, marker, indent + 2, maxRecDepth - 1) + ]) proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int, maxRecDepth: int): Rope = @@ -394,10 +399,16 @@ proc debugTree(conf: ConfigRef; n: PNode, indent: int, maxRecDepth: int; of nkStrLit..nkTripleStrLit: 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)]) - # [istr, symToYaml(n.sym, indent, maxRecDepth), - # rope(n.sym.id)]) + let s = n.sym + addf(result, ",$N$1\"sym\": $2_$3 k: $4 storage: $5 flags: $6 r: $7", + [istr, rope(s.name.s), rope(s.id), + rope($s.loc.k), + rope($s.loc.storage), + rope($s.loc.flags), + s.loc.r + ]) +# [istr, symToYaml(conf, n.sym, indent, maxRecDepth), +# rope(n.sym.id)]) if renderType and n.sym.typ != nil: addf(result, ",$N$1\"typ\": $2", [istr, debugType(conf, n.sym.typ, 2)]) of nkIdent: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 1b70ee41b..59ef05f9c 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -31,10 +31,18 @@ proc intLiteral(i: BiggestInt): Rope = result = ~"(IL64(-9223372036854775807) - IL64(1))" proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = - if ty == nil: internalError(p.config, n.info, "genLiteral: ty is nil") case n.kind of nkCharLit..nkUInt64Lit: - case skipTypes(ty, abstractVarRange).kind + var k: TTypeKind + if ty != nil: + k = skipTypes(ty, abstractVarRange).kind + else: + case n.kind + of nkCharLit: k = tyChar + of nkUInt64Lit: k = tyUInt64 + of nkInt64Lit: k = tyInt64 + else: k = tyNil # don't go into the case variant that uses 'ty' + case k of tyChar, tyNil: result = intLiteral(n.intVal) of tyBool: @@ -46,8 +54,8 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = result = "(($1) $2)" % [getTypeDesc(p.module, ty), intLiteral(n.intVal)] of nkNilLit: - let t = skipTypes(ty, abstractVarRange) - if t.kind == tyProc and t.callConv == ccClosure: + let k = if ty == nil: tyPointer else: skipTypes(ty, abstractVarRange).kind + if k == tyProc and skipTypes(ty, abstractVarRange).callConv == ccClosure: let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) result = p.module.tmpBase & rope(id) if id == p.module.labels: @@ -59,7 +67,9 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = else: result = rope("NIM_NIL") of nkStrLit..nkTripleStrLit: - case skipTypes(ty, abstractVarRange + {tyStatic, tyUserTypeClass, tyUserTypeClassInst}).kind + let k = if ty == nil: tyString + else: skipTypes(ty, abstractVarRange + {tyStatic, tyUserTypeClass, tyUserTypeClassInst}).kind + case k of tyNil: result = genNilStringLiteral(p.module, n.info) of tyString: @@ -168,32 +178,12 @@ proc canMove(p: BProc, n: PNode): bool = # echo n.info, " optimized ", n # result = false -proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = +proc genRefAssign(p: BProc, dest, src: TLoc) = if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config): linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) elif dest.storage == OnHeap: - # location is on heap - # now the writer barrier is inlined for performance: - # - # if afSrcIsNotNil in flags: - # UseMagic(p.module, 'nimGCref') - # lineF(p, cpsStmts, 'nimGCref($1);$n', [rdLoc(src)]) - # elif afSrcIsNil notin flags: - # UseMagic(p.module, 'nimGCref') - # lineF(p, cpsStmts, 'if ($1) nimGCref($1);$n', [rdLoc(src)]) - # if afDestIsNotNil in flags: - # UseMagic(p.module, 'nimGCunref') - # lineF(p, cpsStmts, 'nimGCunref($1);$n', [rdLoc(dest)]) - # elif afDestIsNil notin flags: - # UseMagic(p.module, 'nimGCunref') - # lineF(p, cpsStmts, 'if ($1) nimGCunref($1);$n', [rdLoc(dest)]) - # lineF(p, cpsStmts, '$1 = $2;$n', [rdLoc(dest), rdLoc(src)]) - if canFormAcycle(dest.t): - linefmt(p, cpsStmts, "#asgnRef((void**) $1, $2);$n", - addrLoc(p.config, dest), rdLoc(src)) - else: - linefmt(p, cpsStmts, "#asgnRefNoCycle((void**) $1, $2);$n", - addrLoc(p.config, dest), rdLoc(src)) + linefmt(p, cpsStmts, "#asgnRef((void**) $1, $2);$n", + addrLoc(p.config, dest), rdLoc(src)) else: linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", addrLoc(p.config, dest), rdLoc(src)) @@ -261,7 +251,7 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = # (for objects, etc.): if p.config.selectedGC == gcDestructors: linefmt(p, cpsStmts, - "$1.len = $2.len; $1.p = $2.p;$n", + "$1 = $2;$n", rdLoc(dest), rdLoc(src)) elif needToCopy notin flags or tfShallow in skipTypes(dest.t, abstractVarRange).flags: @@ -286,12 +276,12 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = let ty = skipTypes(dest.t, abstractRange + tyUserTypeClasses + {tyStatic}) case ty.kind of tyRef: - genRefAssign(p, dest, src, flags) + genRefAssign(p, dest, src) of tySequence: if p.config.selectedGC == gcDestructors: genGenericAsgn(p, dest, src, flags) elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode): - genRefAssign(p, dest, src, flags) + genRefAssign(p, dest, src) else: linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n", addrLoc(p.config, dest), rdLoc(src), @@ -300,7 +290,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = if p.config.selectedGC == gcDestructors: genGenericAsgn(p, dest, src, flags) elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode): - genRefAssign(p, dest, src, flags) + genRefAssign(p, dest, src) else: if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config): linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc) @@ -315,16 +305,16 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n", addrLoc(p.config, dest), rdLoc(src)) of tyProc: - if needsComplexAssignment(dest.t): + if containsGarbageCollectedRef(dest.t): # optimize closure assignment: let a = optAsgnLoc(dest, dest.t, "ClE_0".rope) let b = optAsgnLoc(src, dest.t, "ClE_0".rope) - genRefAssign(p, a, b, flags) + genRefAssign(p, a, b) linefmt(p, cpsStmts, "$1.ClP_0 = $2.ClP_0;$n", rdLoc(dest), rdLoc(src)) else: linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) of tyTuple: - if needsComplexAssignment(dest.t): + if containsGarbageCollectedRef(dest.t): if dest.t.len <= 4: genOptAsgnTuple(p, dest, src, flags) else: genGenericAsgn(p, dest, src, flags) else: @@ -335,7 +325,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) elif not isObjLackingTypeField(ty): genGenericAsgn(p, dest, src, flags) - elif needsComplexAssignment(ty): + elif containsGarbageCollectedRef(ty): if ty.sons[0].isNil and asgnComplexity(ty.n) <= 4: discard getTypeDesc(p.module, ty) internalAssert p.config, ty.n != nil @@ -345,7 +335,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = else: linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) of tyArray: - if needsComplexAssignment(dest.t): + if containsGarbageCollectedRef(dest.t): genGenericAsgn(p, dest, src, flags) else: linefmt(p, cpsStmts, @@ -354,7 +344,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = of tyOpenArray, tyVarargs: # open arrays are always on the stack - really? What if a sequence is # passed to an open array? - if needsComplexAssignment(dest.t): + if containsGarbageCollectedRef(dest.t): linefmt(p, cpsStmts, # XXX: is this correct for arrays? "#genericAssignOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n", addrLoc(p.config, dest), addrLoc(p.config, src), @@ -1148,14 +1138,14 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = genTypeInfo(p.module, seqType, e.info)]) # emit the write barrier if required, but we can always move here, so # use 'genRefAssign' for the seq. - genRefAssign(p, a, call, {}) + genRefAssign(p, a, call) #if bt != b.t: # echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t) initLoc(dest, locExpr, e.sons[2], OnHeap) getIntTemp(p, tmpL) lineCg(p, cpsStmts, "$1 = $2->$3++;$n", tmpL.r, rdLoc(a), lenField(p)) dest.r = ropecg(p.module, "$1$3[$2]", rdLoc(a), tmpL.r, dataField(p)) - genAssignment(p, dest, b, {needToCopy, afDestIsNil}) + genAssignment(p, dest, b, {needToCopy}) gcUsage(p.config, e) proc genReset(p: BProc, n: PNode) = @@ -1359,9 +1349,17 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) = getTemp(p, n.typ, tmp) elif d.k == locNone: getTemp(p, n.typ, d) - # generate call to newSeq before adding the elements per hand: - genNewSeqAux(p, dest[], intLiteral(sonsLen(n)), - optNilSeqs notin p.options and n.len == 0) + + let l = intLiteral(sonsLen(n)) + if p.config.selectedGC == gcDestructors: + let seqtype = n.typ + linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n", + rdLoc dest[], l, getTypeDesc(p.module, seqtype.lastSon), + getSeqPayloadType(p.module, seqtype)) + else: + # generate call to newSeq before adding the elements per hand: + genNewSeqAux(p, dest[], l, + optNilSeqs notin p.options and n.len == 0) for i in countup(0, sonsLen(n) - 1): initLoc(arr, locExpr, n[i], OnHeap) arr.r = ropecg(p.module, "$1$3[$2]", rdLoc(dest[]), intLiteral(i), dataField(p)) @@ -1384,7 +1382,13 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = getTemp(p, n.typ, d) # generate call to newSeq before adding the elements per hand: let L = int(lengthOrd(p.config, n.sons[1].typ)) - genNewSeqAux(p, d, intLiteral(L), optNilSeqs notin p.options and L == 0) + if p.config.selectedGC == gcDestructors: + let seqtype = n.typ + linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n", + rdLoc d, rope L, getTypeDesc(p.module, seqtype.lastSon), + getSeqPayloadType(p.module, seqtype)) + else: + genNewSeqAux(p, d, intLiteral(L), optNilSeqs notin p.options and L == 0) initLocExpr(p, n.sons[1], a) # bug #5007; do not produce excessive C source code: if L < 10: @@ -1394,7 +1398,7 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = elem.storage = OnHeap # we know that sequences are on the heap initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage) arr.r = ropecg(p.module, "$1[$2]", rdLoc(a), intLiteral(i)) - genAssignment(p, elem, arr, {afDestIsNil, needToCopy}) + genAssignment(p, elem, arr, {needToCopy}) else: var i: TLoc getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), i) @@ -1405,7 +1409,7 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = elem.storage = OnHeap # we know that sequences are on the heap initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage) arr.r = ropecg(p.module, "$1[$2]", rdLoc(a), rdLoc(i)) - genAssignment(p, elem, arr, {afDestIsNil, needToCopy}) + genAssignment(p, elem, arr, {needToCopy}) lineF(p, cpsStmts, "}$n", []) @@ -2054,6 +2058,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mDotDot, mEqCString: genCall(p, e, d) of mWasMoved: genWasMoved(p, e) of mMove: genMove(p, e, d) + of mDestroy: discard "ignore calls to the default destructor" of mSlice: localError(p.config, e.info, "invalid context for 'toOpenArray'; " & " 'toOpenArray' is only valid within a call expression") diff --git a/compiler/ccgliterals.nim b/compiler/ccgliterals.nim index ccfa49a1d..904d01e81 100644 --- a/compiler/ccgliterals.nim +++ b/compiler/ccgliterals.nim @@ -54,7 +54,7 @@ proc genStringLiteralV1(m: BModule; n: PNode): Rope = proc genStringLiteralDataOnlyV2(m: BModule, s: string): Rope = result = getTempName(m) addf(m.s[cfsData], "static const struct {$n" & - " NI cap; void* allocator; NIM_CHAR data[$2];$n" & + " NI cap; void* allocator; NIM_CHAR data[$2+1];$n" & "} $1 = { $2, NIM_NIL, $3 };$n", [result, rope(len(s)), makeCString(s)]) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 3665a7e85..e83b80b7c 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -307,11 +307,13 @@ proc genSingleVar(p: BProc, a: PNode) = proc genClosureVar(p: BProc, a: PNode) = var immediateAsgn = a.sons[2].kind != nkEmpty + var v: TLoc + initLocExpr(p, a.sons[0], v) + genLineDir(p, a) if immediateAsgn: - var v: TLoc - initLocExpr(p, a.sons[0], v) - genLineDir(p, a) loadInto(p, a.sons[0], a.sons[2], v) + else: + constructLoc(p, v) proc genVarStmt(p: BProc, n: PNode) = for it in n.sons: @@ -609,7 +611,7 @@ proc genRaiseStmt(p: BProc, t: PNode) = lineF(p, cpsStmts, "throw $1;$n", [e]) else: lineCg(p, cpsStmts, "#raiseExceptionEx((#Exception*)$1, $2, $3, $4, $5);$n", - [e, makeCString(typ.sym.name.s), + [e, makeCString(typ.sym.name.s), makeCString(if p.prc != nil: p.prc.name.s else: p.module.module.name.s), makeCString(toFileName(p.config, t.info)), rope(toLinenumber(t.info))]) else: diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index bbfd72354..266f63647 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -193,8 +193,6 @@ proc isImportedCppType(t: PType): bool = (x.sym != nil and sfInfixCall in x.sym.flags) proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope -proc needsComplexAssignment(typ: PType): bool = - result = containsGarbageCollectedRef(typ) proc isObjLackingTypeField(typ: PType): bool {.inline.} = result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and @@ -214,7 +212,7 @@ proc isInvalidReturnType(conf: ConfigRef; rettype: PType): bool = of ctStruct: let t = skipTypes(rettype, typedescInst) if rettype.isImportedCppType or t.isImportedCppType: return false - result = needsComplexAssignment(t) or + result = containsGarbageCollectedRef(t) or (t.kind == tyObject and not isObjLackingTypeField(t)) else: result = false diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 071cd6446..3545edc88 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -294,10 +294,10 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc, type TAssignmentFlag = enum - needToCopy, afDestIsNil, afDestIsNotNil, afSrcIsNil, afSrcIsNotNil + needToCopy TAssignmentFlags = set[TAssignmentFlag] -proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) +proc genRefAssign(p: BProc, dest, src: TLoc) proc isComplexValueType(t: PType): bool {.inline.} = let t = t.skipTypes(abstractInst + tyUserTypeClasses) @@ -313,7 +313,7 @@ proc resetLoc(p: BProc, loc: var TLoc) = var nilLoc: TLoc initLoc(nilLoc, locTemp, loc.lode, OnStack) nilLoc.r = rope("NIM_NIL") - genRefAssign(p, loc, nilLoc, {afSrcIsNil}) + genRefAssign(p, loc, nilLoc) else: linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc)) else: @@ -1490,7 +1490,7 @@ proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool = result = true if optForceFullMake notin m.config.globalOptions: if not equalsFile(code, cfile.cname): - if isDefined(m.config, "nimdiff"): + if m.config.symbolFiles == readOnlySf: #isDefined(m.config, "nimdiff"): if fileExists(cfile.cname): copyFile(cfile.cname.string, cfile.cname.string & ".backup") echo "diff ", cfile.cname.string, ".backup ", cfile.cname.string diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index c2d908193..5ded6d054 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -1009,7 +1009,7 @@ proc stateFromGotoState(n: PNode): int = assert(n.kind == nkGotoState) result = n[0].intVal.int -proc tranformStateAssignments(ctx: var Ctx, n: PNode): PNode = +proc transformStateAssignments(ctx: var Ctx, n: PNode): PNode = # This transforms 3 patterns: ########################## 1 # yield e @@ -1051,7 +1051,7 @@ proc tranformStateAssignments(ctx: var Ctx, n: PNode): PNode = result.add(retStmt) else: for i in 0 ..< n.len: - n[i] = ctx.tranformStateAssignments(n[i]) + n[i] = ctx.transformStateAssignments(n[i]) of nkSkip: discard @@ -1071,7 +1071,7 @@ proc tranformStateAssignments(ctx: var Ctx, n: PNode): PNode = else: for i in 0 ..< n.len: - n[i] = ctx.tranformStateAssignments(n[i]) + n[i] = ctx.transformStateAssignments(n[i]) proc skipStmtList(ctx: Ctx; n: PNode): PNode = result = n @@ -1220,18 +1220,20 @@ proc wrapIntoStateLoop(ctx: var Ctx, n: PNode): PNode = # while true: # block :stateLoop: # gotoState :state + # local vars decl (if needed) # body # Might get wrapped in try-except let loopBody = newNodeI(nkStmtList, n.info) result = newTree(nkWhileStmt, newSymNode(ctx.g.getSysSym(n.info, "true")), loopBody) result.info = n.info + let localVars = newNodeI(nkStmtList, n.info) if not ctx.stateVarSym.isNil: let varSect = newNodeI(nkVarSection, n.info) addVar(varSect, newSymNode(ctx.stateVarSym)) - loopBody.add(varSect) + localVars.add(varSect) if not ctx.tempVars.isNil: - loopBody.add(ctx.tempVars) + localVars.add(ctx.tempVars) let blockStmt = newNodeI(nkBlockStmt, n.info) blockStmt.add(newSymNode(ctx.stateLoopLabel)) @@ -1240,7 +1242,7 @@ proc wrapIntoStateLoop(ctx: var Ctx, n: PNode): PNode = gs.add(ctx.newStateAccess()) gs.add(ctx.g.newIntLit(n.info, ctx.states.len - 1)) - var blockBody = newTree(nkStmtList, gs, n) + var blockBody = newTree(nkStmtList, gs, localVars, n) if ctx.hasExceptions: blockBody = ctx.wrapIntoTryExcept(blockBody) @@ -1292,7 +1294,6 @@ proc transformClosureIterator*(g: ModuleGraph; fn: PSym, n: PNode): PNode = # should folllow the same logic. ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), fn, fn.info) ctx.stateVarSym.typ = g.createClosureIterStateType(fn) - ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), fn, fn.info) var n = n.toStmtList @@ -1320,7 +1321,7 @@ proc transformClosureIterator*(g: ModuleGraph; fn: PSym, n: PNode): PNode = result.add(s) result.add(body) - result = ctx.tranformStateAssignments(result) + result = ctx.transformStateAssignments(result) result = ctx.wrapIntoStateLoop(result) # echo "TRANSFORM TO STATES: " diff --git a/compiler/commands.nim b/compiler/commands.nim index 14141696f..fa17e9851 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -642,14 +642,19 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "help", "h": expectNoArg(conf, switch, arg, pass, info) helpOnError(conf, pass) - of "symbolfiles", "incremental": + of "symbolfiles": discard "ignore for backwards compat" + of "incremental": + when not defined(nimIncremental): + localError(conf, info, "the compiler was not built with " & + "incremental compilation features; bootstrap with " & + "-d:nimIncremental to enable") case arg.normalize of "on": conf.symbolFiles = v2Sf of "off": conf.symbolFiles = disabledSf of "writeonly": conf.symbolFiles = writeOnlySf of "readonly": conf.symbolFiles = readOnlySf of "v2": conf.symbolFiles = v2Sf - else: localError(conf, info, "invalid option for --symbolFiles: " & arg) + else: localError(conf, info, "invalid option for --incremental: " & arg) of "skipcfg": expectNoArg(conf, switch, arg, pass, info) incl(conf.globalOptions, optSkipSystemConfigFile) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index ab89481b8..9a4c1701c 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -86,7 +86,7 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimUncheckedArrayTyp") defineSymbol("nimHasTypeof") defineSymbol("nimErrorProcCanHaveBody") - + defineSymbol("nimHasInstantiationOfInMacro") defineSymbol("nimHasNilSeqs") for f in low(Feature)..high(Feature): defineSymbol("nimHas" & $f) diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index b621e99b9..51ad26f2c 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -117,7 +117,7 @@ Remarks: Rule 1.2 is not yet implemented because ``sink`` is currently import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, strutils, options, dfa, lowerings, tables, modulegraphs, - lineinfos + lineinfos, parampatterns const InterestingSyms = {skVar, skResult, skLet} @@ -261,6 +261,11 @@ proc isLastRead(n: PNode; c: var Con): bool = template interestingSym(s: PSym): bool = s.owner == c.owner and s.kind in InterestingSyms and hasDestructor(s.typ) +template isUnpackedTuple(s: PSym): bool = + ## we move out all elements of unpacked tuples, + ## hence unpacked tuples themselves don't need to be destroyed + s.kind == skTemp and s.typ.kind == tyTuple + proc patchHead(n: PNode) = if n.kind in nkCallKinds and n[0].kind == nkSym and n.len > 1: let s = n[0].sym @@ -397,9 +402,10 @@ proc passCopyToSink(n: PNode; c: var Con): PNode = var m = genCopy(c, n.typ, tmp, n) m.add p(n, c) result.add m - message(c.graph.config, n.info, hintPerformance, - ("passing '$1' to a sink parameter introduces an implicit copy; " & - "use 'move($1)' to prevent it") % $n) + if isLValue(n): + message(c.graph.config, n.info, hintPerformance, + ("passing '$1' to a sink parameter introduces an implicit copy; " & + "use 'move($1)' to prevent it") % $n) else: result.add newTree(nkAsgn, tmp, p(n, c)) result.add tmp @@ -446,6 +452,18 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = ri2.add pArg(ri[i], c, i < L and parameters[i].kind == tySink) #recurse(ri, ri2) result.add ri2 + of nkBracketExpr: + if ri[0].kind == nkSym and isUnpackedTuple(ri[0].sym): + # unpacking of tuple: move out the elements + result = genSink(c, dest.typ, dest, ri) + else: + result = genCopy(c, dest.typ, dest, ri) + result.add p(ri, c) + of nkStmtListExpr: + result = newNodeI(nkStmtList, ri.info) + for i in 0..ri.len-2: + result.add p(ri[i], c) + result.add moveOrCopy(dest, ri[^1], c) of nkObjConstr: result = genSink(c, dest.typ, dest, ri) let ri2 = copyTree(ri) @@ -454,6 +472,17 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = # so these all act like 'sink' parameters: ri2[i].sons[1] = pArg(ri[i][1], c, isSink = true) result.add ri2 + of nkTupleConstr: + result = genSink(c, dest.typ, dest, ri) + let ri2 = copyTree(ri) + for i in 0..<ri.len: + # everything that is passed to an tuple constructor is consumed, + # so these all act like 'sink' parameters: + if ri[i].kind == nkExprColonExpr: + ri2[i].sons[1] = pArg(ri[i][1], c, isSink = true) + else: + ri2[i] = pArg(ri[i], c, isSink = true) + result.add ri2 of nkSym: if ri.sym.kind != skParam and isLastRead(ri, c): # Rule 3: `=sink`(x, z); wasMoved(z) @@ -483,7 +512,7 @@ proc p(n: PNode; c: var Con): PNode = if it.kind == nkVarTuple and hasDestructor(ri.typ): let x = lowerTupleUnpacking(c.graph, it, c.owner) result.add p(x, c) - elif it.kind == nkIdentDefs and hasDestructor(it[0].typ): + elif it.kind == nkIdentDefs and hasDestructor(it[0].typ) and not isUnpackedTuple(it[0].sym): for j in 0..L-2: let v = it[j] doAssert v.kind == nkSym diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 6f61d020d..67f4108e1 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -400,7 +400,13 @@ proc extractImports(n: PNode; result: PNode) = for i in 0..<n.safeLen: extractImports(n[i], result) proc prepareExamples(d: PDoc; n: PNode) = + + var docComment = newTree(nkCommentStmt) + let loc = d.conf.toFileLineCol(n.info) + docComment.comment = "autogenerated by docgen from " & loc + var runnableExamples = newTree(nkStmtList, + docComment, newTree(nkImportStmt, newStrNode(nkStrLit, d.filename))) runnableExamples.info = n.info let imports = newTree(nkStmtList) diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 43d5a8698..09a1cd436 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -47,7 +47,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = internalAssert c.config, sfGenSym in s.flags or s.kind == skType var x = PSym(idTableGet(c.mapping, s)) if x == nil: - x = copySym(s, false) + x = copySym(s) x.owner = c.genSymOwner idTablePut(c.mapping, s, x) result.add newSymNode(x, if c.instLines: actual.info else: templ.info) diff --git a/compiler/importer.nim b/compiler/importer.nim index 60b7872fe..131b1ad8a 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -100,7 +100,7 @@ proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) = if s.kind != skModule: if s.kind != skEnumField: if s.kind notin ExportableSymKinds: - internalError(c.config, s.info, "importAllSymbols: " & $s.kind) + internalError(c.config, s.info, "importAllSymbols: " & $s.kind & " " & s.name.s) if exceptSet.isNil or s.name.id notin exceptSet: rawImportSymbol(c, s) s = nextIter(i, fromMod.tab) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 19bbde777..a9813f5c5 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -66,6 +66,12 @@ type res: Rope # result part; index if this is an # (address, index)-tuple address: Rope # address of an (address, index)-tuple + tmpLoc: Rope # tmp var which stores the (address, index) + # pair to prevent multiple evals. + # the tmp is initialized upon evaling the + # address. + # might be nil. + # (see `maybeMakeTemp`) TBlock = object id: int # the ID of the label; positive means that it @@ -131,16 +137,15 @@ proc newGlobals(): PGlobals = proc initCompRes(r: var TCompRes) = r.address = nil r.res = nil + r.tmpLoc = nil r.typ = etyNone r.kind = resNone proc rdLoc(a: TCompRes): Rope {.inline.} = - result = a.res - when false: - if a.typ != etyBaseIndex: - result = a.res - else: - result = "$1[$2]" % [a.address, a.res] + if a.typ != etyBaseIndex: + result = a.res + else: + result = "$1[$2]" % [a.address, a.res] proc newProc(globals: PGlobals, module: BModule, procDef: PNode, options: TOptions): PProc = @@ -447,12 +452,48 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"], ["", "", "$1", "$1"]] +proc needsTemp(p: PProc; n: PNode): bool = + # check if n contains a call to determine + # if a temp should be made to prevent multiple evals + if n.kind in nkCallKinds + {nkTupleConstr, nkObjConstr, nkBracket, nkCurly}: + return true + for c in n: + if needsTemp(p, c): + return true + +proc maybeMakeTemp(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rope] = + var + a = x.rdLoc + b = a + if needsTemp(p, n): + # if we have tmp just use it + if x.tmpLoc != nil and (mapType(n.typ) == etyBaseIndex or n.kind in {nkHiddenDeref, nkDerefExpr}): + b = "$1[0][$1[1]]" % [x.tmpLoc] + (a: a, tmp: b) + else: + let tmp = p.getTemp + b = tmp + a = "($1 = $2, $1)" % [tmp, a] + (a: a, tmp: b) + else: + (a: a, tmp: b) + proc binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = + # $1 and $2 in the `frmt` string bind to lhs and rhs of the expr, + # if $3 or $4 are present they will be substituted with temps for + # lhs and rhs respectively var x, y: TCompRes useMagic(p, magic) gen(p, n.sons[1], x) gen(p, n.sons[2], y) - r.res = frmt % [x.rdLoc, y.rdLoc] + + var + a, tmp = x.rdLoc + b, tmp2 = y.rdLoc + if "$3" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x) + if "$4" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x) + + r.res = frmt % [a, b, tmp, tmp2] r.kind = resExpr proc unsignedTrimmerJS(size: BiggestInt): Rope = @@ -473,7 +514,8 @@ proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string, gen(p, n.sons[2], y) let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size) if reassign: - r.res = "$1 = (($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer] + let (a, tmp) = maybeMakeTemp(p, n[1], x) + r.res = "$1 = (($5 $2 $3) $4)" % [a, rope op, y.rdLoc, trimmer, tmp] else: r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer] @@ -487,9 +529,12 @@ proc ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = r.kind = resExpr proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = + # $1 binds to n[1], if $2 is present it will be substituted to a tmp of $1 useMagic(p, magic) gen(p, n.sons[1], r) - r.res = frmt % [r.rdLoc] + var a, tmp = r.rdLoc + if "$2" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], r) + r.res = frmt % [a, tmp] r.kind = resExpr proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = @@ -524,6 +569,14 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr: arithAux(p, n, r, op) + of mEqRef, mEqUntracedRef: + if mapType(n[1].typ) != etyBaseIndex: + arithAux(p, n, r, op) + else: + var x, y: TCompRes + gen(p, n[1], x) + gen(p, n[2], y) + r.res = "($# == $# && $# == $#)" % [x.address, y.address, x.res, y.res] else: arithAux(p, n, r, op) r.kind = resExpr @@ -801,6 +854,7 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode) = # A fat pointer is disguised as an array r.res = r.address r.address = nil + r.typ = etyNone elif r.typ == etyBaseIndex: # Deference first r.res = "$1[$2]" % [r.address, r.res] @@ -863,26 +917,42 @@ proc countJsParams(typ: PType): int = const nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, - nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkTupleConstr, nkObjConstr, nkStringToCString, + nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkStringToCString, nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix, nkCommand, nkHiddenCallConv, nkCallStrLit} proc needsNoCopy(p: PProc; y: PNode): bool = - result = (y.kind in nodeKindsNeedNoCopy) or - (skipTypes(y.typ, abstractInst).kind in {tyRef, tyPtr, tyLent, tyVar}) + # if the node is a literal object constructor we have to recursively + # check the expressions passed into it + case y.kind + of nkObjConstr: + for arg in y.sons[1..^1]: + if not needsNoCopy(p, arg[1]): + return false + of nkTupleConstr: + for arg in y.sons: + var arg = arg + if arg.kind == nkExprColonExpr: + arg = arg[1] + if not needsNoCopy(p, arg): + return false + of nkBracket: + for arg in y.sons: + if not needsNoCopy(p, arg): + return false + of nodeKindsNeedNoCopy: + return true + else: + return (mapType(y.typ) != etyBaseIndex and + (skipTypes(y.typ, abstractInst).kind in + {tyRef, tyPtr, tyLent, tyVar, tyCString} + IntegralTypes)) + return true proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = var a, b: TCompRes var xtyp = mapType(p, x.typ) - if x.kind == nkHiddenDeref and x.sons[0].kind == nkCall and xtyp != etyObject: - gen(p, x.sons[0], a) - let tmp = p.getTemp(false) - lineF(p, "var $1 = $2;$n", [tmp, a.rdLoc]) - a.res = "$1[0][$1[1]]" % [tmp] - else: - gen(p, x, a) - + gen(p, x, a) genLineDir(p, y) gen(p, y, b) @@ -911,13 +981,13 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = let tmp = p.getTemp(false) lineF(p, "var $1 = $4; $2 = $1[0]; $3 = $1[1];$n", [tmp, a.address, a.res, b.rdLoc]) elif b.typ == etyBaseIndex: - lineF(p, "$# = $#;$n", [a.res, b.rdLoc]) + lineF(p, "$# = [$#, $#];$n", [a.res, b.address, b.res]) else: internalError(p.config, x.info, "genAsgn") else: lineF(p, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) else: - lineF(p, "$1 = $2;$n", [a.res, b.res]) + lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) proc genAsgn(p: PProc, n: PNode) = genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=false) @@ -971,17 +1041,30 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = r.kind = resExpr proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = - r.typ = etyNone gen(p, n.sons[0], r) + r.typ = mapType(n.typ) let otyp = skipTypes(n.sons[0].typ, abstractVarRange) + + template mkTemp(i: int) = + if r.typ == etyBaseIndex: + if needsTemp(p, n[i]): + let tmp = p.getTemp + r.address = "($1 = $2, $1)[0]" % [tmp, r.res] + r.res = "$1[1]" % [tmp] + r.tmpLoc = tmp + else: + r.address = "$1[0]" % [r.res] + r.res = "$1[1]" % [r.res] if otyp.kind == tyTuple: r.res = ("$1.Field$2") % [r.res, getFieldPosition(p, n.sons[1]).rope] + mkTemp(0) else: if n.sons[1].kind != nkSym: internalError(p.config, n.sons[1].info, "genFieldAccess") var f = n.sons[1].sym if f.loc.r == nil: f.loc.r = mangleName(p.module, f) r.res = "$1.$2" % [r.res, f.loc.r] + mkTemp(1) r.kind = resExpr proc genAddr(p: PProc, n: PNode, r: var TCompRes) @@ -1039,14 +1122,15 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = let m = if n.kind == nkHiddenAddr: n.sons[0] else: n gen(p, m.sons[0], a) gen(p, m.sons[1], b) - internalAssert p.config, a.typ != etyBaseIndex and b.typ != etyBaseIndex - r.address = a.res + #internalAssert p.config, a.typ != etyBaseIndex and b.typ != etyBaseIndex + let (x, tmp) = maybeMakeTemp(p, m[0], a) + r.address = x var typ = skipTypes(m.sons[0].typ, abstractPtrs) if typ.kind == tyArray: first = firstOrd(p.config, typ.sons[0]) else: first = 0 if optBoundsCheck in p.options: useMagic(p, "chckIndx") - r.res = "chckIndx($1, $2, $3.length+$2-1)-$2" % [b.res, rope(first), a.res] + r.res = "chckIndx($1, $2, $3.length+$2-1)-$2" % [b.res, rope(first), tmp] elif first != 0: r.res = "($1)-$2" % [b.res, rope(first)] else: @@ -1062,13 +1146,22 @@ proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = of tyTuple: genFieldAddr(p, n, r) else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')') - r.typ = etyNone + r.typ = mapType(n.typ) if r.res == nil: internalError(p.config, n.info, "genArrayAccess") if ty.kind == tyCString: r.res = "$1.charCodeAt($2)" % [r.address, r.res] + elif r.typ == etyBaseIndex: + if needsTemp(p, n[0]): + let tmp = p.getTemp + r.address = "($1 = $2, $1)[0]" % [tmp, r.rdLoc] + r.res = "$1[1]" % [tmp] + r.tmpLoc = tmp + else: + let x = r.rdLoc + r.address = "$1[0]" % [x] + r.res = "$1[1]" % [x] else: r.res = "$1[$2]" % [r.address, r.res] - r.address = nil r.kind = resExpr template isIndirect(x: PSym): bool = @@ -1169,8 +1262,12 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = if k == etyBaseIndex: r.typ = etyBaseIndex if {sfAddrTaken, sfGlobal} * s.flags != {}: - r.address = "$1[0]" % [s.loc.r] - r.res = "$1[1]" % [s.loc.r] + if isIndirect(s): + r.address = "$1[0][0]" % [s.loc.r] + r.res = "$1[0][1]" % [s.loc.r] + else: + r.address = "$1[0]" % [s.loc.r] + r.res = "$1[1]" % [s.loc.r] else: r.address = s.loc.r r.res = s.loc.r & "_Idx" @@ -1210,14 +1307,17 @@ proc genDeref(p: PProc, n: PNode, r: var TCompRes) = else: var a: TCompRes gen(p, it, a) - r.kind = resExpr - if a.typ == etyBaseIndex: - r.res = "$1[$2]" % [a.address, a.res] - elif it.kind == nkCall: + r.kind = a.kind + r.typ = mapType(p, n.typ) + if r.typ == etyBaseIndex: let tmp = p.getTemp - r.res = "($1 = $2, $1[0])[$1[1]]" % [tmp, a.res] - elif t == etyBaseIndex: - r.res = "$1[0]" % [a.res] + r.address = "($1 = $2, $1)[0]" % [tmp, a.rdLoc] + r.res = "$1[1]" % [tmp] + r.tmpLoc = tmp + elif a.typ == etyBaseIndex: + if a.tmpLoc != nil: + r.tmpLoc = a.tmpLoc + r.res = a.rdLoc else: internalError(p.config, n.info, "genDeref") @@ -1242,7 +1342,7 @@ proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes; emitted: ptr int = add(r.res, ", ") add(r.res, a.res) if emitted != nil: inc emitted[] - elif n.typ.kind in {tyVar, tyLent} and n.kind in nkCallKinds and mapType(param.typ) == etyBaseIndex: + elif n.typ.kind in {tyVar, tyPtr, tyRef, tyLent} and n.kind in nkCallKinds and mapType(param.typ) == etyBaseIndex: # this fixes bug #5608: let tmp = getTemp(p) add(r.res, "($1 = $2, $1[0]), $1[1]" % [tmp, a.rdLoc]) @@ -1366,6 +1466,14 @@ proc genCall(p: PProc, n: PNode, r: var TCompRes) = return gen(p, n.sons[0], r) genArgs(p, n, r) + if n.typ != nil: + let t = mapType(n.typ) + if t == etyBaseIndex: + let tmp = p.getTemp + r.address = "($1 = $2, $1)[0]" % [tmp, r.rdLoc] + r.res = "$1[1]" % [tmp] + r.tmpLoc = tmp + r.typ = t proc genEcho(p: PProc, n: PNode, r: var TCompRes) = let n = n[1].skipConv @@ -1472,12 +1580,12 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = createObjInitList(p, t, initIntSet(), initList) result = ("{$1}") % [initList] if indirect: result = "[$1]" % [result] - of tyVar, tyPtr, tyLent, tyRef: + of tyVar, tyPtr, tyLent, tyRef, tyPointer: if mapType(p, t) == etyBaseIndex: result = putToSeq("[null, 0]", indirect) else: result = putToSeq("null", indirect) - of tySequence, tyOpt, tyString, tyCString, tyPointer, tyProc: + of tySequence, tyOpt, tyString, tyCString, tyProc: result = putToSeq("null", indirect) of tyStatic: if t.n != nil: @@ -1511,10 +1619,13 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = varCode = v.constraint.strVal if n.kind == nkEmpty: - lineF(p, varCode & " = $3;$n", - [returnType, varName, createVar(p, v.typ, isIndirect(v))]) - if v.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and mapType(p, v.typ) == etyBaseIndex: + if not isIndirect(v) and + v.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and mapType(p, v.typ) == etyBaseIndex: + lineF(p, "var $1 = null;$n", [varName]) lineF(p, "var $1_Idx = 0;$n", [varName]) + else: + lineF(p, varCode & " = $3;$n", + [returnType, varName, createVar(p, v.typ, isIndirect(v))]) else: gen(p, n, a) case mapType(p, v.typ) @@ -1531,8 +1642,12 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = lineF(p, varCode & " = $3, $2_Idx = $4;$n", [returnType, v.loc.r, a.address, a.res]) else: - lineF(p, varCode & " = [$3, $4];$n", - [returnType, v.loc.r, a.address, a.res]) + if isIndirect(v): + lineF(p, varCode & " = [[$3, $4]];$n", + [returnType, v.loc.r, a.address, a.res]) + else: + lineF(p, varCode & " = [$3, $4];$n", + [returnType, v.loc.r, a.address, a.res]) else: if targetBaseIndex: let tmp = p.getTemp @@ -1579,7 +1694,12 @@ proc genNew(p: PProc, n: PNode) = var a: TCompRes gen(p, n.sons[1], a) var t = skipTypes(n.sons[1].typ, abstractVar).sons[0] - lineF(p, "$1 = $2;$n", [a.res, createVar(p, t, false)]) + if mapType(t) == etyObject: + lineF(p, "$1 = $2;$n", [a.rdLoc, createVar(p, t, false)]) + elif a.typ == etyBaseIndex: + lineF(p, "$1 = [$3]; $2 = 0;$n", [a.address, a.res, createVar(p, t, false)]) + else: + lineF(p, "$1 = [[$2], 0];$n", [a.rdLoc, createVar(p, t, false)]) proc genNewSeq(p: PProc, n: PNode) = var x, y: TCompRes @@ -1603,20 +1723,20 @@ proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) = if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyChar: r.res.add("[$1].concat(" % [a.res]) else: - r.res.add("($1).concat(" % [a.res]) + r.res.add("($1 || []).concat(" % [a.res]) for i in countup(2, sonsLen(n) - 2): gen(p, n.sons[i], a) if skipTypes(n.sons[i].typ, abstractVarRange).kind == tyChar: r.res.add("[$1]," % [a.res]) else: - r.res.add("$1," % [a.res]) + r.res.add("$1 || []," % [a.res]) gen(p, n.sons[sonsLen(n) - 1], a) if skipTypes(n.sons[sonsLen(n) - 1].typ, abstractVarRange).kind == tyChar: r.res.add("[$1])" % [a.res]) else: - r.res.add("$1)" % [a.res]) + r.res.add("$1 || [])" % [a.res]) proc genToArray(p: PProc; n: PNode; r: var TCompRes) = # we map mArray to PHP's array constructor, a mild hack: @@ -1701,8 +1821,12 @@ proc genReset(p: PProc, n: PNode) = var x: TCompRes useMagic(p, "genericReset") gen(p, n.sons[1], x) - addf(p.body, "$1 = genericReset($1, $2);$n", [x.res, - genTypeInfo(p, n.sons[1].typ)]) + if x.typ == etyBaseIndex: + lineF(p, "$1 = null, $2 = 0;$n", [x.address, x.res]) + else: + let (a, tmp) = maybeMakeTemp(p, n[1], x) + lineF(p, "$1 = genericReset($3, $2);$n", [a, + genTypeInfo(p, n.sons[1].typ), tmp]) proc genMagic(p: PProc, n: PNode, r: var TCompRes) = var @@ -1721,32 +1845,37 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = else: unaryExpr(p, n, r, "subInt", "subInt($1, 1)") of mAppendStrCh: binaryExpr(p, n, r, "addChar", - "if ($1 != null) { addChar($1, $2); } else { $1 = [$2]; }") + "if ($1 != null) { addChar($3, $2); } else { $3 = [$2]; }") of mAppendStrStr: var lhs, rhs: TCompRes gen(p, n[1], lhs) gen(p, n[2], rhs) let rhsIsLit = n[2].kind in nkStrKinds + let (a, tmp) = maybeMakeTemp(p, n[1], lhs) if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString: - r.res = "if ($1 != null) { $1 += $2; } else { $1 = $2$3; }" % [ - lhs.rdLoc, rhs.rdLoc, if rhsIsLit: nil else: ~".slice()"] + r.res = "if ($1 != null) { $4 += $2; } else { $4 = $2$3; }" % [ + a, rhs.rdLoc, if rhsIsLit: nil else: ~".slice()", tmp] else: - r.res = "if ($1 != null) { $1 = ($1).concat($2); } else { $1 = $2$3; }" % [ - lhs.rdLoc, rhs.rdLoc, if rhsIsLit: nil else: ~".slice()"] + r.res = "if ($1 != null) { $4 = ($4).concat($2); } else { $4 = $2$3; }" % [ + lhs.rdLoc, rhs.rdLoc, if rhsIsLit: nil else: ~".slice()", tmp] r.kind = resExpr of mAppendSeqElem: var x, y: TCompRes gen(p, n.sons[1], x) gen(p, n.sons[2], y) - if needsNoCopy(p, n[2]): - r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, y.rdLoc] + let (a, tmp) = maybeMakeTemp(p, n[1], x) + if mapType(n[2].typ) == etyBaseIndex: + let c = "[$1, $2]" % [y.address, y.res] + r.res = "if ($1 != null) { $3.push($2); } else { $3 = [$2]; }" % [a, c, tmp] + elif needsNoCopy(p, n[2]): + r.res = "if ($1 != null) { $3.push($2); } else { $3 = [$2]; }" % [a, y.rdLoc, tmp] else: useMagic(p, "nimCopy") let c = getTemp(p, defineInLocals=false) lineF(p, "var $1 = nimCopy(null, $2, $3);$n", [c, y.rdLoc, genTypeInfo(p, n[2].typ)]) - r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, c] + r.res = "if ($1 != null) { $3.push($2); } else { $3 = [$2]; }" % [a, c, tmp] r.kind = resExpr of mConStrStr: genConStrStr(p, n, r) @@ -1756,38 +1885,56 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)") of mLtStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)") - of mIsNil: unaryExpr(p, n, r, "", "($1 === null)") + of mIsNil: + if mapType(n[1].typ) != etyBaseIndex: + unaryExpr(p, n, r, "", "($1 === null)") + else: + var x: TCompRes + gen(p, n[1], x) + r.res = "($# === null && $# === 0)" % [x.address, x.res] of mEnumToStr: genRepr(p, n, r) of mNew, mNewFinalize: genNew(p, n) - of mChr, mArrToSeq: gen(p, n.sons[1], r) # nothing to do + of mChr: gen(p, n.sons[1], r) + of mArrToSeq: + if needsNoCopy(p, n.sons[1]): + gen(p, n.sons[1], r) + else: + var x: TCompRes + gen(p, n.sons[1], x) + useMagic(p, "nimCopy") + r.res = "nimCopy(null, $1, $2)" % [x.rdLoc, genTypeInfo(p, n.typ)] + of mDestroy: discard "ignore calls to the default destructor" of mOrd: genOrd(p, n, r) of mLengthStr, mLengthSeq, mLengthOpenArray, mLengthArray: - unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)") + unaryExpr(p, n, r, "", "($1 != null ? $2.length : 0)") of mXLenStr, mXLenSeq: unaryExpr(p, n, r, "", "$1.length") of mHigh: - unaryExpr(p, n, r, "", "($1 != null ? ($1.length-1) : -1)") + unaryExpr(p, n, r, "", "($1 != null ? ($2.length-1) : -1)") of mInc: if n[1].typ.skipTypes(abstractRange).kind in tyUInt .. tyUInt64: binaryUintExpr(p, n, r, "+", true) else: if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 += $2") - else: binaryExpr(p, n, r, "addInt", "$1 = addInt($1, $2)") + else: binaryExpr(p, n, r, "addInt", "$1 = addInt($3, $2)") of ast.mDec: if n[1].typ.skipTypes(abstractRange).kind in tyUInt .. tyUInt64: binaryUintExpr(p, n, r, "-", true) else: if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2") - else: binaryExpr(p, n, r, "subInt", "$1 = subInt($1, $2)") + else: binaryExpr(p, n, r, "subInt", "$1 = subInt($3, $2)") of mSetLengthStr: - binaryExpr(p, n, r, "", "$1.length = $2") + binaryExpr(p, n, r, "mnewString", "($1 === null ? $3 = mnewString($2) : $3.length = $2)") of mSetLengthSeq: var x, y: TCompRes gen(p, n.sons[1], x) gen(p, n.sons[2], y) let t = skipTypes(n.sons[1].typ, abstractVar).sons[0] - r.res = """if ($1.length < $2) { for (var i=$1.length;i<$2;++i) $1.push($3); } - else { $1.length = $2; }""" % [x.rdLoc, y.rdLoc, createVar(p, t, false)] + let (a, tmp) = maybeMakeTemp(p, n[1], x) + let (b, tmp2) = maybeMakeTemp(p, n[2], y) + r.res = """if ($1 === null) $4 = []; + if ($4.length < $2) { for (var i=$4.length;i<$5;++i) $4.push($3); } + else { $4.length = $5; }""" % [a, b, createVar(p, t, false), tmp, tmp2] r.kind = resExpr of mCard: unaryExpr(p, n, r, "SetCard", "SetCard($1)") of mLtSet: binaryExpr(p, n, r, "SetLt", "SetLt($1, $2)") @@ -1855,7 +2002,10 @@ proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) = for i in countup(0, sonsLen(n) - 1): if i > 0: add(r.res, ", ") gen(p, n.sons[i], a) - add(r.res, a.res) + if a.typ == etyBaseIndex: + addf(r.res, "[$1, $2]", [a.address, a.res]) + else: + add(r.res, a.res) add(r.res, "]") proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = @@ -1867,7 +2017,10 @@ proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = var it = n.sons[i] if it.kind == nkExprColonExpr: it = it.sons[1] gen(p, it, a) - addf(r.res, "Field$#: $#", [i.rope, a.res]) + if a.typ == etyBaseIndex: + addf(r.res, "Field$#: [$#, $#]", [i.rope, a.address, a.res]) + else: + addf(r.res, "Field$#: $#", [i.rope, a.res]) r.res.add("}") proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = @@ -1887,12 +2040,17 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = let typ = val.typ.skipTypes(abstractInst) if (typ.kind in IntegralTypes+{tyCstring, tyRef, tyPtr} and - mapType(p, typ) != etyBaseIndex) or needsNoCopy(p, it.sons[1]): + mapType(p, typ) != etyBaseIndex) or + a.typ == etyBaseIndex or + needsNoCopy(p, it.sons[1]): discard else: useMagic(p, "nimCopy") a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] - addf(initList, "$#: $#", [f.loc.r, a.res]) + if a.typ == etyBaseIndex: + addf(initList, "$#: [$#, $#]", [f.loc.r, a.address, a.res]) + else: + addf(initList, "$#: $#", [f.loc.r, a.res]) let t = skipTypes(n.typ, abstractInst + skipPtrs) createObjInitList(p, t, fieldIDs, initList) r.res = ("{$1}") % [initList] @@ -2010,11 +2168,14 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = if prc.typ.sons[0] != nil and sfPure notin prc.flags: resultSym = prc.ast.sons[resultPos].sym let mname = mangleName(p.module, resultSym) - let resVar = createVar(p, resultSym.typ, isIndirect(resultSym)) - resultAsgn = p.indentLine(("var $# = $#;$n") % [mname, resVar]) - if resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and + if not isindirect(resultSym) and + resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and mapType(p, resultSym.typ) == etyBaseIndex: + resultAsgn = p.indentLine(("var $# = null;$n") % [mname]) resultAsgn.add p.indentLine("var $#_Idx = 0;$n" % [mname]) + else: + let resVar = createVar(p, resultSym.typ, isIndirect(resultSym)) + resultAsgn = p.indentLine(("var $# = $#;$n") % [mname, resVar]) gen(p, prc.ast.sons[resultPos], a) if mapType(p, resultSym.typ) == etyBaseIndex: returnStmt = "return [$#, $#];$n" % [a.address, a.res] @@ -2106,6 +2267,13 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) = of 4: "0xfffffffe" else: "" r.res = "($1 - ($2 $3))" % [rope minuend, r.res, trimmer] + elif (src.kind == tyPtr and mapType(p, src) == etyObject) and dest.kind == tyPointer: + r.address = r.res + r.res = ~"null" + r.typ = etyBaseIndex + elif (dest.kind == tyPtr and mapType(p, dest) == etyObject) and src.kind == tyPointer: + r.res = r.address + r.typ = etyObject proc gen(p: PProc, n: PNode, r: var TCompRes) = r.typ = etyNone diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index c318421fa..874cb4bd0 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -258,7 +258,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode = # add 'new' statement: result.add newCall(getSysSym(g, n.info, "internalNew"), env) result.add makeClosure(g, iter, env, n.info) - + proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode = let envParam = getHiddenParam(g, owner) let obj = envParam.typ.lastSon @@ -454,11 +454,10 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = createUpField(c, w, up, n.info) w = up of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, - nkTemplateDef, nkTypeSection: - discard - of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef: + nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef, + nkConverterDef, nkMacroDef, nkFuncDef: discard - of nkLambdaKinds, nkIteratorDef, nkFuncDef: + of nkLambdaKinds, nkIteratorDef: if n.typ != nil: detectCapturedVars(n[namePos], owner, c) of nkReturnStmt: @@ -672,9 +671,8 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; else: result = accessViaEnvVar(n, owner, d, c) of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom, - nkTemplateDef, nkTypeSection: - discard - of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef: + nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef, nkConverterDef, + nkMacroDef, nkFuncDef: discard of nkClosure: if n[1].kind == nkNilLit: @@ -685,7 +683,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; # now we know better, so patch it: n.sons[0] = x.sons[0] n.sons[1] = x.sons[1] - of nkLambdaKinds, nkIteratorDef, nkFuncDef: + of nkLambdaKinds, nkIteratorDef: if n.typ != nil and n[namePos].kind == nkSym: let oldInContainer = c.inContainer c.inContainer = 0 @@ -720,19 +718,37 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; # ------------------ old stuff ------------------------------------------- proc semCaptureSym*(s, owner: PSym) = + discard """ + proc outer() = + var x: int + proc inner() = + proc innerInner() = + echo x + innerInner() + inner() + # inner() takes a closure too! + """ + proc propagateClosure(start, last: PSym) = + var o = start + while o != nil and o.kind != skModule: + if o == last: break + o.typ.callConv = ccClosure + o = o.skipGenericOwner + if interestingVar(s) and s.kind != skResult: if owner.typ != nil and not isGenericRoutine(owner): # XXX: is this really safe? # if we capture a var from another generic routine, # it won't be consider captured. var o = owner.skipGenericOwner - while o.kind != skModule and o != nil: + while o != nil and o.kind != skModule: if s.owner == o: if owner.typ.callConv in {ccClosure, ccDefault} or owner.kind == skIterator: owner.typ.callConv = ccClosure + propagateClosure(owner.skipGenericOwner, s.owner) else: discard "do not produce an error here, but later" - #echo "computing .closure for ", owner.name.s, " ", owner.info, " because of ", s.name.s + #echo "computing .closure for ", owner.name.s, " because of ", s.name.s o = o.skipGenericOwner # since the analysis is not entirely correct, we don't set 'tfCapturesEnv' # here diff --git a/compiler/lookups.nim b/compiler/lookups.nim index d2e7fdcfa..2fb4e5241 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -168,7 +168,7 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) = localError(c.config, s.info, "implementation of '$1' expected" % getSymRepr(c.config, s)) inc missingImpls - elif {sfUsed, sfExported} * s.flags == {} and optHints in s.options: + elif {sfUsed, sfExported} * s.flags == {}: if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}: # XXX: implicit type params are currently skTypes # maybe they can be made skGenericParam as well. diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 603def326..d199abcc7 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -78,7 +78,7 @@ proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; owner: PSym): PNode = let value = n.lastSon result = newNodeI(nkStmtList, n.info) - var temp = newSym(skLet, getIdent(g.cache, "_"), owner, value.info, owner.options) + var temp = newSym(skTemp, getIdent(g.cache, "_"), owner, value.info, owner.options) var v = newNodeI(nkLetSection, value.info) let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info) diff --git a/compiler/modules.nim b/compiler/modules.nim index e2f322561..442305a06 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -17,18 +17,7 @@ import proc resetSystemArtifacts*(g: ModuleGraph) = magicsys.resetSysTypes(g) -proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = - # We cannot call ``newSym`` here, because we have to circumvent the ID - # mechanism, which we do in order to assign each module a persistent ID. - new(result) - result.id = -1 # for better error checking - result.kind = skModule - let filename = toFullPath(graph.config, fileIdx) - result.name = getIdent(graph.cache, splitFile(filename).name) - if not isNimIdentifier(result.name.s): - rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s) - - result.info = newLineInfo(fileIdx, 1, 1) +proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: string) = let pck = getPackageName(graph.config, filename) pck2 = if pck.len > 0: pck else: "unknown" @@ -38,13 +27,11 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = packSym = newSym(skPackage, getIdent(graph.cache, pck2), nil, result.info) initStrTable(packSym.tab) graph.packageSyms.strTableAdd(packSym) - result.owner = packSym result.position = int fileIdx if int(fileIdx) >= graph.modules.len: setLen(graph.modules, int(fileIdx) + 1) - #growCache graph.modules, int fileIdx graph.modules[result.position] = result incl(result.flags, sfUsed) @@ -58,16 +45,36 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = # strTableIncl() for error corrections: discard strTableIncl(packSym.tab, result) +proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = + # We cannot call ``newSym`` here, because we have to circumvent the ID + # mechanism, which we do in order to assign each module a persistent ID. + new(result) + result.id = -1 # for better error checking + result.kind = skModule + let filename = toFullPath(graph.config, fileIdx) + result.name = getIdent(graph.cache, splitFile(filename).name) + if not isNimIdentifier(result.name.s): + rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s) + result.info = newLineInfo(fileIdx, 1, 1) + partialInitModule(result, graph, fileIdx, filename) + proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): PSym = result = graph.getModule(fileIdx) if result == nil: - result = newModule(graph, fileIdx) - result.flags = result.flags + flags - if sfMainModule in result.flags: - graph.config.mainPackageId = result.owner.id - - result.id = getModuleId(graph, fileIdx, AbsoluteFile toFullPath(graph.config, fileIdx)) - registerModule(graph, result) + let filename = toFullPath(graph.config, fileIdx) + let (r, id) = loadModuleSym(graph, fileIdx, AbsoluteFile filename) + result = r + if result == nil: + result = newModule(graph, fileIdx) + result.flags = result.flags + flags + if sfMainModule in result.flags: + graph.config.mainPackageId = result.owner.id + result.id = id + registerModule(graph, result) + else: + partialInitModule(result, graph, fileIdx, filename) + result.id = id + assert result.id < 0 discard processModule(graph, result, if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil) elif graph.isDirty(result): diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 4ac5a839d..7e6b67cbe 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -190,7 +190,7 @@ template toFullPath*(conf: ConfigRef; info: TLineInfo): string = proc toMsgFilename*(conf: ConfigRef; info: TLineInfo): string = if info.fileIndex.int32 < 0: result = "???" - return + return let absPath = conf.m.fileInfos[info.fileIndex.int32].fullPath.string let relPath = conf.m.fileInfos[info.fileIndex.int32].projPath.string if optListFullPaths in conf.globalOptions: diff --git a/compiler/pathutils.nim b/compiler/pathutils.nim index 4873f90d6..703467bc4 100644 --- a/compiler/pathutils.nim +++ b/compiler/pathutils.nim @@ -73,23 +73,6 @@ iterator dirs(x: string): (int, int) = var it: PathIter while hasNext(it, x): yield next(it, x) -when false: - iterator dirs(x: string): (int, int) = - var i = 0 - var first = true - while i < x.len: - let prev = i - if first and x[i] in {DirSep, AltSep}: - # absolute path: - inc i - else: - while i < x.len and x[i] notin {DirSep, AltSep}: inc i - if i > prev: - yield (prev, i-1) - first = false - # skip all separators: - while i < x.len and x[i] in {DirSep, AltSep}: inc i - proc isDot(x: string; bounds: (int, int)): bool = bounds[1] == bounds[0] and x[bounds[0]] == '.' diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index ef5223559..3d8e5645b 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -38,7 +38,7 @@ const wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern, wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises, wTags, wLocks, wGcSafe, wExportNims, wUsed} - exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe} + exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe, wNosideeffect} stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks, wOverflowchecks, wNilchecks, wMovechecks, wAssertions, wWarnings, wHints, @@ -855,8 +855,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, sym.flags.incl sfOverriden of wNosideeffect: noVal(c, it) - incl(sym.flags, sfNoSideEffect) - if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect) + if sym != nil: + incl(sym.flags, sfNoSideEffect) + if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect) of wSideeffect: noVal(c, it) incl(sym.flags, sfSideEffect) @@ -1110,10 +1111,19 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, else: sym.flags.incl sfUsed of wLiftLocals: discard else: invalidPragma(c, it) - elif sym.kind in {skVar,skLet,skParam,skField,skProc,skFunc,skConverter,skMethod,skType}: + elif sym != nil and sym.kind in {skVar, skLet, skParam, skField, skProc, + skFunc, skConverter, skMethod, skType}: n.sons[i] = semCustomPragma(c, it) - else: + elif sym != nil: illegalCustomPragma(c, it, sym) + else: + invalidPragma(c, it) + +proc mergePragmas(n, pragmas: PNode) = + if n[pragmasPos].kind == nkEmpty: + n[pragmasPos] = pragmas + else: + for p in pragmas: n.sons[pragmasPos].add p proc implicitPragmas*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = @@ -1123,11 +1133,12 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode, if not o.isNil: pushInfoContext(c.config, n.info) var i = 0 - while i < o.len(): + while i < o.len: if singlePragma(c, sym, o, i, validPragmas): internalError(c.config, n.info, "implicitPragmas") inc i popInfoContext(c.config) + if sym.kind in routineKinds: mergePragmas(sym.ast, o) if lfExportLib in sym.loc.flags and sfExportc notin sym.flags: localError(c.config, n.info, ".dynlib requires .exportc") diff --git a/compiler/rod.nim b/compiler/rod.nim index 92489ffdd..bbd2f0c6c 100644 --- a/compiler/rod.nim +++ b/compiler/rod.nim @@ -16,7 +16,7 @@ when not nimIncremental: template storeNode*(g: ModuleGraph; module: PSym; n: PNode) = discard template loadNode*(g: ModuleGraph; module: PSym): PNode = newNode(nkStmtList) - template getModuleId*(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): int = getID() + proc loadModuleSym*(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): (PSym, int) {.inline.} = (nil, getID()) template addModuleDep*(g: ModuleGraph; module, fileIdx: FileIndex; isIncludeFile: bool) = discard diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim index c61c4ba04..147e8c3d6 100644 --- a/compiler/rodimpl.nim +++ b/compiler/rodimpl.nim @@ -52,7 +52,7 @@ proc needsRecompile(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile; return true return false -proc getModuleId*(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): int = +proc getModuleId(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): int = ## Analyse the known dependency graph. if g.config.symbolFiles == disabledSf: return getID() when false: @@ -82,8 +82,12 @@ proc getModuleId*(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): i db.exec(sql"delete from toplevelstmts where module = ?", module[0]) db.exec(sql"delete from statics where module = ?", module[0]) +proc loadModuleSym*(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): (PSym, int) = + let id = getModuleId(g, fileIdx, fullpath) + result = (g.incr.r.syms.getOrDefault(abs id), id) + proc pushType(w: var Writer, t: PType) = - if not containsOrIncl(w.tmarks, t.id): + if not containsOrIncl(w.tmarks, t.uniqueId): w.tstack.add(t) proc pushSym(w: var Writer, s: PSym) = @@ -109,7 +113,8 @@ proc encodeNode(g: ModuleGraph; fInfo: TLineInfo, n: PNode, result.add(',') encodeVInt(int n.info.line, result) result.add(',') - encodeVInt(toDbFileId(g.incr, g.config, n.info.fileIndex), result) + #encodeVInt(toDbFileId(g.incr, g.config, n.info.fileIndex), result) + encodeVInt(n.info.fileIndex.int, result) elif fInfo.line != n.info.line: result.add('?') encodeVInt(n.info.col, result) @@ -126,7 +131,7 @@ proc encodeNode(g: ModuleGraph; fInfo: TLineInfo, n: PNode, encodeVInt(cast[int32](f), result) if n.typ != nil: result.add('^') - encodeVInt(n.typ.id, result) + encodeVInt(n.typ.uniqueId, result) pushType(w, n.typ) case n.kind of nkCharLit..nkUInt64Lit: @@ -187,7 +192,10 @@ proc encodeType(g: ModuleGraph, t: PType, result: var string) = add(result, '[') encodeVInt(ord(t.kind), result) add(result, '+') - encodeVInt(t.id, result) + encodeVInt(t.uniqueId, result) + if t.id != t.uniqueId: + add(result, '+') + encodeVInt(t.id, result) if t.n != nil: encodeNode(g, unknownLineInfo(), t.n, result) if t.flags != {}: @@ -236,12 +244,16 @@ proc encodeType(g: ModuleGraph, t: PType, result: var string) = encodeVInt(s.id, result) pushSym(w, s) encodeLoc(g, t.loc, result) + if t.typeInst != nil: + add(result, '\21') + encodeVInt(t.typeInst.uniqueId, result) + pushType(w, t.typeInst) for i in countup(0, sonsLen(t) - 1): if t.sons[i] == nil: add(result, "^()") else: add(result, '^') - encodeVInt(t.sons[i].id, result) + encodeVInt(t.sons[i].uniqueId, result) pushType(w, t.sons[i]) proc encodeLib(g: ModuleGraph, lib: PLib, info: TLineInfo, result: var string) = @@ -260,7 +272,7 @@ proc encodeInstantiations(g: ModuleGraph; s: seq[PInstantiation]; pushSym(w, t.sym) for tt in t.concreteTypes: result.add('\17') - encodeVInt(tt.id, result) + encodeVInt(tt.uniqueId, result) pushType(w, tt) result.add('\20') encodeVInt(t.compilesId, result) @@ -278,14 +290,15 @@ proc encodeSym(g: ModuleGraph, s: PSym, result: var string) = encodeStr(s.name.s, result) if s.typ != nil: result.add('^') - encodeVInt(s.typ.id, result) + encodeVInt(s.typ.uniqueId, result) pushType(w, s.typ) result.add('?') if s.info.col != -1'i16: encodeVInt(s.info.col, result) result.add(',') encodeVInt(int s.info.line, result) result.add(',') - encodeVInt(toDbFileId(g.incr, g.config, s.info.fileIndex), result) + #encodeVInt(toDbFileId(g.incr, g.config, s.info.fileIndex), result) + encodeVInt(s.info.fileIndex.int, result) if s.owner != nil: result.add('*') encodeVInt(s.owner.id, result) @@ -313,7 +326,7 @@ proc encodeSym(g: ModuleGraph, s: PSym, result: var string) = of skType, skGenericParam: for t in s.typeInstCache: result.add('\14') - encodeVInt(t.id, result) + encodeVInt(t.uniqueId, result) pushType(w, t) of routineKinds: encodeInstantiations(g, s.procInstCache, result) @@ -321,6 +334,9 @@ proc encodeSym(g: ModuleGraph, s: PSym, result: var string) = result.add('\16') encodeVInt(s.gcUnsafetyReason.id, result) pushSym(w, s.gcUnsafetyReason) + if s.transformedBody != nil: + result.add('\24') + encodeNode(g, s.info, s.transformedBody, result) of skModule, skPackage: encodeInstantiations(g, s.usedGenerics, result) # we don't serialize: @@ -361,18 +377,12 @@ proc storeType(g: ModuleGraph; t: PType) = let m = if t.owner != nil: getModule(t.owner) else: nil let mid = if m == nil: 0 else: abs(m.id) db.exec(sql"insert into types(nimid, module, data) values (?, ?, ?)", - t.id, mid, buf) + t.uniqueId, mid, buf) -proc storeNode*(g: ModuleGraph; module: PSym; n: PNode) = - if g.config.symbolFiles == disabledSf: return - var buf = newStringOfCap(160) - encodeNode(g, module.info, n, buf) - db.exec(sql"insert into toplevelstmts(module, position, data) values (?, ?, ?)", - abs(module.id), module.offset, buf) - inc module.offset +proc transitiveClosure(g: ModuleGraph) = var i = 0 while true: - if i > 10_000: + if i > 100_000: doAssert false, "loop never ends!" if w.sstack.len > 0: let s = w.sstack.pop() @@ -383,14 +393,30 @@ proc storeNode*(g: ModuleGraph; module: PSym; n: PNode) = let t = w.tstack.pop() storeType(g, t) when false: - echo "popped type ", typeToString(t), " ", t.id + echo "popped type ", typeToString(t), " ", t.uniqueId else: break inc i +proc storeNode*(g: ModuleGraph; module: PSym; n: PNode) = + if g.config.symbolFiles == disabledSf: return + var buf = newStringOfCap(160) + encodeNode(g, module.info, n, buf) + db.exec(sql"insert into toplevelstmts(module, position, data) values (?, ?, ?)", + abs(module.id), module.offset, buf) + inc module.offset + transitiveClosure(g) + proc recordStmt*(g: ModuleGraph; module: PSym; n: PNode) = storeNode(g, module, n) +proc storeFilename(g: ModuleGraph; fullpath: AbsoluteFile; fileIdx: FileIndex) = + let id = db.getValue(sql"select id from filenames where fullpath = ?", fullpath.string) + if id.len == 0: + let fullhash = hashFileCached(g.config, fileIdx, fullpath) + db.exec(sql"insert into filenames(nimid, fullpath, fullhash) values (?, ?, ?)", + int(fileIdx), fullpath.string, fullhash) + proc storeRemaining*(g: ModuleGraph; module: PSym) = if g.config.symbolFiles == disabledSf: return var stillForwarded: seq[PSym] = @[] @@ -400,6 +426,13 @@ proc storeRemaining*(g: ModuleGraph; module: PSym) = else: stillForwarded.add s swap w.forwardedSyms, stillForwarded + transitiveClosure(g) + var nimid = 0 + for x in items(g.config.m.fileInfos): + # don't store the "command line" entry: + if nimid != 0: + storeFilename(g, x.fullPath, FileIndex(nimid)) + inc nimid # ---------------- decoder ----------------------------------- @@ -426,9 +459,11 @@ proc decodeLineInfo(g; b; info: var TLineInfo) = else: info.line = uint16(decodeVInt(b.s, b.pos)) if b.s[b.pos] == ',': inc(b.pos) - info.fileIndex = fromDbFileId(g.incr, g.config, decodeVInt(b.s, b.pos)) + #info.fileIndex = fromDbFileId(g.incr, g.config, decodeVInt(b.s, b.pos)) + info.fileIndex = FileIndex decodeVInt(b.s, b.pos) proc skipNode(b) = + # ')' itself cannot be part of a string literal so that this is correct. assert b.s[b.pos] == '(' var par = 0 var pos = b.pos+1 @@ -563,13 +598,18 @@ proc loadType(g; id: int; info: TLineInfo): PType = result.kind = TTypeKind(decodeVInt(b.s, b.pos)) if b.s[b.pos] == '+': inc(b.pos) - result.id = decodeVInt(b.s, b.pos) - setId(result.id) + result.uniqueId = decodeVInt(b.s, b.pos) + setId(result.uniqueId) #if debugIds: registerID(result) else: internalError(g.config, info, "decodeType: no id") + if b.s[b.pos] == '+': + inc(b.pos) + result.id = decodeVInt(b.s, b.pos) + else: + result.id = result.uniqueId # here this also avoids endless recursion for recursive type - g.incr.r.types.add(result.id, result) + g.incr.r.types.add(result.uniqueId, result) if b.s[b.pos] == '(': result.n = decodeNode(g, b, unknownLineInfo()) if b.s[b.pos] == '$': inc(b.pos) @@ -620,6 +660,10 @@ proc loadType(g; id: int; info: TLineInfo): PType = let y = loadSym(g, decodeVInt(b.s, b.pos), info) result.methods.add((x, y)) decodeLoc(g, b, result.loc, info) + if b.s[b.pos] == '\21': + inc(b.pos) + let d = decodeVInt(b.s, b.pos) + result.typeInst = loadType(g, d, info) while b.s[b.pos] == '^': inc(b.pos) if b.s[b.pos] == '(': @@ -725,6 +769,9 @@ proc loadSymFromBlob(g; b; info: TLineInfo): PSym = if b.s[b.pos] == '\16': inc(b.pos) result.gcUnsafetyReason = loadSym(g, decodeVInt(b.s, b.pos), result.info) + if b.s[b.pos] == '\24': + inc b.pos + result.transformedBody = decodeNode(g, b, result.info) of skModule, skPackage: decodeInstantiations(g, b, result.info, result.usedGenerics) of skLet, skVar, skField, skForVar: @@ -874,20 +921,22 @@ proc setupModuleCache*(g: ModuleGraph) = if g.config.symbolFiles == writeOnlySf: removeFile(dbfile) createDir getNimcacheDir(g.config) + let ec = encodeConfig(g) if not fileExists(dbfile): db = open(connection=string dbfile, user="nim", password="", database="nim") createDb(db) - db.exec(sql"insert into config(config) values (?)", encodeConfig(g)) + db.exec(sql"insert into config(config) values (?)", ec) else: db = open(connection=string dbfile, user="nim", password="", database="nim") let oldConfig = db.getValue(sql"select config from config") - g.incr.configChanged = oldConfig != encodeConfig(g) + g.incr.configChanged = oldConfig != ec # ensure the filename IDs stay consistent: for row in db.rows(sql"select fullpath, nimid from filenames order by nimid"): let id = fileInfoIdx(g.config, AbsoluteFile row[0]) doAssert id.int == parseInt(row[1]) + db.exec(sql"update config set config = ?", ec) db.exec(sql"pragma journal_mode=off") # This MUST be turned off, otherwise it's way too slow even for testing purposes: db.exec(sql"pragma SYNCHRONOUS=off") diff --git a/compiler/sem.nim b/compiler/sem.nim index 775c9f7c9..924e53b66 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -204,7 +204,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = if n.kind == nkSym: # and sfGenSym in n.sym.flags: result = n.sym - if result.kind != kind: + if result.kind notin {kind, skTemp}: localError(c.config, n.info, "cannot use symbol of kind '" & $result.kind & "' as a '" & $kind & "'") if sfGenSym in result.flags and result.kind notin {skTemplate, skMacro, skParam}: diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 9433a7327..08917cb29 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1490,6 +1490,13 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = template resultTypeIsInferrable(typ: PType): untyped = typ.isMetaType and typ.kind != tyTypeDesc + +proc goodLineInfo(arg: PNode): TLineinfo = + if arg.kind == nkStmtListExpr and arg.len > 0: + goodLineInfo(arg[^1]) + else: + arg.info + proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = checkSonsLen(n, 2, c.config) var a = n.sons[0] @@ -1571,7 +1578,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = else: typeMismatch(c.config, n.info, lhs.typ, rhsTyp) - n.sons[1] = fitNode(c, le, rhs, n.info) + n.sons[1] = fitNode(c, le, rhs, goodLineInfo(n[1])) liftTypeBoundOps(c, lhs.typ, lhs.info) #liftTypeBoundOps(c, n.sons[0].typ, n.sons[0].info) @@ -1968,6 +1975,22 @@ proc setMs(n: PNode, s: PSym): PNode = n.sons[0] = newSymNode(s) n.sons[0].info = n.info +proc semSizeof(c: PContext, n: PNode): PNode = + if sonsLen(n) != 2: + localError(c.config, n.info, errXExpectsTypeOrValue % "sizeof") + else: + n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType}) + #restoreOldStyleType(n.sons[1]) + n.typ = getSysType(c.graph, n.info, tyInt) + + let size = getSize(c.config, n[1].typ) + if size >= 0: + result = newIntNode(nkIntLit, size) + result.info = n.info + result.typ = n.typ + else: + result = n + proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = # this is a hotspot in the compiler! result = n @@ -2039,6 +2062,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = of mRunnableExamples: if c.config.cmd == cmdDoc and n.len >= 2 and n.lastSon.kind == nkStmtList: when false: + # some of this dead code was moved to `prepareExamples` if sfMainModule in c.module.flags: let inp = toFullPath(c.config, c.module.info) if c.runnableExamples == nil: @@ -2052,6 +2076,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = result = setMs(n, s) else: result = c.graph.emptyNode + of mSizeOf: result = semSizeof(c, setMs(n, s)) of mOmpParFor: checkMinSonsLen(n, 3, c.config) result = semDirectOp(c, n, flags) @@ -2346,7 +2371,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = {checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields} var s = qualifiedLookUp(c, n, checks) if c.matchedConcept == nil: semCaptureSym(s, c.p.owner) - result = semSym(c, n, s, flags) if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator}: #performProcvarCheck(c, n, s) result = symChoice(c, n, s, scClosed) @@ -2354,6 +2378,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = markIndirect(c, result.sym) # if isGenericRoutine(result.sym): # localError(c.config, n.info, errInstantiateXExplicitly, s.name.s) + else: + result = semSym(c, n, s, flags) of nkSym: # because of the changed symbol binding, this does not mean that we # don't have to check the symbol for semantics here again! diff --git a/compiler/seminst.nim b/compiler/seminst.nim index de2e10a9b..17f61c7dd 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -118,7 +118,7 @@ proc freshGenSyms(n: PNode, owner, orig: PSym, symMap: var TIdTable) = n.sym = x elif s.owner.kind == skPackage: #echo "copied this ", s.name.s - x = copySym(s, false) + x = copySym(s) x.owner = owner idTablePut(symMap, s, x) n.sym = x @@ -337,7 +337,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, c.matchedConcept = nil let oldScope = c.currentScope while not isTopLevel(c): c.currentScope = c.currentScope.parent - result = copySym(fn, false) + result = copySym(fn) incl(result.flags, sfFromGeneric) result.owner = fn result.ast = n diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 2aae562f9..df2c084a1 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -346,7 +346,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, result.info = n.info result.typ = n.typ else: - localError(c.config, n.info, "cannot evaluate 'sizeof' because its type is not defined completely") + localError(c.config, n.info, "cannot evaluate 'sizeof' because its type is not defined completely, type: " & n[1].typ.typeToString) result = n of mAlignOf: result = newIntNode(nkIntLit, getAlign(c.config, n[1].typ)) @@ -381,7 +381,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, if n[0].sym.name.s == "=": result = semAsgnOpr(c, n) else: - result = n + result = semShallowCopy(c, n, flags) of mIsPartOf: result = semIsPartOf(c, n, flags) of mTypeTrait: result = semTypeTraits(c, n) of mAstToStr: diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 0317fd8ba..75dea069f 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -56,6 +56,7 @@ type guards: TModel # nested guards locked: seq[PNode] # locked locations gcUnsafe, isRecursive, isToplevel, hasSideEffect, inEnforcedGcSafe: bool + inEnforcedNoSideEffects: bool maxLockLevel, currLockLevel: TLockLevel config: ConfigRef graph: ModuleGraph @@ -194,10 +195,10 @@ proc markGcUnsafe(a: PEffects; reason: PNode) = when true: template markSideEffect(a: PEffects; reason: typed) = - a.hasSideEffect = true + if not a.inEnforcedNoSideEffects: a.hasSideEffect = true else: template markSideEffect(a: PEffects; reason: typed) = - a.hasSideEffect = true + if not a.inEnforcedNoSideEffects: a.hasSideEffect = true markGcUnsafe(a, reason) proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet; conf: ConfigRef) = @@ -846,15 +847,20 @@ proc track(tracked: PEffects, n: PNode) = let oldLocked = tracked.locked.len let oldLockLevel = tracked.currLockLevel var enforcedGcSafety = false + var enforceNoSideEffects = false for i in 0 ..< pragmaList.len: let pragma = whichPragma(pragmaList.sons[i]) if pragma == wLocks: lockLocations(tracked, pragmaList.sons[i]) elif pragma == wGcSafe: enforcedGcSafety = true + elif pragma == wNosideeffect: + enforceNoSideEffects = true if enforcedGcSafety: tracked.inEnforcedGcSafe = true + if enforceNoSideEffects: tracked.inEnforcedNoSideEffects = true track(tracked, n.lastSon) if enforcedGcSafety: tracked.inEnforcedGcSafe = false + if enforceNoSideEffects: tracked.inEnforcedNoSideEffects = false setLen(tracked.locked, oldLocked) tracked.currLockLevel = oldLockLevel of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 1f2b9f0b3..48aa75528 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1268,7 +1268,7 @@ proc semProcAnnotation(c: PContext, prc: PNode; x.add(prc) # recursion assures that this works for multiple macro annotations too: - var r = semOverloadedCall(c, x, x, {skMacro}, {efNoUndeclared}) + var r = semOverloadedCall(c, x, x, {skMacro, skTemplate}, {efNoUndeclared}) if r == nil: # Restore the old list of pragmas since we couldn't process this prc.sons[pragmasPos] = n @@ -1860,7 +1860,7 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode = for i in 0 ..< pragmaList.len: case whichPragma(pragmaList.sons[i]) of wLine: setLine(result, pragmaList.sons[i].info) - of wLocks, wGcSafe: + of wLocks, wGcSafe, wNosideeffect: result = n result.typ = n.sons[1].typ of wNoRewrite: @@ -1963,7 +1963,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = case n.sons[j].kind of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr, nkBlockStmt, nkState: discard - else: localError(c.config, n.sons[j].info, "unreachable statement after 'return'") + else: localError(c.config, n.sons[j].info, + "unreachable statement after 'return' statement or '{.noReturn.}' proc") else: discard if result.len == 1 and diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index f4a1b0302..a011a8fc8 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -392,7 +392,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = localError(c.config, n.info, errTypeExpected) return errorSym(c, n) result = result.typ.sym.copySym - result.typ = copyType(result.typ, result.typ.owner, true) + result.typ = exactReplica(result.typ) result.typ.flags.incl tfUnresolved if result.kind == skGenericParam: @@ -1728,7 +1728,7 @@ proc processMagicType(c: PContext, m: PSym) = of mBool: setMagicType(c.config, m, tyBool, 1) of mChar: setMagicType(c.config, m, tyChar, 1) of mString: - setMagicType(c.config, m, tyString, c.config.target.ptrSize) + setMagicType(c.config, m, tyString, szUncomputedSize) rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar)) when false: if c.config.selectedGc == gcDestructors: @@ -1758,28 +1758,28 @@ proc processMagicType(c: PContext, m: PSym) = of mVoidType: setMagicType(c.config, m, tyVoid, 0) of mArray: - setMagicType(c.config, m, tyArray, 0) + setMagicType(c.config, m, tyArray, szUncomputedSize) of mOpenArray: - setMagicType(c.config, m, tyOpenArray, 0) + setMagicType(c.config, m, tyOpenArray, szUncomputedSize) of mVarargs: - setMagicType(c.config, m, tyVarargs, 0) + setMagicType(c.config, m, tyVarargs, szUncomputedSize) of mRange: - setMagicType(c.config, m, tyRange, 0) + setMagicType(c.config, m, tyRange, szUncomputedSize) rawAddSon(m.typ, newTypeS(tyNone, c)) of mSet: - setMagicType(c.config, m, tySet, 0) + setMagicType(c.config, m, tySet, szUncomputedSize) of mUncheckedArray: - setMagicType(c.config, m, tyUncheckedArray, 0) + setMagicType(c.config, m, tyUncheckedArray, szUncomputedSize) of mSeq: - setMagicType(c.config, m, tySequence, 0) + setMagicType(c.config, m, tySequence, szUncomputedSize) 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) + setMagicType(c.config, m, tyOpt, szUncomputedSize) of mOrdinal: - setMagicType(c.config, m, tyOrdinal, 0) + setMagicType(c.config, m, tyOrdinal, szUncomputedSize) rawAddSon(m.typ, newTypeS(tyNone, c)) of mPNimrodNode: incl m.typ.flags, tfTriggersCompileTime @@ -1787,7 +1787,7 @@ proc processMagicType(c: PContext, m: PSym) = of mBuiltinType: case m.name.s of "lent": setMagicType(c.config, m, tyLent, c.config.target.ptrSize) - of "sink": setMagicType(c.config, m, tySink, 0) + of "sink": setMagicType(c.config, m, tySink, szUncomputedSize) else: localError(c.config, m.info, errTypeExpected) else: localError(c.config, m.info, errTypeExpected) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index b05fb37ae..ffa913f1d 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -233,7 +233,7 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym = #result = PSym(idTableGet(cl.symMap, s)) #if result == nil: - result = copySym(s, false) + result = copySym(s) incl(result.flags, sfFromGeneric) #idTablePut(cl.symMap, s, result) result.owner = s.owner diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 4adf0bed3..d66e8d121 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -432,7 +432,7 @@ proc handleFloatRange(f, a: PType): TTypeRelation = else: result = isNone proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) = - if fGenericOrigin != nil and last.kind == tyGenericInst and + if fGenericOrigin != nil and last.kind == tyGenericInst and last.len-1 == fGenericOrigin.len: for i in countup(1, sonsLen(fGenericOrigin) - 1): let x = PType(idTableGet(c.bindings, fGenericOrigin.sons[i])) @@ -1034,8 +1034,8 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, template doBind: bool = trDontBind notin flags - # var and static arguments match regular modifier-free types - var a = maybeSkipDistinct(c, aOrig.skipTypes({tyStatic, tyVar, tyLent}), c.calleeSym) + # var, sink and static arguments match regular modifier-free types + var a = maybeSkipDistinct(c, aOrig.skipTypes({tyStatic, tyVar, tyLent, tySink}), c.calleeSym) # XXX: Theoretically, maybeSkipDistinct could be called before we even # start the param matching process. This could be done in `prepareOperand` # for example, but unfortunately `prepareOperand` is not called in certain @@ -1045,7 +1045,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, return typeRel(c, f, lastSon(aOrig)) if a.kind == tyGenericInst and - skipTypes(f, {tyVar, tyLent}).kind notin { + skipTypes(f, {tyVar, tyLent, tySink}).kind notin { tyGenericBody, tyGenericInvocation, tyGenericInst, tyGenericParam} + tyTypeClasses: return typeRel(c, f, lastSon(a)) @@ -1835,7 +1835,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, if destIsGeneric: dest = generateTypeInstance(c, m.bindings, arg, dest) let fdest = typeRel(m, f, dest) - if fdest in {isEqual, isGeneric}: + if fdest in {isEqual, isGeneric} and not (dest.kind == tyLent and f.kind == tyVar): markUsed(c.config, arg.info, c.converters[i], c.graph.usageSym) var s = newSymNode(c.converters[i]) s.typ = c.converters[i].typ @@ -2231,8 +2231,15 @@ proc matchesAux(c: PContext, n, nOrig: PNode, else: m.state = csNoMatch return + if formal.typ.kind == tyVar: - if not n.isLValue: + let arg_converter = if arg.kind == nkHiddenDeref: arg[0] else: arg + if arg_converter.kind == nkHiddenCallConv: + if arg_converter.typ.kind != tyVar: + m.state = csNoMatch + m.mutabilityProblem = uint8(f-1) + return + elif not n.isLValue: m.state = csNoMatch m.mutabilityProblem = uint8(f-1) return @@ -2243,6 +2250,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, else: 0 # iterates over the actual given arguments a = 1 + arg: PNode # current prepared argument m.state = csMatch # until proven otherwise m.call = newNodeI(n.kind, n.info) @@ -2297,7 +2305,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m.typedescMatched = false n.sons[a].sons[1] = prepareOperand(c, formal.typ, n.sons[a].sons[1]) n.sons[a].typ = n.sons[a].sons[1].typ - var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, + arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, n.sons[a].sons[1], n.sons[a].sons[1]) if arg == nil: m.state = csNoMatch @@ -2334,7 +2342,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m.typedescMatched = false incl(marker, formal.position) n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) - var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, + arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, n.sons[a], nOrig.sons[a]) if arg != nil and m.baseTypeMatch and container != nil: addSon(container, arg) @@ -2368,7 +2376,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m.baseTypeMatch = false m.typedescMatched = false n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) - var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, + arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, n.sons[a], nOrig.sons[a]) if arg == nil: m.state = csNoMatch diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index 4791788fa..a34383d9f 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -52,6 +52,7 @@ proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt = proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset: BiggestInt): tuple[offset, align: BiggestInt] = ## ``offset`` is the offset within the object, after the node has been written, no padding bytes added ## ``align`` maximum alignment from all sub nodes + assert n != nil if n.typ != nil and n.typ.size == szIllegalRecursion: result.offset = szIllegalRecursion result.align = szIllegalRecursion @@ -177,7 +178,7 @@ proc computePackedObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOf proc computeSizeAlign(conf: ConfigRef; typ: PType) = ## computes and sets ``size`` and ``align`` members of ``typ`` - + assert typ != nil let hasSize = typ.size != szUncomputedSize let hasAlign = typ.align != szUncomputedSize @@ -220,7 +221,7 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = typ.align = int16(conf.target.ptrSize) of tyString: - if tfHasAsgn in typ.flags: + if conf.selectedGC == gcDestructors: typ.size = conf.target.ptrSize * 2 else: typ.size = conf.target.ptrSize @@ -237,13 +238,13 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = # recursive tuplers are not allowed and should be detected in the frontend if base.kind == tyTuple: computeSizeAlign(conf, base) - if base.size == szIllegalRecursion: - typ.size = szIllegalRecursion - typ.align = szIllegalRecursion + if base.size < 0: + typ.size = base.size + typ.align = base.align return typ.align = int16(conf.target.ptrSize) - if typ.kind == tySequence and tfHasAsgn in typ.flags: + if typ.kind == tySequence and conf.selectedGC == gcDestructors: typ.size = conf.target.ptrSize * 2 else: typ.size = conf.target.ptrSize @@ -310,9 +311,9 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = for i in countup(0, sonsLen(typ) - 1): let child = typ.sons[i] computeSizeAlign(conf, child) - if child.size == szIllegalRecursion: - typ.size = szIllegalRecursion - typ.align = szIllegalRecursion + if child.size < 0: + typ.size = child.size + typ.align = child.align return maxAlign = max(maxAlign, child.align) sizeAccum = align(sizeAccum, child.align) + child.size @@ -379,7 +380,7 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = typ.size = typ.lastSon.size typ.align = typ.lastSon.align - of tyGenericInst, tyDistinct, tyGenericBody, tyAlias: + of tyGenericInst, tyDistinct, tyGenericBody, tyAlias, tySink: computeSizeAlign(conf, typ.lastSon) typ.size = typ.lastSon.size typ.align = typ.lastSon.align diff --git a/compiler/trees.nim b/compiler/trees.nim index fb523de9d..ca2360e12 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -92,8 +92,7 @@ proc isCaseObj*(n: PNode): bool = proc isDeepConstExpr*(n: PNode): bool = case n.kind - of nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, - nkFloatLit..nkFloat64Lit, nkNilLit: + of nkCharLit..nkNilLit: result = true of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv: result = isDeepConstExpr(n.sons[1]) diff --git a/compiler/vm.nim b/compiler/vm.nim index 5b5b807bb..7e7ec8903 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -960,6 +960,17 @@ 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 opcSymIsInstantiationOf: + decodeBC(rkInt) + let a = regs[rb].node + let b = regs[rc].node + if a.kind == nkSym and a.sym.kind in skProcKinds and + b.kind == nkSym and b.sym.kind in skProcKinds: + regs[ra].intVal = + if sfFromGeneric in a.sym.flags and a.sym.owner == b.sym: 1 + else: 0 + else: + stackTrace(c, tos, pc, "node is not a proc symbol") of opcEcho: let rb = instr.regB if rb == 1: diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 25ace3cdd..493078f74 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -147,7 +147,8 @@ type opcTypeTrait, opcMarshalLoad, opcMarshalStore, opcToNarrowInt, - opcSymOwner + opcSymOwner, + opcSymIsInstantiationOf TBlock* = object label*: PSym diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index c37ec7c6b..1f2a3e6d1 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1148,6 +1148,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mGetImpl: genUnaryABC(c, n, dest, opcGetImpl) of mGetImplTransf: genUnaryABC(c, n, dest, opcGetImplTransf) of mSymOwner: genUnaryABC(c, n, dest, opcSymOwner) + of mSymIsInstantiationOf: genBinaryABC(c, n, dest, opcSymIsInstantiationOf) of mNChild: genBinaryABC(c, n, dest, opcNChild) of mNSetChild: genVoidABC(c, n, dest, opcNSetChild) of mNDel: genVoidABC(c, n, dest, opcNDel) @@ -1238,8 +1239,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opcCallSite, dest) of mNGenSym: genBinaryABC(c, n, dest, opcGenSym) - of mMinI, mMaxI, mAbsF64, mMinF64, mMaxF64, mAbsI, - mDotDot: + of mMinI, mMaxI, mAbsF64, mMinF64, mMaxF64, mAbsI, mDotDot: c.genCall(n, dest) of mExpandToAst: if n.len != 2: @@ -1258,6 +1258,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = globalError(c.config, n.info, "cannot evaluate 'sizeof/alignof' because its type is not defined completely") of mRunnableExamples: discard "just ignore any call to runnableExamples" + of mDestroy: discard "ignore calls to the default destructor" else: # mGCref, mGCunref, globalError(c.config, n.info, "cannot generate code for: " & $m) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index f87ab4508..75873bfe8 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -11,7 +11,7 @@ #import vmdeps, vm from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin, arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc, - floor, ceil, `mod`, fmod + floor, ceil, `mod` from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir @@ -102,7 +102,6 @@ proc registerAdditionalOps*(c: PCtx) = wrap1f_math(trunc) wrap1f_math(floor) wrap1f_math(ceil) - wrap2f_math(fmod) proc `mod Wrapper`(a: VmArgs) {.nimcall.} = setResult(a, `mod`(getFloat(a, 0), getFloat(a, 1))) |