diff options
author | jrfondren <41455523+jrfondren@users.noreply.github.com> | 2019-05-03 13:03:45 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-03 13:03:45 -0500 |
commit | 8cadeb960597a47a09100bdda05672f177d158d2 (patch) | |
tree | afe59c7bc9f5502801754e0f7fead84552a3d4e6 /compiler | |
parent | 6dfde0e931176405491987e14969f68d81957730 (diff) | |
parent | 515ab81477c1c3e4811c4fbf43a3ff81b87be970 (diff) | |
download | Nim-8cadeb960597a47a09100bdda05672f177d158d2.tar.gz |
Merge branch 'devel' into expand-amb-identifier-output
Diffstat (limited to 'compiler')
31 files changed, 890 insertions, 499 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 75e67ac67..aeb19ae1a 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -644,7 +644,7 @@ type mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mNewString, mNewStringOfCap, mParseBiggestFloat, mMove, mWasMoved, mDestroy, - mDefault, mAccessEnv, mReset, + mDefault, mUnown, mAccessEnv, mReset, mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, mOrdinal, @@ -877,6 +877,13 @@ type TTypeSeq* = seq[PType] TLockLevel* = distinct int16 + + TTypeAttachedOp* = enum ## as usual, order is important here + attachedDestructor, + attachedAsgn, + attachedSink, + attachedDeepCopy + TType* {.acyclic.} = object of TIdObj # \ # types are identical iff they have the # same id; there may be multiple copies of a type @@ -897,12 +904,7 @@ type owner*: PSym # the 'owner' of the type sym*: PSym # types have the sym associated with them # it is used for converting types to strings - destructor*: PSym # destructor. warning: nil here may not necessary - # mean that there is no destructor. - # see instantiateDestructor in semdestruct.nim - deepCopy*: PSym # overriden 'deepCopy' operation - assignment*: PSym # overriden '=' operation - sink*: PSym # overriden '=sink' operation + attachedOps*: array[TTypeAttachedOp, PSym] # destructors, etc. methods*: seq[(int,PSym)] # attached methods size*: BiggestInt # the size of the type in bytes # -1 means that the size is unkwown @@ -1275,6 +1277,7 @@ const UnspecifiedLockLevel* = TLockLevel(-1'i16) MaxLockLevel* = 1000'i16 UnknownLockLevel* = TLockLevel(1001'i16) + AttachedOpToStr*: array[TTypeAttachedOp, string] = ["=destroy", "=", "=sink", "=deepcopy"] proc `$`*(x: TLockLevel): string = if x.ord == UnspecifiedLockLevel.ord: result = "<unspecified>" @@ -1341,10 +1344,7 @@ proc assignType*(dest, src: PType) = dest.n = src.n dest.size = src.size dest.align = src.align - dest.destructor = src.destructor - dest.deepCopy = src.deepCopy - dest.sink = src.sink - dest.assignment = src.assignment + dest.attachedOps = src.attachedOps dest.lockLevel = src.lockLevel # this fixes 'type TLock = TSysLock': if src.sym != nil: @@ -1831,3 +1831,7 @@ proc addParam*(procType: PType; param: PSym) = param.position = procType.len-1 addSon(procType.n, newSymNode(param)) rawAddSon(procType, param.typ) + +template destructor*(t: PType): PSym = t.attachedOps[attachedDestructor] +template assignment*(t: PType): PSym = t.attachedOps[attachedAsgn] +template asink*(t: PType): PSym = t.attachedOps[attachedSink] diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index e46e2c47d..1e856ad73 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -46,13 +46,23 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, genAssignment(p, d, tmp, {}) # no need for deep copying else: add(pl, ~")") - if p.module.compileToCpp and lfSingleUse in d.flags: - # do not generate spurious temporaries for C++! For C we're better off - # with them to prevent undefined behaviour and because the codegen - # is free to emit expressions multiple times! - d.k = locCall - d.r = pl - excl d.flags, lfSingleUse + if p.module.compileToCpp: + if lfSingleUse in d.flags: + # do not generate spurious temporaries for C++! For C we're better off + # with them to prevent undefined behaviour and because the codegen + # is free to emit expressions multiple times! + d.k = locCall + d.r = pl + excl d.flags, lfSingleUse + else: + if d.k == locNone and p.splitDecls == 0: + getTempCpp(p, typ.sons[0], d, pl) + else: + if d.k == locNone: getTemp(p, typ.sons[0], d) + var list: TLoc + initLoc(list, locCall, d.lode, OnUnknown) + list.r = pl + genAssignment(p, d, list, {}) # no need for deep copying else: if d.k == locNone: getTemp(p, typ.sons[0], d) assert(d.t != nil) # generate an assignment to d: @@ -218,10 +228,12 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = genParamLoop(pl) template genCallPattern {.dirty.} = - lineF(p, cpsStmts, callPattern & ";$n", [rdLoc(op), pl, pl.addComma, rawProc]) + if tfIterator in typ.flags: + lineF(p, cpsStmts, PatIter & ";$n", [rdLoc(op), pl, pl.addComma, rawProc]) + else: + lineF(p, cpsStmts, PatProc & ";$n", [rdLoc(op), pl, pl.addComma, rawProc]) let rawProc = getRawProcType(p, typ) - let callPattern = if tfIterator in typ.flags: PatIter else: PatProc if typ.sons[0] != nil: if isInvalidReturnType(p.config, typ.sons[0]): if sonsLen(ri) > 1: add(pl, ~", ") @@ -246,7 +258,11 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = assert(d.t != nil) # generate an assignment to d: var list: TLoc initLoc(list, locCall, d.lode, OnUnknown) - list.r = callPattern % [rdLoc(op), pl, pl.addComma, rawProc] + if tfIterator in typ.flags: + list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc] + else: + list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc] + genAssignment(p, d, list, {}) # no need for deep copying else: genCallPattern() diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 9f4d57359..e03c4b1c1 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -99,17 +99,17 @@ proc bitSetToWord(s: TBitSet, size: int): BiggestInt = if j < len(s): result = result or (ze64(s[j]) shl (j * 8)) proc genRawSetData(cs: TBitSet, size: int): Rope = - var frmt: FormatStr if size > 8: result = "{$n" % [] for i in countup(0, size - 1): if i < size - 1: # not last iteration? - if (i + 1) mod 8 == 0: frmt = "0x$1,$n" - else: frmt = "0x$1, " + if (i + 1) mod 8 == 0: + addf(result, "0x$1,$n", [rope(toHex(ze64(cs[i]), 2))]) + else: + addf(result, "0x$1, ", [rope(toHex(ze64(cs[i]), 2))]) else: - frmt = "0x$1}$n" - addf(result, frmt, [rope(toHex(ze64(cs[i]), 2))]) + addf(result, "0x$1}$n", [rope(toHex(ze64(cs[i]), 2))]) else: result = intLiteral(bitSetToWord(cs, size)) # result := rope('0x' + ToHex(bitSetToWord(cs, size), size * 2)) @@ -510,10 +510,7 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = "mulInt64", "divInt64", "modInt64", "addInt64", "subInt64" ] - opr: array[mAddI..mPred, string] = [ - "($#)($# + $#)", "($#)($# - $#)", "($#)($# * $#)", - "($#)($# / $#)", "($#)($# % $#)", - "($#)($# + $#)", "($#)($# - $#)"] + opr: array[mAddI..mPred, string] = ["+", "-", "*", "/", "%", "+", "-"] var a, b: TLoc assert(e.sons[1].typ != nil) assert(e.sons[2].typ != nil) @@ -523,7 +520,7 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = # later via 'chckRange' let t = e.typ.skipTypes(abstractRange) if optOverflowCheck notin p.options: - let res = opr[m] % [getTypeDesc(p.module, e.typ), rdLoc(a), rdLoc(b)] + let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)] putIntoDest(p, d, e, res) else: let res = binaryArithOverflowRaw(p, t, a, b, @@ -531,11 +528,6 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res]) proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = - const - opr: array[mUnaryMinusI..mAbsI, string] = [ - mUnaryMinusI: "((NI$2)-($1))", - mUnaryMinusI64: "-($1)", - mAbsI: "($1 > 0? ($1) : -($1))"] var a: TLoc t: PType @@ -545,54 +537,17 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = if optOverflowCheck in p.options: linefmt(p, cpsStmts, "if ($1 == $2) #raiseOverflow();$n", [rdLoc(a), intLiteral(firstOrd(p.config, t))]) - putIntoDest(p, d, e, opr[m] % [rdLoc(a), rope(getSize(p.config, t) * 8)]) + case m + of mUnaryMinusI: + putIntoDest(p, d, e, "((NI$2)-($1))" % [rdLoc(a), rope(getSize(p.config, t) * 8)]) + of mUnaryMinusI64: + putIntoDest(p, d, e, "-($1)" % [rdLoc(a)]) + of mAbsI: + putIntoDest(p, d, e, "($1 > 0? ($1) : -($1))" % [rdLoc(a)]) + else: + assert(false, $m) proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = - const - binArithTab: array[mAddF64..mXor, string] = [ - "(($4)($1) + ($4)($2))", # AddF64 - "(($4)($1) - ($4)($2))", # SubF64 - "(($4)($1) * ($4)($2))", # MulF64 - "(($4)($1) / ($4)($2))", # DivF64 - "($4)((NU$5)($1) >> (NU$3)($2))", # ShrI - "($4)((NU$3)($1) << (NU$3)($2))", # ShlI - "($4)((NI$3)($1) >> (NU$3)($2))", # AshrI - "($4)($1 & $2)", # BitandI - "($4)($1 | $2)", # BitorI - "($4)($1 ^ $2)", # BitxorI - "(($1 <= $2) ? $1 : $2)", # MinI - "(($1 >= $2) ? $1 : $2)", # MaxI - "(($1 <= $2) ? $1 : $2)", # MinF64 - "(($1 >= $2) ? $1 : $2)", # MaxF64 - "($4)((NU$3)($1) + (NU$3)($2))", # AddU - "($4)((NU$3)($1) - (NU$3)($2))", # SubU - "($4)((NU$3)($1) * (NU$3)($2))", # MulU - "($4)((NU$3)($1) / (NU$3)($2))", # DivU - "($4)((NU$3)($1) % (NU$3)($2))", # ModU - "($1 == $2)", # EqI - "($1 <= $2)", # LeI - "($1 < $2)", # LtI - "($1 == $2)", # EqF64 - "($1 <= $2)", # LeF64 - "($1 < $2)", # LtF64 - "((NU$3)($1) <= (NU$3)($2))", # LeU - "((NU$3)($1) < (NU$3)($2))", # LtU - "((NU64)($1) <= (NU64)($2))", # LeU64 - "((NU64)($1) < (NU64)($2))", # LtU64 - "($1 == $2)", # EqEnum - "($1 <= $2)", # LeEnum - "($1 < $2)", # LtEnum - "((NU8)($1) == (NU8)($2))", # EqCh - "((NU8)($1) <= (NU8)($2))", # LeCh - "((NU8)($1) < (NU8)($2))", # LtCh - "($1 == $2)", # EqB - "($1 <= $2)", # LeB - "($1 < $2)", # LtB - "($1 == $2)", # EqRef - "($1 == $2)", # EqPtr - "($1 <= $2)", # LePtr - "($1 < $2)", # LtPtr - "($1 != $2)"] # Xor var a, b: TLoc s, k: BiggestInt @@ -603,9 +558,59 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = # BUGFIX: cannot use result-type here, as it may be a boolean s = max(getSize(p.config, a.t), getSize(p.config, b.t)) * 8 k = getSize(p.config, a.t) * 8 - putIntoDest(p, d, e, - binArithTab[op] % [rdLoc(a), rdLoc(b), rope(s), - getSimpleTypeDesc(p.module, e.typ), rope(k)]) + + template applyFormat(frmt: untyped) = + putIntoDest(p, d, e, frmt % [ + rdLoc(a), rdLoc(b), rope(s), + getSimpleTypeDesc(p.module, e.typ), rope(k)] + ) + + case op + of mAddF64: applyFormat("(($4)($1) + ($4)($2))") + of mSubF64: applyFormat("(($4)($1) - ($4)($2))") + of mMulF64: applyFormat("(($4)($1) * ($4)($2))") + of mDivF64: applyFormat("(($4)($1) / ($4)($2))") + of mShrI: applyFormat("($4)((NU$5)($1) >> (NU$3)($2))") + of mShlI: applyFormat("($4)((NU$3)($1) << (NU$3)($2))") + of mAshrI: applyFormat("($4)((NI$3)($1) >> (NU$3)($2))") + of mBitandI: applyFormat("($4)($1 & $2)") + of mBitorI: applyFormat("($4)($1 | $2)") + of mBitxorI: applyFormat("($4)($1 ^ $2)") + of mMinI: applyFormat("(($1 <= $2) ? $1 : $2)") + of mMaxI: applyFormat("(($1 >= $2) ? $1 : $2)") + of mMinF64: applyFormat("(($1 <= $2) ? $1 : $2)") + of mMaxF64: applyFormat("(($1 >= $2) ? $1 : $2)") + of mAddU: applyFormat("($4)((NU$3)($1) + (NU$3)($2))") + of mSubU: applyFormat("($4)((NU$3)($1) - (NU$3)($2))") + of mMulU: applyFormat("($4)((NU$3)($1) * (NU$3)($2))") + of mDivU: applyFormat("($4)((NU$3)($1) / (NU$3)($2))") + of mModU: applyFormat("($4)((NU$3)($1) % (NU$3)($2))") + of mEqI: applyFormat("($1 == $2)") + of mLeI: applyFormat("($1 <= $2)") + of mLtI: applyFormat("($1 < $2)") + of mEqF64: applyFormat("($1 == $2)") + of mLeF64: applyFormat("($1 <= $2)") + of mLtF64: applyFormat("($1 < $2)") + of mLeU: applyFormat("((NU$3)($1) <= (NU$3)($2))") + of mLtU: applyFormat("((NU$3)($1) < (NU$3)($2))") + of mLeU64: applyFormat("((NU64)($1) <= (NU64)($2))") + of mLtU64: applyFormat("((NU64)($1) < (NU64)($2))") + of mEqEnum: applyFormat("($1 == $2)") + of mLeEnum: applyFormat("($1 <= $2)") + of mLtEnum: applyFormat("($1 < $2)") + of mEqCh: applyFormat("((NU8)($1) == (NU8)($2))") + of mLeCh: applyFormat("((NU8)($1) <= (NU8)($2))") + of mLtCh: applyFormat("((NU8)($1) < (NU8)($2))") + of mEqB: applyFormat("($1 == $2)") + of mLeB: applyFormat("($1 <= $2)") + of mLtB: applyFormat("($1 < $2)") + of mEqRef: applyFormat("($1 == $2)") + of mEqUntracedRef: applyFormat("($1 == $2)") + of mLePtr: applyFormat("($1 <= $2)") + of mLtPtr: applyFormat("($1 < $2)") + of mXor: applyFormat("($1 != $2)") + else: + assert(false, $op) proc genEqProc(p: BProc, e: PNode, d: var TLoc) = var a, b: TLoc @@ -628,7 +633,8 @@ proc genIsNil(p: BProc, e: PNode, d: var TLoc) = proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = const - unArithTab: array[mNot..mToBiggestInt, string] = ["!($1)", # Not + unArithTab: array[mNot..mToBiggestInt, string] = [ + "!($1)", # Not "$1", # UnaryPlusI "($3)((NU$2) ~($1))", # BitnotI "$1", # UnaryPlusF64 @@ -654,10 +660,58 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = assert(e.sons[1].typ != nil) initLocExpr(p, e.sons[1], a) t = skipTypes(e.typ, abstractRange) - putIntoDest(p, d, e, - unArithTab[op] % [rdLoc(a), rope(getSize(p.config, t) * 8), + + + template applyFormat(frmt: untyped) = + putIntoDest(p, d, e, frmt % [rdLoc(a), rope(getSize(p.config, t) * 8), getSimpleTypeDesc(p.module, e.typ)]) + + case op + of mNot: + applyFormat("!($1)") + of mUnaryPlusI: + applyFormat("$1") + of mBitnotI: + applyFormat("($3)((NU$2) ~($1))") + of mUnaryPlusF64: + applyFormat("$1") + of mUnaryMinusF64: + applyFormat("-($1)") + of mAbsF64: + applyFormat("($1 < 0? -($1) : ($1))") + # BUGFIX: fabs() makes problems for Tiny C + of mZe8ToI: + applyFormat("(($3)(NU)(NU8)($1))") + of mZe8ToI64: + applyFormat("(($3)(NU64)(NU8)($1))") + of mZe16ToI: + applyFormat("(($3)(NU)(NU16)($1))") + of mZe16ToI64: + applyFormat("(($3)(NU64)(NU16)($1))") + of mZe32ToI64: + applyFormat("(($3)(NU64)(NU32)($1))") + of mZeIToI64: + applyFormat("(($3)(NU64)(NU)($1))") + of mToU8: + applyFormat("(($3)(NU8)(NU)($1))") + of mToU16: + applyFormat("(($3)(NU16)(NU)($1))") + of mToU32: + applyFormat("(($3)(NU32)(NU64)($1))") + of mToFloat: + applyFormat("((double) ($1))") + of mToBiggestFloat: + applyFormat("((double) ($1))") + of mToInt: + applyFormat("float64ToInt32($1)") + of mToBiggestInt: + applyFormat("float64ToInt64($1)") + else: + assert false, $op + + + proc isCppRef(p: BProc; typ: PType): bool {.inline.} = result = p.module.compileToCpp and skipTypes(typ, abstractInstOwned).kind == tyVar and @@ -942,6 +996,23 @@ proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) = of tyTuple: genTupleElem(p, n, d) else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')') +proc isSimpleExpr(n: PNode): bool = + # calls all the way down --> can stay expression based + case n.kind + of nkCallKinds, nkDotExpr, nkPar, nkTupleConstr, + nkObjConstr, nkBracket, nkCurly, nkHiddenDeref, nkDerefExpr, nkHiddenAddr, + nkHiddenStdConv, nkHiddenSubConv, nkConv, nkAddr: + for c in n: + if not isSimpleExpr(c): return false + result = true + of nkStmtListExpr: + for i in 0..n.len-2: + if n[i].kind notin {nkCommentStmt, nkEmpty}: return false + result = isSimpleExpr(n.lastSon) + else: + if n.isAtom: + result = true + proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = # how to generate code? # 'expr1 and expr2' becomes: @@ -963,24 +1034,41 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = # tmp = a # end: # a = tmp - var - L: TLabel - tmp: TLoc - getTemp(p, e.typ, tmp) # force it into a temp! - inc p.splitDecls - expr(p, e.sons[1], tmp) - L = getLabel(p) - if m == mOr: - lineF(p, cpsStmts, "if ($1) goto $2;$n", [rdLoc(tmp), L]) - else: - lineF(p, cpsStmts, "if (!($1)) goto $2;$n", [rdLoc(tmp), L]) - expr(p, e.sons[2], tmp) - fixLabel(p, L) - if d.k == locNone: - d = tmp + when false: + #if isSimpleExpr(e) and p.module.compileToCpp: + var tmpA, tmpB: TLoc + #getTemp(p, e.typ, tmpA) + #getTemp(p, e.typ, tmpB) + initLocExprSingleUse(p, e.sons[1], tmpA) + initLocExprSingleUse(p, e.sons[2], tmpB) + tmpB.k = locExpr + if m == mOr: + tmpB.r = "((" & rdLoc(tmpA) & ")||(" & rdLoc(tmpB) & "))" + else: + tmpB.r = "((" & rdLoc(tmpA) & ")&&(" & rdLoc(tmpB) & "))" + if d.k == locNone: + d = tmpB + else: + genAssignment(p, d, tmpB, {}) else: - genAssignment(p, d, tmp, {}) # no need for deep copying - dec p.splitDecls + var + L: TLabel + tmp: TLoc + getTemp(p, e.typ, tmp) # force it into a temp! + inc p.splitDecls + expr(p, e.sons[1], tmp) + L = getLabel(p) + if m == mOr: + lineF(p, cpsStmts, "if ($1) goto $2;$n", [rdLoc(tmp), L]) + else: + lineF(p, cpsStmts, "if (!($1)) goto $2;$n", [rdLoc(tmp), L]) + expr(p, e.sons[2], tmp) + fixLabel(p, L) + if d.k == locNone: + d = tmp + else: + genAssignment(p, d, tmp, {}) # no need for deep copying + dec p.splitDecls proc genEcho(p: BProc, n: PNode) = # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)`` @@ -1675,7 +1763,7 @@ proc fewCmps(conf: ConfigRef; s: PNode): bool = else: result = sonsLen(s) <= 8 # 8 seems to be a good value -proc binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) = +template binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) = putIntoDest(p, d, e, frmt % [rdLoc(a), rdSetElemLoc(p.config, b, a.t)]) proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) = @@ -1686,7 +1774,7 @@ proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) = of 8: binaryExprIn(p, e, a, b, d, "(($1 &((NU64)1<<((NU)($2)&63U)))!=0)") else: binaryExprIn(p, e, a, b, d, "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)") -proc binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) = +template binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) = var a, b: TLoc assert(d.k == locNone) initLocExpr(p, e.sons[1], a) @@ -1753,13 +1841,19 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of 1, 2, 4, 8: case op of mIncl: - var ts = "NU" & $(size * 8) - binaryStmtInExcl(p, e, d, - "$1 |= ((" & ts & ")1)<<(($2)%(sizeof(" & ts & ")*8));$n") + case size + of 1: binaryStmtInExcl(p, e, d, "$1 |= ((NU8)1)<<(($2) & 7);$n") + of 2: binaryStmtInExcl(p, e, d, "$1 |= ((NU16)1)<<(($2) & 15);$n") + of 4: binaryStmtInExcl(p, e, d, "$1 |= ((NU32)1)<<(($2) & 31);$n") + of 8: binaryStmtInExcl(p, e, d, "$1 |= ((NU64)1)<<(($2) & 63);$n") + else: assert(false, $size) of mExcl: - var ts = "NU" & $(size * 8) - binaryStmtInExcl(p, e, d, "$1 &= ~(((" & ts & ")1) << (($2) % (sizeof(" & - ts & ")*8)));$n") + case size + of 1: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU8)1) << (($2) & 7));$n") + of 2: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU16)1) << (($2) & 15));$n") + of 4: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU32)1) << (($2) & 31));$n") + of 8: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU64)1) << (($2) & 63));$n") + else: assert(false, $size) of mCard: if size <= 4: unaryExprChar(p, e, d, "#countBits32($1)") else: unaryExprChar(p, e, d, "#countBits64($1)") @@ -1824,14 +1918,14 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) = var a: TLoc initLocExpr(p, e.sons[1], a) let etyp = skipTypes(e.typ, abstractRange+{tyOwned}) + let srcTyp = skipTypes(e.sons[1].typ, abstractRange) if etyp.kind in ValueTypes and lfIndirect notin a.flags: putIntoDest(p, d, e, "(*($1*) ($2))" % [getTypeDesc(p.module, e.typ), addrLoc(p.config, a)], a.storage) - elif etyp.kind == tyProc and etyp.callConv == ccClosure: + elif etyp.kind == tyProc and etyp.callConv == ccClosure and srcTyp.callConv != ccClosure: putIntoDest(p, d, e, "(($1) ($2))" % [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage) else: - let srcTyp = skipTypes(e.sons[1].typ, abstractRange) # C++ does not like direct casts from pointer to shorter integral types if srcTyp.kind in {tyPtr, tyPointer} and etyp.kind in IntegralTypes: putIntoDest(p, d, e, "(($1) (ptrdiff_t) ($2))" % @@ -2085,7 +2179,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mCharToStr: genDollar(p, e, d, "#nimCharToStr($1)") of mFloatToStr: genDollar(p, e, d, "#nimFloatToStr($1)") of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)") - of mStrToStr: expr(p, e.sons[1], d) + of mStrToStr, mUnown: expr(p, e.sons[1], d) of mEnumToStr: if optNimV2 in p.config.globalOptions: genEnumToStr(p, e, d) @@ -2228,14 +2322,14 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, it.sons[0], a) initLocExpr(p, it.sons[1], b) lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" & - "$2 |=((" & ts & ")(1)<<(($1)%(sizeof(" & ts & ")*8)));$n", [ + "$2 |=(($5)(1)<<(($1)%(sizeof($5)*8)));$n", [ rdLoc(idx), rdLoc(d), rdSetElemLoc(p.config, a, e.typ), - rdSetElemLoc(p.config, b, e.typ)]) + rdSetElemLoc(p.config, b, e.typ), rope(ts)]) else: initLocExpr(p, it, a) lineF(p, cpsStmts, - "$1 |=((" & ts & ")(1)<<(($2)%(sizeof(" & ts & ")*8)));$n", - [rdLoc(d), rdSetElemLoc(p.config, a, e.typ)]) + "$1 |=(($3)(1)<<(($2)%(sizeof($3)*8)));$n", + [rdLoc(d), rdSetElemLoc(p.config, a, e.typ), rope(ts)]) proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = var rec: TLoc @@ -2609,7 +2703,10 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = genProc(p.module, prc) of nkParForStmt: genParForStmt(p, n) of nkState: genState(p, n) - of nkGotoState: genGotoState(p, n) + of nkGotoState: + # simply never set it back to 0 here from here on... + inc p.splitDecls + genGotoState(p, n) of nkBreakState: genBreakState(p, n, d) else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind") diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 386bedae9..abddb7c6c 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -207,7 +207,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) = let tryStmt = p.nestedTryStmts.pop if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions: # Pop safe points generated by try - if not tryStmt.inExcept and not isDefined(p.config, "nimQuirky"): + if not tryStmt.inExcept: linefmt(p, cpsStmts, "#popSafePoint();$n", []) # Pop this try-stmt of the list of nested trys @@ -227,8 +227,9 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) = if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions: # Pop exceptions that was handled by the # except-blocks we are in - for i in countdown(howManyExcepts-1, 0): - linefmt(p, cpsStmts, "#popCurrentException();$n", []) + if not p.noSafePoints: + for i in countdown(howManyExcepts-1, 0): + linefmt(p, cpsStmts, "#popCurrentException();$n", []) proc genGotoState(p: BProc, n: PNode) = # we resist the temptation to translate it into duff's device as it later @@ -449,7 +450,7 @@ proc genReturnStmt(p: BProc, t: PNode) = blockLeaveActions(p, howManyTrys = p.nestedTryStmts.len, howManyExcepts = p.inExceptBlockLen) - if (p.finallySafePoints.len > 0) and not isDefined(p.config, "nimQuirky"): + if (p.finallySafePoints.len > 0) and not p.noSafePoints: # If we're in a finally block, and we came here by exception # consume it before we return. var safePoint = p.finallySafePoints[p.finallySafePoints.len-1] @@ -1004,6 +1005,8 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = (t.kind == nkHiddenTryStmt and sfSystemModule in p.module.module.flags) if not quirkyExceptions: p.module.includeHeader("<setjmp.h>") + else: + p.noSafePoints = true genLineDir(p, t) discard cgsym(p.module, "Exception") var safePoint: Rope @@ -1021,7 +1024,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint]) startBlock(p, "if ($1.status == 0) {$n", [safePoint]) var length = sonsLen(t) - add(p.nestedTryStmts, (t, false)) + add(p.nestedTryStmts, (t, quirkyExceptions)) expr(p, t.sons[0], d) if not quirkyExceptions: linefmt(p, cpsStmts, "#popSafePoint();$n", []) @@ -1135,9 +1138,9 @@ proc genAsmStmt(p: BProc, t: PNode) = # work: if p.prc == nil: # top level asm statement? - addf(p.module.s[cfsProcHeaders], CC[p.config.cCompiler].asmStmtFrmt, [s]) + add(p.module.s[cfsProcHeaders], runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s])) else: - lineF(p, cpsStmts, CC[p.config.cCompiler].asmStmtFrmt, [s]) + add(p.s(cpsStmts), indentLine(p, runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s]))) proc determineSection(n: PNode): TCFileSection = result = cfsProcHeaders diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index fb0f7dbf4..facd8b3d4 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -338,12 +338,17 @@ proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope = if result == nil: result = cacheGetType(m.typeCache, sig) proc structOrUnion(t: PType): Rope = + let cachedUnion {.global.} = rope("union") + let cachedStruct {.global.} = rope("struct") let t = t.skipTypes({tyAlias, tySink}) - (if tfUnion in t.flags: rope("union") else: rope("struct")) + if tfUnion in t.flags: cachedUnion + else: cachedStruct -proc getForwardStructFormat(m: BModule): string = - if m.compileToCpp: result = "$1 $2;$n" - else: result = "typedef $1 $2 $2;$n" +proc addForwardStructFormat(m: BModule, structOrUnion: Rope, typename: Rope) = + if m.compileToCpp: + m.s[cfsForwardTypes].addf "$1 $2;$n", [structOrUnion, typename] + else: + m.s[cfsForwardTypes].addf "typedef $1 $2 $2;$n", [structOrUnion, typename] proc seqStar(m: BModule): string = if m.config.selectedGC == gcDestructors: result = "" @@ -360,8 +365,7 @@ proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope = result = getTypeName(m, typ, sig) m.forwTypeCache[sig] = result if not isImportedType(concrete): - addf(m.s[cfsForwardTypes], getForwardStructFormat(m), - [structOrUnion(typ), result]) + addForwardStructFormat(m, structOrUnion(typ), result) else: pushType(m, concrete) doAssert m.forwTypeCache[sig] == result @@ -733,8 +737,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = if result == nil: result = getTypeName(m, origTyp, sig) if not isImportedType(t): - addf(m.s[cfsForwardTypes], getForwardStructFormat(m), - [structOrUnion(t), result]) + addForwardStructFormat(m, structOrUnion(t), result) m.forwTypeCache[sig] = result assert(cacheGetType(m.typeCache, sig) == nil) m.typeCache[sig] = result & seqStar(m) @@ -845,8 +848,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = result = getTypeName(m, origTyp, sig) m.forwTypeCache[sig] = result if not isImportedType(t): - addf(m.s[cfsForwardTypes], getForwardStructFormat(m), - [structOrUnion(t), result]) + addForwardStructFormat(m, structOrUnion(t), result) assert m.forwTypeCache[sig] == result m.typeCache[sig] = result # always call for sideeffects: if not incompleteType(t): @@ -905,7 +907,8 @@ proc finishTypeDescriptions(m: BModule) = discard getTypeDesc(m, m.typeStack[i]) inc(i) -template cgDeclFrmt*(s: PSym): string = s.constraint.strVal +template cgDeclFrmt*(s: PSym): string = + s.constraint.strVal proc isReloadable(m: BModule, prc: PSym): bool = return m.hcrOn and sfNonReloadable notin prc.flags @@ -943,7 +946,7 @@ proc genProcHeader(m: BModule, prc: PSym, asPtr: bool = false): Rope = params]) else: let asPtrStr = if asPtr: (rope("(*") & name & ")") else: name - result = prc.cgDeclFrmt % [rettype, asPtrStr, params] + result = runtimeFormat(prc.cgDeclFrmt, [rettype, asPtrStr, params]) # ------------------ type info generation ------------------------------------- @@ -1346,10 +1349,10 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope = # results are not deterministic! genTupleInfo(m, t, origType, result, info) else: internalError(m.config, "genTypeInfo(" & $t.kind & ')') - if t.deepCopy != nil: - genDeepCopyProc(m, t.deepCopy, result) - elif origType.deepCopy != nil: - genDeepCopyProc(m, origType.deepCopy, result) + if t.attachedOps[attachedDeepCopy] != nil: + genDeepCopyProc(m, t.attachedOps[attachedDeepCopy], result) + elif origType.attachedOps[attachedDeepCopy] != nil: + genDeepCopyProc(m, origType.attachedOps[attachedDeepCopy], result) result = prefixTI.rope & result & ")".rope proc genTypeSection(m: BModule, n: PNode) = diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 92cc3819b..9bc3353dc 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -206,15 +206,15 @@ proc indentLine(p: BProc, r: Rope): Rope = prepend(result, "\t".rope) template appcg(m: BModule, c: var Rope, frmt: FormatStr, - args: varargs[untyped]) = + args: untyped) = add(c, ropecg(m, frmt, args)) template appcg(m: BModule, sec: TCFileSection, frmt: FormatStr, - args: varargs[untyped]) = + args: untyped) = add(m.s[sec], ropecg(m, frmt, args)) template appcg(p: BProc, sec: TCProcSection, frmt: FormatStr, - args: varargs[untyped]) = + args: untyped) = add(p.s(sec), ropecg(p.module, frmt, args)) template line(p: BProc, sec: TCProcSection, r: Rope) = @@ -224,7 +224,7 @@ template line(p: BProc, sec: TCProcSection, r: string) = add(p.s(sec), indentLine(p, r.rope)) template lineF(p: BProc, sec: TCProcSection, frmt: FormatStr, - args: openarray[Rope]) = + args: untyped) = add(p.s(sec), indentLine(p, frmt % args)) template lineCg(p: BProc, sec: TCProcSection, frmt: FormatStr, @@ -440,6 +440,15 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) = result.flags = {} constructLoc(p, result, not needsInit) +proc getTempCpp(p: BProc, t: PType, result: var TLoc; value: Rope) = + inc(p.labels) + result.r = "T" & rope(p.labels) & "_" + linefmt(p, cpsStmts, "$1 $2 = $3;$n", [getTypeDesc(p.module, t), result.r, value]) + result.k = locTemp + result.lode = lodeTyp t + result.storage = OnStack + result.flags = {} + proc getIntTemp(p: BProc, result: var TLoc) = inc(p.labels) result.r = "T" & rope(p.labels) & "_" @@ -484,7 +493,7 @@ proc localVarDecl(p: BProc; n: PNode): Rope = add(result, " ") add(result, s.loc.r) else: - result = s.cgDeclFrmt % [result, s.loc.r] + result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.r]) proc assignLocalVar(p: BProc, n: PNode) = #assert(s.loc.k == locNone) # not yet assigned @@ -535,7 +544,7 @@ proc assignGlobalVar(p: BProc, n: PNode) = if sfVolatile in s.flags: add(decl, " volatile") addf(decl, " $1;$n", [s.loc.r]) else: - decl = (s.cgDeclFrmt & ";$n") % [td, s.loc.r] + decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r]) add(p.module.s[cfsVars], decl) if p.withinLoop > 0: # fixes tests/run/tzeroarray: diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 4cd66b333..d9c3b7b86 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -70,6 +70,7 @@ type threadVarAccessed*: bool # true if the proc already accessed some threadvar hasCurFramePointer*: bool # true if _nimCurFrame var needed to recover after # exception is generated + noSafePoints*: bool # the proc doesn't use safe points in exception handling lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements currLineInfo*: TLineInfo # AST codegen will make this superfluous nestedTryStmts*: seq[tuple[n: PNode, inExcept: bool]] diff --git a/compiler/dfa.nim b/compiler/dfa.nim index 436fd699f..d36427098 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -567,19 +567,36 @@ proc genReturn(c: var Con; n: PNode) = const InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp} - PathKinds* = {nkDotExpr, nkCheckedFieldExpr, + PathKinds0 = {nkDotExpr, nkCheckedFieldExpr, nkBracketExpr, nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr, - nkHiddenStdConv, nkHiddenSubConv, nkObjDownConv, nkObjUpConv} + nkObjDownConv, nkObjUpConv} + PathKinds1 = {nkHiddenStdConv, nkHiddenSubConv} + +proc getRoot(n: PNode): PNode = + result = n + while true: + case result.kind + of PathKinds0: + result = result[0] + of PathKinds1: + result = result[1] + else: break + +proc skipConvDfa*(n: PNode): PNode = + result = n + while true: + case result.kind + of nkObjDownConv, nkObjUpConv: + result = result[0] + of PathKinds1: + result = result[1] + else: break proc genUse(c: var Con; orig: PNode) = - var n = orig - var iters = 0 - while n.kind in PathKinds: - n = n[0] - inc iters + let n = dfa.getRoot(orig) if n.kind == nkSym and n.sym.kind in InterestingSyms: - c.code.add Instr(n: orig, kind: use, sym: if iters > 0: nil else: n.sym) + c.code.add Instr(n: orig, kind: use, sym: if orig != n: nil else: n.sym) proc aliases(obj, field: PNode): bool = var n = field @@ -590,7 +607,7 @@ proc aliases(obj, field: PNode): bool = if sameTrees(obj, n): return true case n.kind of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv, - nkObjDownConv, nkObjUpConv, nkHiddenDeref: + nkObjDownConv, nkObjUpConv, nkHiddenDeref, nkDerefExpr: n = n[0] of nkBracketExpr: let x = n[0] @@ -616,13 +633,19 @@ proc instrTargets*(ins: Instr; loc: PNode): bool = # use x; question does it affect 'x.f'? Yes. result = aliases(ins.n, loc) or aliases(loc, ins.n) -proc isAnalysableFieldAccess*(n: PNode; owner: PSym): bool = - var n = n +proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool = + var n = orig while true: case n.kind of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv, - nkObjDownConv, nkObjUpConv, nkHiddenDeref: + nkObjDownConv, nkObjUpConv: + n = n[0] + of nkHiddenDeref, nkDerefExpr: + # We "own" sinkparam[].loc but not ourVar[].location as it is a nasty + # pointer indirection. n = n[0] + return n.kind == nkSym and n.sym.owner == owner and (isSinkParam(n.sym) or + n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned, tyVar}) of nkBracketExpr: let x = n[0] if x.typ != nil and x.typ.skipTypes(abstractInst).kind == tyTuple: @@ -633,11 +656,25 @@ proc isAnalysableFieldAccess*(n: PNode; owner: PSym): bool = break # XXX Allow closure deref operations here if we know # the owner controlled the closure allocation? - result = n.kind == nkSym and n.sym.owner == owner and owner.kind != skModule + result = n.kind == nkSym and n.sym.owner == owner and + owner.kind != skModule and + (n.sym.kind != skParam or isSinkParam(n.sym)) # or n.sym.typ.kind == tyVar) + # Note: There is a different move analyzer possible that checks for + # consume(param.key); param.key = newValue for all paths. Then code like + # + # let splited = split(move self.root, x) + # self.root = merge(splited.lower, splited.greater) + # + # could be written without the ``move self.root``. However, this would be + # wrong! Then the write barrier for the ``self.root`` assignment would + # free the old data and all is lost! Lesson: Don't be too smart, trust the + # lower level C++ optimizer to specialize this code. proc genDef(c: var Con; n: PNode) = if n.kind == nkSym and n.sym.kind in InterestingSyms: c.code.add Instr(n: n, kind: def, sym: n.sym) + elif isAnalysableFieldAccess(n, c.owner): + c.code.add Instr(n: n, kind: def, sym: nil) proc canRaise(fn: PNode): bool = const magicsThatCanRaise = { @@ -715,7 +752,7 @@ proc gen(c: var Con; n: PNode) = # "uses" 'i'. But we are only talking about builtin array indexing so # it doesn't matter and 'x = 34' is NOT a usage of 'x'. genDef(c, n[0]) - of PathKinds: + of PathKinds0 - {nkHiddenStdConv, nkHiddenSubConv, nkObjDownConv, nkObjUpConv}: genUse(c, n) of nkIfStmt, nkIfExpr: genIf(c, n) of nkWhenStmt: @@ -732,8 +769,8 @@ proc gen(c: var Con; n: PNode) = nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr: for x in n: gen(c, x) of nkPragmaBlock: gen(c, n.lastSon) - of nkDiscardStmt: gen(c, n.sons[0]) - of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast: + of nkDiscardStmt, nkObjDownConv, nkObjUpConv: gen(c, n.sons[0]) + of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast, nkHiddenSubConv, nkHiddenStdConv: gen(c, n.sons[1]) of nkStringToCString, nkCStringToString: gen(c, n.sons[0]) of nkVarSection, nkLetSection: genVarSection(c, n) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 93efef526..014f757db 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -172,7 +172,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, outExt, RelativeDir"htmldocs", false) result.thisDir = result.destFile.splitFile.dir -proc dispA(conf: ConfigRef; dest: var Rope, xml, tex: string, args: openArray[Rope]) = +template dispA(conf: ConfigRef; dest: var Rope, xml, tex: string, args: openArray[Rope]) = if conf.cmd != cmdRst2tex: addf(dest, xml, args) else: addf(dest, tex, args) diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index 2232601f4..048860423 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -20,11 +20,12 @@ type TGen = object of PPassContext doc: PDoc module: PSym + config: ConfigRef PGen = ref TGen template shouldProcess(g): bool = (g.module.owner.id == g.doc.conf.mainPackageId and optWholeProject in g.doc.conf.globalOptions) or - sfMainModule in g.module.flags + sfMainModule in g.module.flags or g.config.projectMainIdx == g.module.info.fileIndex template closeImpl(body: untyped) {.dirty.} = var g = PGen(p) @@ -60,6 +61,7 @@ template myOpenImpl(ext: untyped) {.dirty.} = var g: PGen new(g) g.module = module + g.config = graph.config var d = newDocumentor(AbsoluteFile toFullPath(graph.config, FileIndex module.position), graph.cache, graph.config, ext) d.hasToc = true diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index ab42f4f52..9880ecf4f 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -376,7 +376,7 @@ proc nameToCC*(name: string): TSystemCC = result = ccNone proc isVSCompatible*(conf: ConfigRef): bool = - return conf.cCompiler == ccVcc or + return conf.cCompiler == ccVcc or conf.cCompiler == ccClangCl or (conf.cCompiler == ccIcl and conf.target.hostOS in osDos..osWindows) @@ -738,7 +738,7 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile, # way of being able to debug and rebuild the program at the same time. This # is accomplished using the /PDB:<filename> flag (there also exists the # /PDBALTPATH:<filename> flag). The only downside is that the .pdb files are - # atleast 300kb big (when linking statically to the runtime - or else 5mb+) + # atleast 300kb big (when linking statically to the runtime - or else 5mb+) # and will quickly accumulate. There is a hacky solution: we could try to # delete all .pdb files with a pattern and swallow exceptions. # @@ -910,7 +910,8 @@ proc callCCompiler*(conf: ConfigRef) = else: AbsoluteFile(conf.projectName) linkCmd = getLinkCmd(conf, mainOutput, objfiles) if optCompileOnly notin conf.globalOptions: - if defined(windows) and linkCmd.len > 8_000: + const MaxCmdLen = when defined(windows): 8_000 else: 32_000 + if linkCmd.len > MaxCmdLen: # Windows's command line limit is about 8K (don't laugh...) so C compilers on # Windows support a feature where the command line can be passed via ``@linkcmd`` # to them. diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 7901ed4dc..0e414c975 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -136,7 +136,7 @@ to do it. import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, strutils, options, dfa, lowerings, tables, modulegraphs, msgs, - lineinfos, parampatterns + lineinfos, parampatterns, sighashes const InterestingSyms = {skVar, skResult, skLet, skForVar, skTemp} @@ -195,15 +195,17 @@ proc isLastRead(n: PNode; c: var Con): bool = # first we need to search for the instruction that belongs to 'n': c.otherRead = nil var instr = -1 + let m = dfa.skipConvDfa(n) + for i in 0..<c.g.len: # This comparison is correct and MUST not be ``instrTargets``: - if c.g[i].kind == use and c.g[i].n == n: + if c.g[i].kind == use and c.g[i].n == m: if instr < 0: instr = i break dbg: - echo "starting point for ", n, " is ", instr + echo "starting point for ", n, " is ", instr, " ", n.kind if instr < 0: return false # we go through all paths beginning from 'instr+1' and need to @@ -314,24 +316,37 @@ proc makePtrType(c: Con, baseType: PType): PType = result = newType(tyPtr, c.owner) addSonSkipIntLit(result, baseType) -template genOp(opr, opname, ri) = - let op = opr +proc addDestroy(c: var Con; n: PNode) = + # append to front: + c.destroys = newTree(nkStmtList, n, c.destroys) + +proc genOp(c: Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode = + var op = t.attachedOps[kind] + + if op == nil: + # give up and find the canonical type instead: + let h = sighashes.hashType(t, {CoType, CoConsiderOwned}) + let canon = c.graph.canonTypes.getOrDefault(h) + if canon != nil: + op = canon.attachedOps[kind] + if op == nil: - globalError(c.graph.config, dest.info, "internal error: '" & opname & + globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] & "' operator not found for type " & typeToString(t)) elif op.ast[genericParamsPos].kind != nkEmpty: - globalError(c.graph.config, dest.info, "internal error: '" & opname & + globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] & "' operator is generic") - if sfError in op.flags: checkForErrorPragma(c, t, ri, opname) + if sfError in op.flags: checkForErrorPragma(c, t, ri, AttachedOpToStr[kind]) let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ)) addrExp.add(dest) result = newTree(nkCall, newSymNode(op), addrExp) proc genSink(c: Con; t: PType; dest, ri: PNode): PNode = let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) - let op = if t.sink != nil: t.sink else: t.assignment - if op != nil: - genOp(op, "=sink", ri) + let k = if t.attachedOps[attachedSink] != nil: attachedSink + else: attachedAsgn + if t.attachedOps[k] != nil: + result = genOp(c, t, k, dest, ri) else: # in rare cases only =destroy exists but no sink or assignment # (see Pony object in tmove_objconstr.nim) @@ -342,15 +357,15 @@ proc genCopy(c: Con; t: PType; dest, ri: PNode): PNode = if tfHasOwned in t.flags: checkForErrorPragma(c, t, ri, "=") let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) - genOp(t.assignment, "=", ri) + result = genOp(c, t, attachedAsgn, dest, ri) proc genCopyNoCheck(c: Con; t: PType; dest, ri: PNode): PNode = let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) - genOp(t.assignment, "=", ri) + result = genOp(c, t, attachedAsgn, dest, ri) proc genDestroy(c: Con; t: PType; dest: PNode): PNode = let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) - genOp(t.destructor, "=destroy", nil) + result = genOp(c, t, attachedDestructor, dest, nil) proc addTopVar(c: var Con; v: PNode) = c.topLevelVars.add newTree(nkIdentDefs, v, c.emptyNode, c.emptyNode) @@ -408,6 +423,17 @@ proc sinkParamIsLastReadCheck(c: var Con, s: PNode) = localError(c.graph.config, c.otherRead.info, "sink parameter `" & $s.sym.name.s & "` is already consumed at " & toFileLineCol(c. graph.config, s.info)) +proc isSinkTypeForParam(t: PType): bool = + # a parameter like 'seq[owned T]' must not be used only once, but its + # elements must, so we detect this case here: + result = t.skipTypes({tyGenericInst, tyAlias}).kind in {tySink, tyOwned} + when false: + if isSinkType(t): + if t.skipTypes({tyGenericInst, tyAlias}).kind in {tyArray, tyVarargs, tyOpenArray, tySequence}: + result = false + else: + result = true + proc passCopyToSink(n: PNode; c: var Con): PNode = result = newNodeIT(nkStmtListExpr, n.info, n.typ) let tmp = getTemp(c, n.typ, n.info) @@ -441,7 +467,7 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode = let L = if parameters != nil: parameters.len else: 0 result.add arg[0] for i in 1..<arg.len: - result.add pArg(arg[i], c, i < L and isSinkType(parameters[i])) + result.add pArg(arg[i], c, i < L and isSinkTypeForParam(parameters[i])) elif arg.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkBracket, nkCharLit..nkTripleStrLit}: discard "object construction to sink parameter: nothing to do" result = arg @@ -518,16 +544,22 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = let L = if parameters != nil: parameters.len else: 0 ri2.add ri[0] for i in 1..<ri.len: - ri2.add pArg(ri[i], c, i < L and isSinkType(parameters[i])) + ri2.add pArg(ri[i], c, i < L and isSinkTypeForParam(parameters[i])) #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) + result.add p(ri, c) + elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c): + # Rule 3: `=sink`(x, z); wasMoved(z) + var snk = genSink(c, dest.typ, dest, ri) + snk.add ri + result = newTree(nkStmtList, snk, genWasMoved(ri, c)) else: result = genCopy(c, dest.typ, dest, ri) - result.add p(ri, c) + result.add p(ri, c) of nkStmtListExpr: result = newNodeI(nkStmtList, ri.info) for i in 0..ri.len-2: @@ -610,7 +642,9 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = result = genCopy(c, dest.typ, dest, ri) result.add p(ri, c) of nkHiddenSubConv, nkHiddenStdConv: - if ri[1].kind in movableNodeKinds: + if sameType(ri.typ, ri[1].typ): + result = moveOrCopy(dest, ri[1], c) + elif ri[1].kind in movableNodeKinds: result = moveOrCopy(dest, ri[1], c) var b = newNodeIT(ri.kind, ri.info, ri.typ) b.add ri[0] # add empty node @@ -673,6 +707,15 @@ proc injectDefaultCalls(n: PNode, c: var Con) = proc isCursor(n: PNode): bool {.inline.} = result = n.kind == nkSym and sfCursor in n.sym.flags +proc keepVar(n, it: PNode, c: var Con): PNode = + # keep the var but transform 'ri': + result = copyNode(n) + var itCopy = copyNode(it) + for j in 0..it.len-2: + itCopy.add it[j] + itCopy.add p(it[it.len-1], c) + result.add itCopy + proc p(n: PNode; c: var Con): PNode = case n.kind of nkVarSection, nkLetSection: @@ -694,24 +737,17 @@ proc p(n: PNode; c: var Con): PNode = c.addTopVar v # make sure it's destroyed at the end of the proc: if not isUnpackedTuple(it[0].sym): - c.destroys.add genDestroy(c, v.typ, v) - if ri.kind != nkEmpty: - let r = moveOrCopy(v, ri, c) - result.add r + c.addDestroy genDestroy(c, v.typ, v) + if ri.kind != nkEmpty: + let r = moveOrCopy(v, ri, c) + result.add r else: - # keep it, but transform 'ri': - var varSection = copyNode(n) - var itCopy = copyNode(it) - for j in 0..L-2: - itCopy.add it[j] - itCopy.add p(ri, c) - varSection.add itCopy - result.add varSection + result.add keepVar(n, it, c) of nkCallKinds: let parameters = n[0].typ let L = if parameters != nil: parameters.len else: 0 for i in 1 ..< n.len: - n.sons[i] = pArg(n[i], c, i < L and isSinkType(parameters[i])) + n.sons[i] = pArg(n[i], c, i < L and isSinkTypeForParam(parameters[i])) if n.typ != nil and hasDestructor(n.typ): discard "produce temp creation" result = newNodeIT(nkStmtListExpr, n.info, n.typ) @@ -720,11 +756,11 @@ proc p(n: PNode; c: var Con): PNode = sinkExpr.add n result.add sinkExpr result.add tmp - c.destroys.add genDestroy(c, n.typ, tmp) + c.addDestroy genDestroy(c, n.typ, tmp) else: result = n of nkAsgn, nkFastAsgn: - if hasDestructor(n[0].typ): + if hasDestructor(n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda, nkClosure}: result = moveOrCopy(n[0], n[1], c) else: result = copyNode(n) @@ -783,8 +819,8 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode = let params = owner.typ.n for i in 1 ..< params.len: let param = params[i].sym - if isSinkParam(param) and hasDestructor(param.typ.skipTypes({tySink})): - c.destroys.add genDestroy(c, param.typ.skipTypes({tyGenericInst, tyAlias, tySink}), params[i]) + if isSinkTypeForParam(param.typ) and hasDestructor(param.typ.skipTypes({tySink})): + c.addDestroy genDestroy(c, param.typ.skipTypes({tyGenericInst, tyAlias, tySink}), params[i]) #if optNimV2 in c.graph.config.globalOptions: # injectDefaultCalls(n, c) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 5f93cbbbd..cd13aab78 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -32,9 +32,9 @@ import ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options, nversion, nimsets, msgs, std / sha1, bitsets, idents, types, os, tables, times, ropes, math, passes, ccgutils, wordrecg, renderer, - intsets, cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils, + intsets, cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils, pathutils, transf - + from modulegraphs import ModuleGraph, PPassContext @@ -365,92 +365,92 @@ proc genOr(p: PProc, a, b: PNode, r: var TCompRes) = line(p, "}") type - TMagicFrmt = array[0..3, string] + TMagicFrmt = array[0..1, string] TMagicOps = array[mAddI..mStrToStr, TMagicFrmt] -const # magic checked op; magic unchecked op; checked op; unchecked op - jsOps: TMagicOps = [ - ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # AddI - ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # SubI - ["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI - ["divInt", "", "divInt($1, $2)", "Math.trunc($1 / $2)"], # DivI - ["modInt", "", "modInt($1, $2)", "Math.trunc($1 % $2)"], # ModI - ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ - ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred - ["", "", "($1 + $2)", "($1 + $2)"], # AddF64 - ["", "", "($1 - $2)", "($1 - $2)"], # SubF64 - ["", "", "($1 * $2)", "($1 * $2)"], # MulF64 - ["", "", "($1 / $2)", "($1 / $2)"], # DivF64 - ["", "", "", ""], # ShrI - ["", "", "($1 << $2)", "($1 << $2)"], # ShlI - ["", "", "($1 >> $2)", "($1 >> $2)"], # AshrI - ["", "", "($1 & $2)", "($1 & $2)"], # BitandI - ["", "", "($1 | $2)", "($1 | $2)"], # BitorI - ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI - ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI - ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI - ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64 - ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64 - ["", "", "", ""], # addU - ["", "", "", ""], # subU - ["", "", "", ""], # mulU - ["", "", "", ""], # divU - ["", "", "($1 % $2)", "($1 % $2)"], # modU - ["", "", "($1 == $2)", "($1 == $2)"], # EqI - ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI - ["", "", "($1 < $2)", "($1 < $2)"], # LtI - ["", "", "($1 == $2)", "($1 == $2)"], # EqF64 - ["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64 - ["", "", "($1 < $2)", "($1 < $2)"], # LtF64 - ["", "", "($1 <= $2)", "($1 <= $2)"], # leU - ["", "", "($1 < $2)", "($1 < $2)"], # ltU - ["", "", "($1 <= $2)", "($1 <= $2)"], # leU64 - ["", "", "($1 < $2)", "($1 < $2)"], # ltU64 - ["", "", "($1 == $2)", "($1 == $2)"], # EqEnum - ["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum - ["", "", "($1 < $2)", "($1 < $2)"], # LtEnum - ["", "", "($1 == $2)", "($1 == $2)"], # EqCh - ["", "", "($1 <= $2)", "($1 <= $2)"], # LeCh - ["", "", "($1 < $2)", "($1 < $2)"], # LtCh - ["", "", "($1 == $2)", "($1 == $2)"], # EqB - ["", "", "($1 <= $2)", "($1 <= $2)"], # LeB - ["", "", "($1 < $2)", "($1 < $2)"], # LtB - ["", "", "($1 == $2)", "($1 == $2)"], # EqRef - ["", "", "($1 == $2)", "($1 == $2)"], # EqUntracedRef - ["", "", "($1 <= $2)", "($1 <= $2)"], # LePtr - ["", "", "($1 < $2)", "($1 < $2)"], # LtPtr - ["", "", "($1 != $2)", "($1 != $2)"], # Xor - ["", "", "($1 == $2)", "($1 == $2)"], # EqCString - ["", "", "($1 == $2)", "($1 == $2)"], # EqProc - ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI - ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64 - ["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI - ["", "", "!($1)", "!($1)"], # Not - ["", "", "+($1)", "+($1)"], # UnaryPlusI - ["", "", "~($1)", "~($1)"], # BitnotI - ["", "", "+($1)", "+($1)"], # UnaryPlusF64 - ["", "", "-($1)", "-($1)"], # UnaryMinusF64 - ["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64 - ["Ze8ToI", "Ze8ToI", "Ze8ToI($1)", "Ze8ToI($1)"], # mZe8ToI - ["Ze8ToI64", "Ze8ToI64", "Ze8ToI64($1)", "Ze8ToI64($1)"], # mZe8ToI64 - ["Ze16ToI", "Ze16ToI", "Ze16ToI($1)", "Ze16ToI($1)"], # mZe16ToI - ["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64 - ["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64 - ["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64 - ["toU8", "toU8", "toU8($1)", "toU8($1)"], # toU8 - ["toU16", "toU16", "toU16($1)", "toU16($1)"], # toU16 - ["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32 - ["", "", "$1", "$1"], # ToFloat - ["", "", "$1", "$1"], # ToBiggestFloat - ["", "", "Math.trunc($1)", "Math.trunc($1)"], # ToInt - ["", "", "Math.trunc($1)", "Math.trunc($1)"], # ToBiggestInt - ["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"], - ["nimBoolToStr", "nimBoolToStr", "nimBoolToStr($1)", "nimBoolToStr($1)"], - ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"], - ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"], - ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"], - ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"], - ["", "", "$1", "$1"]] +const # magic checked op; magic unchecked op; + jsMagics: TMagicOps = [ + ["addInt", ""], # AddI + ["subInt", ""], # SubI + ["mulInt", ""], # MulI + ["divInt", ""], # DivI + ["modInt", ""], # ModI + ["addInt", ""], # Succ + ["subInt", ""], # Pred + ["", ""], # AddF64 + ["", ""], # SubF64 + ["", ""], # MulF64 + ["", ""], # DivF64 + ["", ""], # ShrI + ["", ""], # ShlI + ["", ""], # AshrI + ["", ""], # BitandI + ["", ""], # BitorI + ["", ""], # BitxorI + ["nimMin", "nimMin"], # MinI + ["nimMax", "nimMax"], # MaxI + ["nimMin", "nimMin"], # MinF64 + ["nimMax", "nimMax"], # MaxF64 + ["", ""], # addU + ["", ""], # subU + ["", ""], # mulU + ["", ""], # divU + ["", ""], # modU + ["", ""], # EqI + ["", ""], # LeI + ["", ""], # LtI + ["", ""], # EqF64 + ["", ""], # LeF64 + ["", ""], # LtF64 + ["", ""], # leU + ["", ""], # ltU + ["", ""], # leU64 + ["", ""], # ltU64 + ["", ""], # EqEnum + ["", ""], # LeEnum + ["", ""], # LtEnum + ["", ""], # EqCh + ["", ""], # LeCh + ["", ""], # LtCh + ["", ""], # EqB + ["", ""], # LeB + ["", ""], # LtB + ["", ""], # EqRef + ["", ""], # EqUntracedRef + ["", ""], # LePtr + ["", ""], # LtPtr + ["", ""], # Xor + ["", ""], # EqCString + ["", ""], # EqProc + ["negInt", ""], # UnaryMinusI + ["negInt64", ""], # UnaryMinusI64 + ["absInt", ""], # AbsI + ["", ""], # Not + ["", ""], # UnaryPlusI + ["", ""], # BitnotI + ["", ""], # UnaryPlusF64 + ["", ""], # UnaryMinusF64 + ["", ""], # AbsF64 + ["Ze8ToI", "Ze8ToI"], # mZe8ToI + ["Ze8ToI64", "Ze8ToI64"], # mZe8ToI64 + ["Ze16ToI", "Ze16ToI"], # mZe16ToI + ["Ze16ToI64", "Ze16ToI64"], # mZe16ToI64 + ["Ze32ToI64", "Ze32ToI64"], # mZe32ToI64 + ["ZeIToI64", "ZeIToI64"], # mZeIToI64 + ["toU8", "toU8"], # toU8 + ["toU16", "toU16"], # toU16 + ["toU32", "toU32"], # toU32 + ["", ""], # ToFloat + ["", ""], # ToBiggestFloat + ["", ""], # ToInt + ["", ""], # ToBiggestInt + ["nimCharToStr", "nimCharToStr"], + ["nimBoolToStr", "nimBoolToStr"], + ["cstrToNimstr", "cstrToNimstr"], + ["cstrToNimstr", "cstrToNimstr"], + ["cstrToNimstr", "cstrToNimstr"], + ["cstrToNimstr", "cstrToNimstr"], + ["", ""]] proc needsTemp(p: PProc; n: PNode): bool = # check if n contains a call to determine @@ -478,7 +478,7 @@ proc maybeMakeTemp(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rope] = else: (a: a, tmp: b) -proc binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = +template 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 @@ -490,8 +490,8 @@ proc binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = 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) + when "$3" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x) + when "$4" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x) r.res = frmt % [a, b, tmp, tmp2] r.kind = resExpr @@ -520,7 +520,7 @@ proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string, r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer] r.kind = resExpr -proc ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = +template ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = var x, y, z: TCompRes useMagic(p, magic) gen(p, n.sons[1], x) @@ -529,7 +529,7 @@ proc ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = r.res = frmt % [x.rdLoc, y.rdLoc, z.rdLoc] r.kind = resExpr -proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = +template 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) @@ -541,15 +541,108 @@ proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = var x, y: TCompRes + xLoc,yLoc: Rope let i = ord(optOverflowCheck notin p.options) - useMagic(p, jsOps[op][i]) + useMagic(p, jsMagics[op][i]) if sonsLen(n) > 2: gen(p, n.sons[1], x) gen(p, n.sons[2], y) - r.res = jsOps[op][i + 2] % [x.rdLoc, y.rdLoc] + xLoc = x.rdLoc + yLoc = y.rdLoc else: gen(p, n.sons[1], r) - r.res = jsOps[op][i + 2] % [r.rdLoc] + xLoc = r.rdLoc + + template applyFormat(frmtA, frmtB: string) = + if i == 0: + r.res = frmtA % [xLoc, yLoc] + else: + r.res = frmtB % [xLoc, yLoc] + + case op: + of mAddI: applyFormat("addInt($1, $2)", "($1 + $2)") + of mSubI: applyFormat("subInt($1, $2)", "($1 - $2)") + of mMulI: applyFormat("mulInt($1, $2)", "($1 * $2)") + of mDivI: applyFormat("divInt($1, $2)", "Math.trunc($1 / $2)") + of mModI: applyFormat("modInt($1, $2)", "Math.trunc($1 % $2)") + of mSucc: applyFormat("addInt($1, $2)", "($1 + $2)") + of mPred: applyFormat("subInt($1, $2)", "($1 - $2)") + of mAddF64: applyFormat("($1 + $2)", "($1 + $2)") + of mSubF64: applyFormat("($1 - $2)", "($1 - $2)") + of mMulF64: applyFormat("($1 * $2)", "($1 * $2)") + of mDivF64: applyFormat("($1 / $2)", "($1 / $2)") + of mShrI: applyFormat("", "") + of mShlI: applyFormat("($1 << $2)", "($1 << $2)") + of mAshrI: applyFormat("($1 >> $2)", "($1 >> $2)") + of mBitandI: applyFormat("($1 & $2)", "($1 & $2)") + of mBitorI: applyFormat("($1 | $2)", "($1 | $2)") + of mBitxorI: applyFormat("($1 ^ $2)", "($1 ^ $2)") + of mMinI: applyFormat("nimMin($1, $2)", "nimMin($1, $2)") + of mMaxI: applyFormat("nimMax($1, $2)", "nimMax($1, $2)") + of mMinF64: applyFormat("nimMin($1, $2)", "nimMin($1, $2)") + of mMaxF64: applyFormat("nimMax($1, $2)", "nimMax($1, $2)") + of mAddU: applyFormat("", "") + of msubU: applyFormat("", "") + of mmulU: applyFormat("", "") + of mdivU: applyFormat("", "") + of mmodU: applyFormat("($1 % $2)", "($1 % $2)") + of mEqI: applyFormat("($1 == $2)", "($1 == $2)") + of mLeI: applyFormat("($1 <= $2)", "($1 <= $2)") + of mLtI: applyFormat("($1 < $2)", "($1 < $2)") + of mEqF64: applyFormat("($1 == $2)", "($1 == $2)") + of mLeF64: applyFormat("($1 <= $2)", "($1 <= $2)") + of mLtF64: applyFormat("($1 < $2)", "($1 < $2)") + of mleU: applyFormat("($1 <= $2)", "($1 <= $2)") + of mltU: applyFormat("($1 < $2)", "($1 < $2)") + of mleU64: applyFormat("($1 <= $2)", "($1 <= $2)") + of mltU64: applyFormat("($1 < $2)", "($1 < $2)") + of mEqEnum: applyFormat("($1 == $2)", "($1 == $2)") + of mLeEnum: applyFormat("($1 <= $2)", "($1 <= $2)") + of mLtEnum: applyFormat("($1 < $2)", "($1 < $2)") + of mEqCh: applyFormat("($1 == $2)", "($1 == $2)") + of mLeCh: applyFormat("($1 <= $2)", "($1 <= $2)") + of mLtCh: applyFormat("($1 < $2)", "($1 < $2)") + of mEqB: applyFormat("($1 == $2)", "($1 == $2)") + of mLeB: applyFormat("($1 <= $2)", "($1 <= $2)") + of mLtB: applyFormat("($1 < $2)", "($1 < $2)") + of mEqRef: applyFormat("($1 == $2)", "($1 == $2)") + of mEqUntracedRef: applyFormat("($1 == $2)", "($1 == $2)") + of mLePtr: applyFormat("($1 <= $2)", "($1 <= $2)") + of mLtPtr: applyFormat("($1 < $2)", "($1 < $2)") + of mXor: applyFormat("($1 != $2)", "($1 != $2)") + of mEqCString: applyFormat("($1 == $2)", "($1 == $2)") + of mEqProc: applyFormat("($1 == $2)", "($1 == $2)") + of mUnaryMinusI: applyFormat("negInt($1)", "-($1)") + of mUnaryMinusI64: applyFormat("negInt64($1)", "-($1)") + of mAbsI: applyFormat("absInt($1)", "Math.abs($1)") + of mNot: applyFormat("!($1)", "!($1)") + of mUnaryPlusI: applyFormat("+($1)", "+($1)") + of mBitnotI: applyFormat("~($1)", "~($1)") + of mUnaryPlusF64: applyFormat("+($1)", "+($1)") + of mUnaryMinusF64: applyFormat("-($1)", "-($1)") + of mAbsF64: applyFormat("Math.abs($1)", "Math.abs($1)") + of mZe8ToI: applyFormat("Ze8ToI($1)", "Ze8ToI($1)") + of mZe8ToI64: applyFormat("Ze8ToI64($1)", "Ze8ToI64($1)") + of mZe16ToI: applyFormat("Ze16ToI($1)", "Ze16ToI($1)") + of mZe16ToI64: applyFormat("Ze16ToI64($1)", "Ze16ToI64($1)") + of mZe32ToI64: applyFormat("Ze32ToI64($1)", "Ze32ToI64($1)") + of mZeIToI64: applyFormat("ZeIToI64($1)", "ZeIToI64($1)") + of mtoU8: applyFormat("toU8($1)", "toU8($1)") + of mtoU16: applyFormat("toU16($1)", "toU16($1)") + of mtoU32: applyFormat("toU32($1)", "toU32($1)") + of mToFloat: applyFormat("$1", "$1") + of mToBiggestFloat: applyFormat("$1", "$1") + of mToInt: applyFormat("Math.trunc($1)", "Math.trunc($1)") + of mToBiggestInt: applyFormat("Math.trunc($1)", "Math.trunc($1)") + of mCharToStr: applyFormat("nimCharToStr($1)", "nimCharToStr($1)") + of mBoolToStr: applyFormat("nimBoolToStr($1)", "nimBoolToStr($1)") + of mIntToStr: applyFormat("cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")") + of mInt64ToStr: applyFormat("cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")") + of mFloatToStr: applyFormat("cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")") + of mCStrToStr: applyFormat("cstrToNimstr($1)", "cstrToNimstr($1)") + of mStrToStr, mUnown: applyFormat("$1", "$1") + else: + assert false, $op proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = case op @@ -1268,6 +1361,9 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) r.res = s.loc.r of skProc, skFunc, skConverter, skMethod: + if sfCompileTime in s.flags: + localError(p.config, n.info, "request to generate code for .compileTime proc: " & + s.name.s) discard mangleName(p.module, s) r.res = s.loc.r if lfNoDecl in s.loc.flags or s.magic != mNone or @@ -1603,6 +1699,9 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = else: varCode = "var $2" else: + # Is this really a thought through feature? this basically unused + # feature makes it impossible for almost all format strings in + # this function to be checked at compile time. varCode = v.constraint.strVal if n.kind == nkEmpty: @@ -1611,8 +1710,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = 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))]) + line(p, runtimeFormat(varCode & " = $3;$n", [returnType, varName, createVar(p, v.typ, isIndirect(v))])) else: gen(p, n, a) case mapType(p, v.typ) @@ -1626,29 +1724,29 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = let targetBaseIndex = {sfAddrTaken, sfGlobal} * v.flags == {} if a.typ == etyBaseIndex: if targetBaseIndex: - lineF(p, varCode & " = $3, $2_Idx = $4;$n", - [returnType, v.loc.r, a.address, a.res]) + line(p, runtimeFormat(varCode & " = $3, $2_Idx = $4;$n", + [returnType, v.loc.r, a.address, a.res])) else: if isIndirect(v): - lineF(p, varCode & " = [[$3, $4]];$n", - [returnType, v.loc.r, a.address, a.res]) + line(p, runtimeFormat(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]) + line(p, runtimeFormat(varCode & " = [$3, $4];$n", + [returnType, v.loc.r, a.address, a.res])) else: if targetBaseIndex: let tmp = p.getTemp lineF(p, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n", [tmp, a.res, v.loc.r]) else: - lineF(p, varCode & " = $3;$n", [returnType, v.loc.r, a.res]) + line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, a.res])) return else: s = a.res if isIndirect(v): - lineF(p, varCode & " = [$3];$n", [returnType, v.loc.r, s]) + line(p, runtimeFormat(varCode & " = [$3];$n", [returnType, v.loc.r, s])) else: - lineF(p, varCode & " = $3;$n", [returnType, v.loc.r, s]) + line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, s])) if useReloadingGuard: dec p.extraIndent @@ -2117,7 +2215,7 @@ proc genReturnStmt(p: PProc, n: PNode) = lineF(p, "break BeforeRet;$n", []) proc frameCreate(p: PProc; procname, filename: Rope): Rope = - let frameFmt = + const frameFmt = "var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" result = p.indentLine(frameFmt % [procname, filename]) @@ -2185,7 +2283,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = var def: Rope if not prc.constraint.isNil: - def = (prc.constraint.strVal & " {$n$#$#$#$#$#") % + def = runtimeFormat(prc.constraint.strVal & " {$n$#$#$#$#$#", [ returnType, name, header, @@ -2193,7 +2291,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = optionalLine(p.locals), optionalLine(resultAsgn), optionalLine(genProcBody(p, prc)), - optionalLine(p.indentLine(returnStmt))] + optionalLine(p.indentLine(returnStmt))]) else: result = ~"\L" diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 180c5531b..6f0f8e0a5 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -261,7 +261,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode = proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode = let envParam = getHiddenParam(g, owner) - let obj = envParam.typ.lastSon + let obj = envParam.typ.skipTypes({tyOwned, tyRef}) addField(obj, s, g.cache) var access = newSymNode(envParam) @@ -320,15 +320,23 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym; rawAddSon(result, obj) c.ownerToType[owner.id] = result +proc asOwnedRef(c: DetectionPass; t: PType): PType = + if optNimV2 in c.graph.config.globalOptions: + assert t.kind == tyRef + result = newType(tyOwned, t.owner) + result.rawAddSon t + else: + result = t + proc getEnvTypeForOwnerUp(c: var DetectionPass; owner: PSym; info: TLineInfo): PType = var r = c.getEnvTypeForOwner(owner, info) result = newType(tyPtr, owner) - rawAddSon(result, r.base) + rawAddSon(result, r.skipTypes({tyOwned, tyRef})) proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) = let refObj = c.getEnvTypeForOwner(dest, info) # getHiddenParam(dest).typ - let obj = refObj.lastSon + let obj = refObj.skipTypes({tyOwned, tyRef}) # The assumption here is that gcDestructors means we cannot deal # with cycles properly, so it's better to produce a weak ref (=ptr) here. # This seems to be generally correct but since it's a bit risky it's only @@ -343,7 +351,7 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) = let upIdent = getIdent(c.graph.cache, upName) let upField = lookupInRecord(obj.n, upIdent) if upField != nil: - if upField.typ.base != fieldType.base: + if upField.typ.skipTypes({tyOwned, tyRef}) != fieldType.skipTypes({tyOwned, tyRef}): localError(c.graph.config, dep.info, "internal error: up references do not agree") else: let result = newSym(skField, upIdent, obj.owner, obj.owner.info) @@ -414,8 +422,8 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = addClosureParam(c, owner, n.info) if interestingIterVar(s): if not c.capturedVars.containsOrIncl(s.id): - let obj = getHiddenParam(c.graph, owner).typ.lastSon - #let obj = c.getEnvTypeForOwner(s.owner).lastSon + let obj = getHiddenParam(c.graph, owner).typ.skipTypes({tyOwned, tyRef}) + #let obj = c.getEnvTypeForOwner(s.owner).skipTypes({tyOwned, tyRef}) if s.name.id == getIdent(c.graph.cache, ":state").id: obj.n[0].sym.id = -s.id @@ -440,8 +448,8 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = #echo "capturing ", n.info # variable 's' is actually captured: if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id): - let obj = c.getEnvTypeForOwner(ow, n.info).lastSon - #getHiddenParam(owner).typ.lastSon + let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef}) + #getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef}) addField(obj, s, c.graph.cache) # create required upFields: var w = owner.skipGenericOwner @@ -530,14 +538,14 @@ proc setupEnvVar(owner: PSym; d: DetectionPass; let envVarType = d.ownerToType.getOrDefault(owner.id) if envVarType.isNil: localError d.graph.config, owner.info, "internal error: could not determine closure type" - result = newEnvVar(d.graph.cache, owner, envVarType) + result = newEnvVar(d.graph.cache, owner, asOwnedRef(d, envVarType)) c.envVars[owner.id] = result proc getUpViaParam(g: ModuleGraph; owner: PSym): PNode = let p = getHiddenParam(g, owner) result = p.newSymNode if owner.isIterator: - let upField = lookupInRecord(p.typ.lastSon.n, getIdent(g.cache, upName)) + let upField = lookupInRecord(p.typ.skipTypes({tyOwned, tyRef}).n, getIdent(g.cache, upName)) if upField == nil: localError(g.config, owner.info, "could not find up reference for closure iter") else: @@ -566,10 +574,10 @@ proc rawClosureCreation(owner: PSym; # add ``env.param = param`` result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info)) - let upField = lookupInRecord(env.typ.lastSon.n, getIdent(d.graph.cache, upName)) + let upField = lookupInRecord(env.typ.skipTypes({tyOwned, tyRef}).n, getIdent(d.graph.cache, upName)) if upField != nil: let up = getUpViaParam(d.graph, owner) - if up != nil and upField.typ.base == up.typ.base: + if up != nil and upField.typ.skipTypes({tyOwned, tyRef}) == up.typ.skipTypes({tyOwned, tyRef}): result.add(newAsgnStmt(rawIndirectAccess(env, upField, env.info), up, env.info)) #elif oldenv != nil and oldenv.typ == upField.typ: @@ -584,11 +592,11 @@ proc closureCreationForIter(iter: PNode; let owner = iter.sym.skipGenericOwner var v = newSym(skVar, getIdent(d.graph.cache, envName), owner, iter.info) incl(v.flags, sfShadowed) - v.typ = getHiddenParam(d.graph, iter.sym).typ + v.typ = asOwnedRef(d, getHiddenParam(d.graph, iter.sym).typ) var vnode: PNode if owner.isIterator: let it = getHiddenParam(d.graph, owner) - addUniqueField(it.typ.sons[0], v, d.graph.cache) + addUniqueField(it.typ.skipTypes({tyOwned, tyRef}), v, d.graph.cache) vnode = indirectAccess(newSymNode(it), v, v.info) else: vnode = v.newSymNode @@ -597,10 +605,10 @@ proc closureCreationForIter(iter: PNode; result.add(vs) result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode)) - let upField = lookupInRecord(v.typ.lastSon.n, getIdent(d.graph.cache, upName)) + let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef}).n, getIdent(d.graph.cache, upName)) if upField != nil: let u = setupEnvVar(owner, d, c) - if u.typ.base == upField.typ.base: + if u.typ.skipTypes({tyOwned, tyRef}) == upField.typ.skipTypes({tyOwned, tyRef}): result.add(newAsgnStmt(rawIndirectAccess(vnode, upField, iter.info), u, iter.info)) else: @@ -610,7 +618,7 @@ proc closureCreationForIter(iter: PNode; proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass; c: var LiftingPass): PNode = let access = setupEnvVar(owner, d, c) - let obj = access.typ.sons[0] + let obj = access.typ.skipTypes({tyOwned, tyRef}) let field = getFieldFromObj(obj, n.sym) if field != nil: result = rawIndirectAccess(access, field, n.info) @@ -619,7 +627,7 @@ proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass; result = n proc getStateField*(g: ModuleGraph; owner: PSym): PSym = - getHiddenParam(g, owner).typ.sons[0].n.sons[0].sym + getHiddenParam(g, owner).typ.skipTypes({tyOwned, tyRef}).n.sons[0].sym proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; c: var LiftingPass): PNode @@ -644,7 +652,7 @@ proc symToClosure(n: PNode; owner: PSym; d: DetectionPass; while true: if access.typ == wanted: return makeClosure(d.graph, s, access, n.info) - let obj = access.typ.sons[0] + let obj = access.typ.skipTypes({tyOwned, tyRef}) let upField = lookupInRecord(obj.n, getIdent(d.graph.cache, upName)) if upField == nil: localError(d.graph.config, n.info, "internal error: no environment found") diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 310263875..2dcaa7984 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -126,7 +126,7 @@ proc newDeepCopyCall(op: PSym; x, y: PNode): PNode = proc useNoGc(c: TLiftCtx; t: PType): bool {.inline.} = result = optNimV2 in c.graph.config.globalOptions and - (tfHasGCedMem in t.flags or t.isGCedMem) + ({tfHasGCedMem, tfHasOwned} * t.flags != {} or t.isGCedMem) proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode; field: var PSym): bool = @@ -171,7 +171,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode; body.add newAsgnCall(c.graph, op, x, y) result = true -proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode): bool = +proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode) = var op = t.destructor if op == nil and useNoGc(c, t): op = produceSym(c.c, t, attachedDestructor, c.info) @@ -182,7 +182,6 @@ proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode): bool = markUsed(c.graph.config, c.info, op, c.graph.usageSym) onUse(c.info, op) body.add destructorCall(c.graph, op, x) - result = true elif useNoGc(c, t): internalError(c.graph.config, c.info, "type-bound operator could not be resolved") @@ -197,7 +196,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = assert t.typeInst != nil # patch generic destructor: op = c.c.instTypeBoundOp(c.c, op, t.typeInst, c.info, attachedAsgn, 1) - t.destructor = op + t.attachedOps[attachedDestructor] = op markUsed(c.graph.config, c.info, op, c.graph.usageSym) onUse(c.info, op) @@ -207,9 +206,9 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = of attachedAsgn: result = considerAsgnOrSink(c, t, body, x, y, t.assignment) of attachedSink: - result = considerAsgnOrSink(c, t, body, x, y, t.sink) + result = considerAsgnOrSink(c, t, body, x, y, t.asink) of attachedDeepCopy: - let op = t.deepCopy + let op = t.attachedOps[attachedDeepCopy] if op != nil: markUsed(c.graph.config, c.info, op, c.graph.usageSym) onUse(c.info, op) @@ -323,8 +322,8 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add moveCall # alternatively we could do this: when false: - doAssert t.sink != nil - body.add newAsgnCall(c.graph, t.sink, x, y) + doAssert t.asink != nil + body.add newAsgnCall(c.graph, t.asink, x, y) of attachedDestructor: doAssert t.destructor != nil body.add destructorCall(c.graph, t.destructor, x) @@ -365,10 +364,10 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = #var disposeCall = genBuiltin(c.graph, mDispose, "dispose", x) if isFinal(elemType): - discard addDestructorCall(c, elemType, actions, genDeref(x)) + addDestructorCall(c, elemType, actions, genDeref(x)) actions.add callCodegenProc(c.graph, "nimRawDispose", c.info, x) else: - discard addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x)) + addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x)) actions.add callCodegenProc(c.graph, "nimDestroyAndDispose", c.info, x) case c.kind @@ -502,27 +501,10 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = proc produceSymDistinctType(c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym = assert typ.kind == tyDistinct let baseType = typ[0] - case kind - of attachedAsgn: - if baseType.assignment == nil: - discard produceSym(c, baseType, kind, info) - typ.assignment = baseType.assignment - result = typ.assignment - of attachedSink: - if baseType.sink == nil: - discard produceSym(c, baseType, kind, info) - typ.sink = baseType.sink - result = typ.sink - of attachedDeepCopy: - if baseType.deepCopy == nil: - discard produceSym(c, baseType, kind, info) - typ.deepCopy = baseType.deepCopy - result = typ.deepCopy - of attachedDestructor: - if baseType.destructor == nil: - discard produceSym(c, baseType, kind, info) - typ.destructor = baseType.destructor - result = typ.destructor + if baseType.attachedOps[kind] == nil: + discard produceSym(c, baseType, kind, info) + typ.attachedOps[kind] = baseType.attachedOps[kind] + result = typ.attachedOps[kind] proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym = @@ -536,11 +518,7 @@ proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp; a.c = c let g = c.graph let body = newNodeI(nkStmtList, info) - let procname = case kind - of attachedAsgn: getIdent(g.cache, "=") - of attachedSink: getIdent(g.cache, "=sink") - of attachedDeepCopy: getIdent(g.cache, "=deepcopy") - of attachedDestructor: getIdent(g.cache, "=destroy") + let procname = getIdent(g.cache, AttachedOpToStr[kind]) result = newSym(skProc, procname, typ.owner, info) a.fn = result @@ -557,11 +535,7 @@ proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp; result.typ.addParam src # register this operation already: - case kind - of attachedAsgn: typ.assignment = result - of attachedSink: typ.sink = result - of attachedDeepCopy: typ.deepCopy = result - of attachedDestructor: typ.destructor = result + typ.attachedOps[kind] = result var tk: TTypeKind if optNimV2 in c.graph.config.globalOptions: @@ -636,23 +610,15 @@ proc createTypeBoundOps(c: PContext; orig: PType; info: TLineInfo) = # 5. We have a (custom) generic destructor. let typ = canon.skipTypes({tyGenericInst, tyAlias}) # we generate the destructor first so that other operators can depend on it: - if typ.destructor == nil: - discard produceSym(c, typ, attachedDestructor, info) - else: - inst(typ.destructor, typ) - if typ.assignment == nil: - discard produceSym(c, typ, attachedAsgn, info) - else: - inst(typ.assignment, typ) - if typ.sink == nil: - discard produceSym(c, typ, attachedSink, info) - else: - inst(typ.sink, typ) + for k in attachedDestructor..attachedSink: + if typ.attachedOps[k] == nil: + discard produceSym(c, typ, k, info) + else: + inst(typ.attachedOps[k], typ) if overwrite: - orig.destructor = typ.destructor - orig.assignment = typ.assignment - orig.sink = typ.sink + for k in attachedDestructor..attachedSink: + orig.attachedOps[k] = typ.attachedOps[k] if not isTrival(orig.destructor): #or not isTrival(orig.assignment) or diff --git a/compiler/modules.nim b/compiler/modules.nim index 3d8ced35d..1b8c7b958 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -17,9 +17,9 @@ import proc resetSystemArtifacts*(g: ModuleGraph) = magicsys.resetSysTypes(g) -proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: string) = +proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) = let - pck = getPackageName(graph.config, filename) + pck = getPackageName(graph.config, filename.string) pck2 = if pck.len > 0: pck else: "unknown" pack = getIdent(graph.cache, pck2) var packSym = graph.packageSyms.strTableGet(pack) @@ -27,6 +27,22 @@ proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; fil packSym = newSym(skPackage, getIdent(graph.cache, pck2), nil, result.info) initStrTable(packSym.tab) graph.packageSyms.strTableAdd(packSym) + else: + let existing = strTableGet(packSym.tab, result.name) + if existing != nil and existing.info.fileIndex != result.info.fileIndex: + when false: + # we used to produce an error: + localError(graph.config, result.info, + "module names need to be unique per Nimble package; module clashes with " & + toFullPath(graph.config, existing.info.fileIndex)) + else: + # but starting with version 0.20 we now produce a fake Nimble package instead + # to resolve the conflicts: + let pck3 = fakePackageName(graph.config, filename) + packSym = newSym(skPackage, getIdent(graph.cache, pck3), nil, result.info) + initStrTable(packSym.tab) + graph.packageSyms.strTableAdd(packSym) + result.owner = packSym result.position = int fileIdx @@ -37,13 +53,7 @@ proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; fil incl(result.flags, sfUsed) initStrTable(result.tab) strTableAdd(result.tab, result) # a module knows itself - let existing = strTableGet(packSym.tab, result.name) - if existing != nil and existing.info.fileIndex != result.info.fileIndex: - localError(graph.config, result.info, - "module names need to be unique per Nimble package; module clashes with " & - toFullPath(graph.config, existing.info.fileIndex)) - # strTableIncl() for error corrections: - discard strTableIncl(packSym.tab, result) + strTableAdd(packSym.tab, result) proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = # We cannot call ``newSym`` here, because we have to circumvent the ID @@ -51,7 +61,7 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = new(result) result.id = -1 # for better error checking result.kind = skModule - let filename = toFullPath(graph.config, fileIdx) + let filename = AbsoluteFile 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) @@ -61,8 +71,8 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): PSym = result = graph.getModule(fileIdx) if result == nil: - let filename = toFullPath(graph.config, fileIdx) - let (r, id) = loadModuleSym(graph, fileIdx, AbsoluteFile filename) + let filename = AbsoluteFile toFullPath(graph.config, fileIdx) + let (r, id) = loadModuleSym(graph, fileIdx, filename) result = r if result == nil: result = newModule(graph, fileIdx) diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim index f94c3d72c..d7c6b25ae 100644 --- a/compiler/packagehandling.nim +++ b/compiler/packagehandling.nim @@ -29,9 +29,6 @@ proc getPackageName*(conf: ConfigRef; path: string): string = for file in walkFiles(d / "*.nimble"): result = file.splitFile.name break packageSearch - for file in walkFiles(d / "*.babel"): - result = file.splitFile.name - break packageSearch # we also store if we didn't find anything: when not defined(nimNoNilSeqs): if result.isNil: result = "" @@ -41,10 +38,19 @@ proc getPackageName*(conf: ConfigRef; path: string): string = dec parents if parents <= 0: break +proc fakePackageName*(conf: ConfigRef; path: AbsoluteFile): string = + # foo/../bar becomes foo7_7bar + result = relativeTo(path, conf.projectPath, '/').string.multiReplace( + {"/": "7", "..": "_", "7": "77", "_": "__"}) + proc withPackageName*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile = let x = getPackageName(conf, path.string) if x.len == 0: result = path else: let (p, file, ext) = path.splitFile - result = p / RelativeFile((x & '_' & file) & ext) + if x == "stdlib": + # Hot code reloading now relies on 'stdlib_system' names etc. + result = p / RelativeFile((x & '_' & file) & ext) + else: + result = p / RelativeFile(fakePackageName(conf, path)) diff --git a/compiler/ropes.nim b/compiler/ropes.nim index 2071ab46a..87026025f 100644 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -211,7 +211,7 @@ proc ropeConcat*(a: varargs[Rope]): Rope = proc prepend*(a: var Rope, b: Rope) = a = b & a proc prepend*(a: var Rope, b: string) = a = b & a -proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope = +proc runtimeFormat*(frmt: FormatStr, args: openArray[Rope]): Rope = var i = 0 var length = len(frmt) result = nil @@ -269,7 +269,10 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope = add(result, substr(frmt, start, i - 1)) assert(ropeInvariant(result)) -proc addf*(c: var Rope, frmt: FormatStr, args: openArray[Rope]) = +proc `%`*(frmt: static[FormatStr], args: openArray[Rope]): Rope = + runtimeFormat(frmt, args) + +template addf*(c: var Rope, frmt: FormatStr, args: openArray[Rope]) = ## shortcut for ``add(c, frmt % args)``. add(c, frmt % args) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index f7a7f20dc..4b269dd4a 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -72,12 +72,6 @@ type TExprFlags* = set[TExprFlag] - TTypeAttachedOp* = enum - attachedAsgn, - attachedSink, - attachedDeepCopy, - attachedDestructor - PContext* = ref TContext TContext* = object of TPassContext # a context represents a module enforceVoidContext*: PType diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index f39603c0e..ead9dab27 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -937,7 +937,7 @@ proc semExprNoType(c: PContext, n: PNode): PNode = let isPush = hintExtendedContext in c.config.notes if isPush: pushInfoContext(c.config, n.info) result = semExpr(c, n, {efWantStmt}) - result = discardCheck(c, result, {}) + discardCheck(c, result, {}) if isPush: popInfoContext(c.config) proc isTypeExpr(n: PNode): bool = @@ -1526,17 +1526,52 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = x.typ.flags.incl tfVarIsPtr #echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info -proc asgnToResult(c: PContext, n, le, ri: PNode) = +proc borrowCheck(c: PContext, n, le, ri: PNode) = + const + PathKinds0 = {nkDotExpr, nkCheckedFieldExpr, + nkBracketExpr, nkAddr, nkHiddenAddr, + nkObjDownConv, nkObjUpConv} + PathKinds1 = {nkHiddenStdConv, nkHiddenSubConv} + + proc getRoot(n: PNode; followDeref: bool): PNode = + result = n + while true: + case result.kind + of nkDerefExpr, nkHiddenDeref: + if followDeref: result = result[0] + else: break + of PathKinds0: + result = result[0] + of PathKinds1: + result = result[1] + else: break + + proc scopedLifetime(c: PContext; ri: PNode): bool {.inline.} = + let n = getRoot(ri, followDeref = false) + result = (ri.kind in nkCallKinds+{nkObjConstr}) or + (n.kind == nkSym and n.sym.owner == c.p.owner) + + proc escapes(c: PContext; le: PNode): bool {.inline.} = + # param[].foo[] = self definitely escapes, we don't need to + # care about pointer derefs: + let n = getRoot(le, followDeref = true) + result = n.kind == nkSym and n.sym.kind == skParam + # Special typing rule: do not allow to pass 'owned T' to 'T' in 'result = x': - if ri.typ != nil and ri.typ.skipTypes(abstractInst).kind == tyOwned and - le.typ != nil and le.typ.skipTypes(abstractInst).kind != tyOwned and ri.kind in nkCallKinds: - localError(c.config, n.info, "cannot return an owned pointer as an unowned pointer; " & - "use 'owned(" & typeToString(le.typ) & ")' as the return type") + const absInst = abstractInst - {tyOwned} + if ri.typ != nil and ri.typ.skipTypes(absInst).kind == tyOwned and + le.typ != nil and le.typ.skipTypes(absInst).kind != tyOwned and + scopedLifetime(c, ri): + if le.kind == nkSym and le.sym.kind == skResult: + localError(c.config, n.info, "cannot return an owned pointer as an unowned pointer; " & + "use 'owned(" & typeToString(le.typ) & ")' as the return type") + elif escapes(c, le): + localError(c.config, n.info, + "assignment produces a dangling ref: the unowned ref lives longer than the owned ref") 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]) @@ -1623,7 +1658,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = c.p.owner.typ.sons[0] = rhsTyp else: typeMismatch(c.config, n.info, lhs.typ, rhsTyp) - asgnToResult(c, n, n.sons[0], rhs) + borrowCheck(c, n, lhs, rhs) n.sons[1] = fitNode(c, le, rhs, goodLineInfo(n[1])) liftTypeBoundOps(c, lhs.typ, lhs.info) @@ -1673,7 +1708,7 @@ proc semProcBody(c: PContext, n: PNode): PNode = a.sons[1] = result result = semAsgn(c, a) else: - result = discardCheck(c, result, {}) + discardCheck(c, result, {}) if c.p.owner.kind notin {skMacro, skTemplate} and c.p.resultSym != nil and c.p.resultSym.typ.isMetaType: diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index f290a08d5..7c75d8624 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -322,6 +322,39 @@ proc semOf(c: PContext, n: PNode): PNode = n.typ = getSysType(c.graph, n.info, tyBool) result = n +proc semUnown(c: PContext; n: PNode): PNode = + proc unownedType(c: PContext; t: PType): PType = + case t.kind + of tyTuple: + var elems = newSeq[PType](t.len) + var someChange = false + for i in 0..<t.len: + elems[i] = unownedType(c, t[i]) + if elems[i] != t[i]: someChange = true + if someChange: + result = newType(tyTuple, t.owner) + # we have to use 'rawAddSon' here so that type flags are + # properly computed: + for e in elems: result.rawAddSon(e) + else: + result = t + of tyOwned: result = t.sons[0] + of tySequence, tyOpenArray, tyArray, tyVarargs, tyVar, tyLent, + tyGenericInst, tyAlias: + let L = t.len-1 + let b = unownedType(c, t[L]) + if b != t[L]: + result = copyType(t, t.owner, keepId = false) + result[L] = b + result.flags.excl tfHasOwned + else: + result = t + else: + result = t + + result = copyTree(n[1]) + result.typ = unownedType(c, result.typ) + proc magicsAfterOverloadResolution(c: PContext, n: PNode, flags: TExprFlags): PNode = ## This is the preferred code point to implement magics. @@ -433,4 +466,6 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, let t = n[1].typ.skipTypes(abstractVar) if t.destructor != nil: result.sons[0] = newSymNode(t.destructor) + of mUnown: + result = semUnown(c, n) else: result = n diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index f2a6a7891..c835ce8e3 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -527,7 +527,7 @@ proc isNoEffectList(n: PNode): bool {.inline.} = n.len == 0 or (n[tagEffects] == nil and n[exceptionEffects] == nil) proc isTrival(caller: PNode): bool {.inline.} = - result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil} + result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil, mMove, mWasMoved} proc trackOperand(tracked: PEffects, n: PNode, paramType: PType; caller: PNode) = let a = skipConvAndClosure(n) @@ -738,7 +738,7 @@ proc track(tracked: PEffects, n: PNode) = mergeTags(tracked, effectList.sons[tagEffects], n) gcsafeAndSideeffectCheck() if a.kind != nkSym or a.sym.magic != mNBindSym: - for i in 1 ..< len(n): trackOperand(tracked, n.sons[i], paramType(op, i), a) + for i in 1 ..< n.len: trackOperand(tracked, n.sons[i], paramType(op, i), a) if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}: # may not look like an assignment, but it is: let arg = n.sons[1] @@ -754,7 +754,7 @@ proc track(tracked: PEffects, n: PNode) = track(tracked, n.sons[i]) of nkDotExpr: guardDotAccess(tracked, n) - for i in 0 ..< len(n): track(tracked, n.sons[i]) + for i in 0 ..< n.len: track(tracked, n.sons[i]) of nkCheckedFieldExpr: track(tracked, n.sons[0]) if warnProveField in tracked.config.notes: @@ -821,7 +821,7 @@ proc track(tracked: PEffects, n: PNode) = of nkForStmt, nkParForStmt: # we are very conservative here and assume the loop is never executed: let oldState = tracked.init.len - for i in 0 .. len(n)-3: + for i in 0 .. n.len-3: let it = n[i] track(tracked, it) if tracked.owner.kind != skMacro: @@ -830,13 +830,19 @@ proc track(tracked: PEffects, n: PNode) = createTypeBoundOps(tracked.c, x.typ, x.info) else: createTypeBoundOps(tracked.c, it.typ, it.info) - for i in len(n)-2..len(n)-1: - track(tracked, n.sons[i]) + let iterCall = n[n.len-2] + let loopBody = n[n.len-1] + if tracked.owner.kind != skMacro and iterCall.safelen > 1: + # XXX this is a bit hacky: + if iterCall[1].typ != nil and iterCall[1].typ.skipTypes(abstractVar).kind notin {tyVarargs, tyOpenArray}: + createTypeBoundOps(tracked.c, iterCall[1].typ, iterCall[1].info) + track(tracked, iterCall) + track(tracked, loopBody) setLen(tracked.init, oldState) of nkObjConstr: when false: track(tracked, n.sons[0]) let oldFacts = tracked.guards.s.len - for i in 1 ..< len(n): + for i in 1 ..< n.len: let x = n.sons[i] track(tracked, x) if x.sons[0].kind == nkSym and sfDiscriminant in x.sons[0].sym.flags: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 067b3bc9c..b8f35408f 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -43,7 +43,7 @@ proc semDiscard(c: PContext, n: PNode): PNode = n.sons[0] = semExprWithType(c, n.sons[0]) let sonType = n.sons[0].typ let sonKind = n.sons[0].kind - if isEmptyType(sonType) or sonType.kind == tyNone or n.sons[0].kind == nkTypeOfExpr: + if isEmptyType(sonType) or sonType.kind in {tyNone, tyTypeDesc} or sonKind == nkTypeOfExpr: localError(c.config, n.info, errInvalidDiscard) if sonType.kind == tyProc and sonKind notin nkCallKinds: # tyProc is disallowed to prevent ``discard foo`` to be valid, when ``discard foo()`` is meant. @@ -130,13 +130,13 @@ proc fixNilType(c: PContext; n: PNode) = for it in n: fixNilType(c, it) n.typ = nil -proc discardCheck(c: PContext, expr: PNode, flags: TExprFlags): PNode = - result = expr +proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) = if c.matchedConcept != nil or efInTypeof in flags: return if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}: if implicitlyDiscardable(result): - result = newNode(nkDiscardStmt, result.info, @[result]) + var n = newNodeI(nkDiscardStmt, result.info, 1) + n[0] = result elif result.typ.kind != tyError and c.config.cmd != cmdInteractive: var n = result while n.kind in skipForDiscardable: n = n.lastSon @@ -168,8 +168,7 @@ proc semIf(c: PContext, n: PNode; flags: TExprFlags): PNode = else: illFormedAst(it, c.config) if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or (not hasElse and efInTypeof notin flags): - for it in n: - it.sons[^1] = discardCheck(c, it.sons[^1], flags) + for it in n: discardCheck(c, it.lastSon, flags) result.kind = nkIfStmt # propagate any enforced VoidContext: if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext @@ -267,14 +266,12 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode = dec c.p.inTryStmt if isEmptyType(typ) or typ.kind in {tyNil, tyExpr}: - n.sons[0] = discardCheck(c, n.sons[0], flags) - for i in 1..n.len-1: - n.sons[i].sons[^1] = discardCheck(c, n.sons[i].sons[^1], flags) + discardCheck(c, n.sons[0], flags) + for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon, flags) if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext else: - if n.lastSon.kind == nkFinally: - n.sons[^1].sons[^1] = discardCheck(c, n.sons[^1].sons[^1], flags) + if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon, flags) n.sons[0] = fitNode(c, typ, n.sons[0], n.sons[0].info) for i in 1..last: var it = n.sons[i] @@ -474,10 +471,14 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = ($typ.kind).substr(2).toLowerAscii) elif typ.kind == tyProc and tfUnresolved in typ.flags: localError(c.config, def.info, errProcHasNoConcreteType % def.renderTree) - elif symkind == skVar and typ.kind == tyOwned and def.kind notin nkCallKinds: - # special type inference rule: 'var it = ownedPointer' is turned - # into an unowned pointer. - typ = typ.lastSon + when false: + # XXX This typing rule is neither documented nor complete enough to + # justify it. Instead use the newer 'unowned x' until we figured out + # a more general solution. + if symkind == skVar and typ.kind == tyOwned and def.kind notin nkCallKinds: + # special type inference rule: 'var it = ownedPointer' is turned + # into an unowned pointer. + typ = typ.lastSon else: if symkind == skLet: localError(c.config, a.info, errLetNeedsInit) @@ -729,7 +730,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode = openScope(c) n.sons[length-1] = semExprBranch(c, n.sons[length-1], flags) if efInTypeof notin flags: - n.sons[^1] = discardCheck(c, n.sons[^1], flags) + discardCheck(c, n.sons[length-1], flags) closeScope(c) dec(c.p.nestedLoopCounter) @@ -919,8 +920,7 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode = closeScope(c) if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or (not hasElse and efInTypeof notin flags): - for i in 1..n.len-1: - n.sons[i].sons[^1] = discardCheck(c, n.sons[i].sons[^1], flags) + for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon, flags) # propagate any enforced VoidContext: if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext @@ -1568,7 +1568,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = if obj.kind in {tyObject, tyDistinct, tySequence, tyString}: obj = canonType(c, obj) if obj.destructor.isNil: - obj.destructor = s + obj.attachedOps[attachedDestructor] = s else: prevDestructor(c, obj.destructor, obj, n.info) noError = true @@ -1592,7 +1592,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = elif t.kind == tyGenericInvocation: t = t.sons[0] else: break if t.kind in {tyObject, tyDistinct, tyEnum, tySequence, tyString}: - if t.deepCopy.isNil: t.deepCopy = s + if t.attachedOps[attachedDeepCopy].isNil: t.attachedOps[attachedDeepCopy] = s else: localError(c.config, n.info, errGenerated, "cannot bind another 'deepCopy' to: " & typeToString(t)) @@ -1631,11 +1631,11 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = # attach these ops to the canonical tySequence obj = canonType(c, obj) #echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](obj) - let opr = if s.name.s == "=": addr(obj.assignment) else: addr(obj.sink) - if opr[].isNil: - opr[] = s + let k = if name == "=": attachedAsgn else: attachedSink + if obj.attachedOps[k].isNil: + obj.attachedOps[k] = s else: - prevDestructor(c, opr[], obj, n.info) + prevDestructor(c, obj.attachedOps[k], obj, n.info) if obj.owner.getModule != s.getModule: localError(c.config, n.info, errGenerated, "type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")") @@ -1828,7 +1828,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n) if s.name.s[0] in {'.', '('}: - if s.name.s in [".", ".()", ".="] and {destructor, dotOperators} * c.features == {}: + if s.name.s in [".", ".()", ".="] and {Feature.destructor, dotOperators} * c.features == {}: localError(c.config, n.info, "the overloaded " & s.name.s & " operator has to be enabled with {.experimental: \"dotOperators\".}") elif s.name.s == "()" and callOperator notin c.features: @@ -2124,7 +2124,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = n.typ = n.sons[i].typ if not isEmptyType(n.typ): n.kind = nkStmtListExpr elif i != last or voidContext: - n.sons[i] = discardCheck(c, n.sons[i], flags) + discardCheck(c, n.sons[i], flags) else: n.typ = n.sons[i].typ if not isEmptyType(n.typ): n.kind = nkStmtListExpr diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index cbb7a95c9..2854c90ae 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -559,9 +559,11 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = else: s = semIdentVis(c, skTemplate, n.sons[0], {}) - if s.owner != nil and sfSystemModule in s.owner.flags and - s.name.s in ["!=", ">=", ">", "incl", "excl", "in", "notin", "isnot"]: - incl(s.flags, sfCallsite) + if s.owner != nil: + const names = ["!=", ">=", ">", "incl", "excl", "in", "notin", "isnot"] + if sfSystemModule in s.owner.flags and s.name.s in names or + s.owner.name.s == "vm" and s.name.s == "stackTrace": + incl(s.flags, sfCallsite) styleCheckDef(c.config, s) onDef(n[0].info, s) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index e24597956..d0c8c8520 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1348,8 +1348,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = # special check for generic object with # generic/partial specialized parent let tx = result.skipTypes(abstractPtrs, 50) - if tx.isNil: - localError(c.config, n.info, "invalid recursion in type '$1'" % typeToString(result[0])) + if tx.isNil or isTupleRecursive(tx): + localError(c.config, n.info, "illegal recursion in type '$1'" % typeToString(result[0])) return errorType(c) if tx != result and tx.kind == tyObject and tx.sons[0] != nil: semObjectTypeForInheritedGenericInst(c, n, tx) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 931a54f96..5d2c4203c 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -380,13 +380,13 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = if newbody.isGenericAlias: newbody = newbody.skipGenericAlias rawAddSon(result, newbody) checkPartialConstructedType(cl.c.config, cl.info, newbody) - let dc = newbody.deepCopy + let dc = newbody.attachedOps[attachedDeepCopy] if not cl.allowMetaTypes: - if dc != nil and sfFromGeneric notin newbody.deepCopy.flags: + if dc != nil and sfFromGeneric notin newbody.attachedOps[attachedDeepCopy].flags: # 'deepCopy' needs to be instantiated for # generics *when the type is constructed*: - newbody.deepCopy = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info, - attachedDeepCopy, 1) + newbody.attachedOps[attachedDeepCopy] = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info, + attachedDeepCopy, 1) if bodyIsNew and newbody.typeInst == nil: #doassert newbody.typeInst == nil newbody.typeInst = result @@ -592,7 +592,8 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = skipIntLiteralParams(result) of tySequence: - if cl.isReturnType and cl.c.config.selectedGc == gcDestructors and result.destructor.isNil and + if cl.isReturnType and cl.c.config.selectedGc == gcDestructors and + result.attachedOps[attachedDestructor].isNil and result[0].kind != tyEmpty and optNimV2 notin cl.c.config.globalOptions: let s = cl.c.graph.sysTypes[tySequence] var old = copyType(s, s.owner, keepId=false) @@ -601,9 +602,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = old.n = nil old.flags = {tfHasAsgn} old.addSonSkipIntLit result[0] - result.destructor = old.destructor - result.assignment = old.assignment - result.sink = old.sink + result.attachedOps = old.attachedOps cl.c.typesWithOps.add((result, old)) else: discard @@ -619,19 +618,19 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result.n = replaceObjBranches(cl, result.n) template typeBound(c, newty, oldty, field, info) = - let opr = newty.field + let opr = newty.attachedOps[field] if opr != nil and sfFromGeneric notin opr.flags: # '=' needs to be instantiated for generics when the type is constructed: #echo "DESTROY: instantiating ", astToStr(field), " for ", typeToString(oldty) - newty.field = c.instTypeBoundOp(c, opr, oldty, info, attachedAsgn, 1) + newty.attachedOps[field] = c.instTypeBoundOp(c, opr, oldty, info, attachedAsgn, 1) proc instAllTypeBoundOp*(c: PContext, info: TLineInfo) = var i = 0 while i < c.typesWithOps.len: let (newty, oldty) = c.typesWithOps[i] - typeBound(c, newty, oldty, destructor, info) - typeBound(c, newty, oldty, sink, info) - typeBound(c, newty, oldty, assignment, info) + typeBound(c, newty, oldty, attachedDestructor, info) + typeBound(c, newty, oldty, attachedSink, info) + typeBound(c, newty, oldty, attachedAsgn, info) inc i setLen(c.typesWithOps, 0) diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 8e4458942..c69d58cd3 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -349,7 +349,7 @@ proc hashBodyTree(graph: ModuleGraph, c: var MD5Context, n: PNode) = c &= n.strVal else: for i in 0..<n.len: - hashTree(c, n.sons[i]) + hashBodyTree(graph, c, n.sons[i]) proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash = ## compute unique digest of the proc/func/method symbols @@ -369,7 +369,7 @@ proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash = if sym.ast != nil: md5Init(c) c.md5Update(cast[cstring](result.addr), sizeof(result)) - c.hashTree(sym.ast[bodyPos]) + hashBodyTree(graph, c, sym.ast[bodyPos]) c.md5Final(result.Md5Digest) graph.symBodyHashes[sym.id] = result diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 7083b052f..194055da9 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2092,14 +2092,21 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, result = localConvMatch(c, m, f, a, arg) else: r = typeRel(m, base(f), a) - if r >= isGeneric: + case r + of isGeneric: inc(m.convMatches) result = copyTree(arg) - if r == isGeneric: - result.typ = getInstantiatedType(c, arg, m, base(f)) + result.typ = getInstantiatedType(c, arg, m, base(f)) m.baseTypeMatch = true - # bug #4799, varargs accepting subtype relation object - elif r == isSubtype: + of isFromIntLit: + inc(m.intConvMatches, 256) + result = implicitConv(nkHiddenStdConv, f[0], arg, m, c) + m.baseTypeMatch = true + of isEqual: + inc(m.convMatches) + result = copyTree(arg) + m.baseTypeMatch = true + of isSubtype: # bug #4799, varargs accepting subtype relation object inc(m.subtypeMatches) if base(f).kind == tyTypeDesc: result = arg diff --git a/compiler/transf.nim b/compiler/transf.nim index c455e3319..25a8bc5dd 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -651,6 +651,8 @@ proc transformFor(c: PTransf, n: PNode): PTransNode = var t = formal.typ if formal.ast != nil and formal.ast.typ.destructor != nil and t.destructor == nil: t = formal.ast.typ # better use the type that actually has a destructor. + elif t.destructor == nil and arg.typ.destructor != nil: + t = arg.typ # generate a temporary and produce an assignment statement: var temp = newTemp(c, t, formal.info) #temp.sym.flags.incl sfCursor diff --git a/compiler/trees.nim b/compiler/trees.nim index 55a3c619e..a06e6152c 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -101,7 +101,7 @@ proc isDeepConstExpr*(n: PNode): bool = if not isDeepConstExpr(n.sons[i]): return false if n.typ.isNil: result = true else: - let t = n.typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink}) + let t = n.typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned}) if t.kind in {tyRef, tyPtr}: return false if t.kind != tyObject or not isCaseObj(t.n): result = true diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index a71ebc351..9c6f8a970 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -931,6 +931,21 @@ proc ldNullOpcode(t: PType): TOpcode = assert t != nil if fitsRegister(t): opcLdNullReg else: opcLdNull +proc whichAsgnOpc(n: PNode): TOpcode = + case n.typ.skipTypes(abstractRange+{tyOwned}-{tyTypeDesc}).kind + of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64: + opcAsgnInt + of tyString, tyCString: + opcAsgnStr + of tyFloat..tyFloat128: + opcAsgnFloat + of tyRef, tyNil, tyVar, tyLent, tyPtr: + opcAsgnRef + else: + opcAsgnComplex + +proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = opc + proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = case m of mAnd: c.genAndOr(n, opcFJmp, dest) @@ -957,7 +972,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.genNarrow(n.sons[1], d) c.genAsgnPatch(n.sons[1], d) c.freeTemp(d) - of mOrd, mChr, mArrToSeq: c.gen(n.sons[1], dest) + of mOrd, mChr, mArrToSeq, mUnown: c.gen(n.sons[1], dest) of mNew, mNewFinalize: unused(c, n, dest) c.genNew(n) @@ -1330,6 +1345,17 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mRunnableExamples: discard "just ignore any call to runnableExamples" of mDestroy: discard "ignore calls to the default destructor" + of mMove: + let arg = n[1] + let a = c.genx(arg) + assert dest >= 0 + if dest < 0: dest = c.getTemp(arg.typ) + gABC(c, arg, whichAsgnOpc(arg), dest, a, 1) + # XXX use ldNullOpcode() here? + c.gABx(n, opcLdNull, a, c.genType(arg.typ)) + c.gABx(n, opcNodeToReg, a, a) + c.genAsgnPatch(arg, a) + c.freeTemp(a) else: # mGCref, mGCunref, globalError(c.config, n.info, "cannot generate code for: " & $m) @@ -1423,21 +1449,6 @@ proc genDeref(c: PCtx, n: PNode, dest: var TDest, flags: TGenFlags) = if {gfNodeAddr, gfNode} * flags == {} and fitsRegister(n.typ): c.gABC(n, opcNodeToReg, dest, dest) -proc whichAsgnOpc(n: PNode): TOpcode = - case n.typ.skipTypes(abstractRange+{tyOwned}-{tyTypeDesc}).kind - of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64: - opcAsgnInt - of tyString, tyCString: - opcAsgnStr - of tyFloat..tyFloat128: - opcAsgnFloat - of tyRef, tyNil, tyVar, tyLent, tyPtr: - opcAsgnRef - else: - opcAsgnComplex - -proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = opc - proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) = let tmp = c.genx(ri) assert dest >= 0 @@ -1585,8 +1596,8 @@ proc genGlobalInit(c: PCtx; n: PNode; s: PSym) = # var decls{.compileTime.}: seq[NimNode] = @[] let dest = c.getTemp(s.typ) c.gABx(n, opcLdGlobal, dest, s.position) - if s.ast != nil: - let tmp = c.genx(s.ast) + if s.astdef != nil: + let tmp = c.genx(s.astdef) c.genAdditionalCopy(n, opcWrDeref, dest, 0, tmp) c.freeTemp(dest) c.freeTemp(tmp) |