Merge branch 'devel' into expand-amb-identifier-output

This commit is contained in:
jrfondren
2019-05-03 13:03:45 -05:00
committed by GitHub
99 changed files with 3389 additions and 1421 deletions

View File

@@ -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]

View File

@@ -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()

View File

@@ -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])
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:
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
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")

View File

@@ -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

View File

@@ -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) =

View File

@@ -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:

View File

@@ -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]]

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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.

View File

@@ -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:
globalError(c.graph.config, dest.info, "internal error: '" & opname &
# 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: '" & 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)

View File

@@ -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"

View File

@@ -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")

View File

@@ -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

View File

@@ -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)

View File

@@ -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))

View File

@@ -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)

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)