mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-18 21:40:32 +00:00
ARC related bugfixes and refactorings (#12781)
This commit is contained in:
@@ -1456,7 +1456,7 @@ proc isGCedMem*(t: PType): bool {.inline.} =
|
||||
result = t.kind in {tyString, tyRef, tySequence} or
|
||||
t.kind == tyProc and t.callConv == ccClosure
|
||||
|
||||
proc propagateToOwner*(owner, elem: PType) =
|
||||
proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) =
|
||||
const HaveTheirOwnEmpty = {tySequence, tyOpt, tySet, tyPtr, tyRef, tyProc}
|
||||
owner.flags = owner.flags + (elem.flags * {tfHasMeta, tfTriggersCompileTime})
|
||||
if tfNotNil in elem.flags:
|
||||
@@ -1472,19 +1472,13 @@ proc propagateToOwner*(owner, elem: PType) =
|
||||
if elem.isMetaType:
|
||||
owner.flags.incl tfHasMeta
|
||||
|
||||
if tfHasAsgn in elem.flags:
|
||||
let mask = elem.flags * {tfHasAsgn, tfHasOwned}
|
||||
if mask != {} and propagateHasAsgn:
|
||||
let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
if o2.kind in {tyTuple, tyObject, tyArray,
|
||||
tySequence, tyOpt, tySet, tyDistinct, tyOpenArray, tyVarargs}:
|
||||
o2.flags.incl tfHasAsgn
|
||||
owner.flags.incl tfHasAsgn
|
||||
|
||||
if tfHasOwned in elem.flags:
|
||||
let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
if o2.kind in {tyTuple, tyObject, tyArray,
|
||||
tySequence, tyOpt, tySet, tyDistinct, tyOpenArray, tyVarargs}:
|
||||
o2.flags.incl tfHasOwned
|
||||
owner.flags.incl tfHasOwned
|
||||
o2.flags.incl mask
|
||||
owner.flags.incl mask
|
||||
|
||||
if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
|
||||
tyGenericInvocation, tyPtr}:
|
||||
@@ -1494,11 +1488,11 @@ proc propagateToOwner*(owner, elem: PType) =
|
||||
# ensure this doesn't bite us in sempass2.
|
||||
owner.flags.incl tfHasGCedMem
|
||||
|
||||
proc rawAddSon*(father, son: PType) =
|
||||
proc rawAddSon*(father, son: PType; propagateHasAsgn = true) =
|
||||
when not defined(nimNoNilSeqs):
|
||||
if isNil(father.sons): father.sons = @[]
|
||||
father.sons.add(son)
|
||||
if not son.isNil: propagateToOwner(father, son)
|
||||
if not son.isNil: propagateToOwner(father, son, propagateHasAsgn)
|
||||
|
||||
proc rawAddSonNoPropagationOfTypeFlags*(father, son: PType) =
|
||||
when not defined(nimNoNilSeqs):
|
||||
|
||||
@@ -1041,7 +1041,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
|
||||
linefmt(p, cpsStmts, "#popSafePoint();$n", [])
|
||||
genRestoreFrameAfterException(p)
|
||||
elif 1 < t.len and t[1].kind == nkExceptBranch:
|
||||
startBlock(p, "if (#getCurrentException()) {$n")
|
||||
startBlock(p, "if (#nimBorrowCurrentException()) {$n")
|
||||
else:
|
||||
startBlock(p)
|
||||
p.nestedTryStmts[^1].inExcept = true
|
||||
@@ -1068,7 +1068,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
|
||||
else:
|
||||
genTypeInfo(p.module, t[i][j].typ, t[i][j].info)
|
||||
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
|
||||
appcg(p.module, orExpr, "#isObj(#getCurrentException()->$1, $2)", [memberName, checkFor])
|
||||
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
|
||||
|
||||
if i > 1: line(p, cpsStmts, "else ")
|
||||
startBlock(p, "if ($1) {$n", [orExpr])
|
||||
@@ -1082,7 +1082,14 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
|
||||
endBlock(p) # end of else block
|
||||
if i < t.len and t[i].kind == nkFinally:
|
||||
p.finallySafePoints.add(safePoint)
|
||||
genSimpleBlock(p, t[i][0])
|
||||
startBlock(p)
|
||||
genStmts(p, t[i][0])
|
||||
# pretend we handled the exception in a 'finally' so that we don't
|
||||
# re-raise the unhandled one but instead keep the old one (it was
|
||||
# not popped either):
|
||||
if not quirkyExceptions and getCompilerProc(p.module.g.graph, "nimLeaveFinally") != nil:
|
||||
linefmt(p, cpsStmts, "if ($1.status != 0) #nimLeaveFinally();$n", [safePoint])
|
||||
endBlock(p)
|
||||
discard pop(p.finallySafePoints)
|
||||
if not quirkyExceptions:
|
||||
linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", [safePoint])
|
||||
|
||||
@@ -1884,7 +1884,7 @@ proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
|
||||
if not moduleHasChanged(m.g.graph, m.module):
|
||||
result = false
|
||||
elif not equalsFile(code, cfile.cname):
|
||||
if false:
|
||||
when false:
|
||||
#m.config.symbolFiles == readOnlySf: #isDefined(m.config, "nimdiff"):
|
||||
if fileExists(cfile.cname):
|
||||
copyFile(cfile.cname.string, cfile.cname.string & ".backup")
|
||||
|
||||
@@ -170,6 +170,7 @@ proc genOp(c: Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode =
|
||||
op = canon.attachedOps[kind]
|
||||
|
||||
if op == nil:
|
||||
#echo dest.typ.id
|
||||
globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] &
|
||||
"' operator not found for type " & typeToString(t))
|
||||
elif op.ast[genericParamsPos].kind != nkEmpty:
|
||||
@@ -250,22 +251,25 @@ proc destructiveMoveVar(n: PNode; c: var Con): PNode =
|
||||
# generate: (let tmp = v; reset(v); tmp)
|
||||
# XXX: Strictly speaking we can only move if there is a ``=sink`` defined
|
||||
# or if no ``=sink`` is defined and also no assignment.
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
if not hasDestructor(n.typ):
|
||||
result = copyTree(n)
|
||||
else:
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
|
||||
var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), c.owner, n.info)
|
||||
temp.typ = n.typ
|
||||
var v = newNodeI(nkLetSection, n.info)
|
||||
let tempAsNode = newSymNode(temp)
|
||||
var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), c.owner, n.info)
|
||||
temp.typ = n.typ
|
||||
var v = newNodeI(nkLetSection, n.info)
|
||||
let tempAsNode = newSymNode(temp)
|
||||
|
||||
var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
|
||||
vpart[0] = tempAsNode
|
||||
vpart[1] = c.emptyNode
|
||||
vpart[2] = n
|
||||
v.add(vpart)
|
||||
var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
|
||||
vpart[0] = tempAsNode
|
||||
vpart[1] = c.emptyNode
|
||||
vpart[2] = n
|
||||
v.add(vpart)
|
||||
|
||||
result.add v
|
||||
result.add genWasMoved(skipConv(n), c)
|
||||
result.add tempAsNode
|
||||
result.add v
|
||||
result.add genWasMoved(skipConv(n), c)
|
||||
result.add tempAsNode
|
||||
|
||||
proc sinkParamIsLastReadCheck(c: var Con, s: PNode) =
|
||||
assert s.kind == nkSym and s.sym.kind == skParam
|
||||
@@ -273,8 +277,12 @@ 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 p(n: PNode; c: var Con; consumed = false): PNode
|
||||
proc pArg(arg: PNode; c: var Con; isSink: bool): PNode
|
||||
type ProcessMode = enum
|
||||
normal
|
||||
consumed
|
||||
sinkArg
|
||||
|
||||
proc p(n: PNode; c: var Con; mode: ProcessMode = normal): PNode
|
||||
proc moveOrCopy(dest, ri: PNode; c: var Con): PNode
|
||||
|
||||
proc isClosureEnv(n: PNode): bool = n.kind == nkSym and n.sym.name.s[0] == ':'
|
||||
@@ -366,6 +374,10 @@ template handleNested(n: untyped, processCall: untyped) =
|
||||
branch.add if node.typ == nil: p(node, c) #noreturn
|
||||
else: processCall
|
||||
result.add branch
|
||||
of nkWhen: # This should be a "when nimvm" node.
|
||||
result = copyTree(n)
|
||||
template node: untyped = n[1][0]
|
||||
result[1][0] = processCall
|
||||
else: assert(false)
|
||||
|
||||
proc ensureDestruction(arg: PNode; c: var Con): PNode =
|
||||
@@ -384,75 +396,6 @@ proc ensureDestruction(arg: PNode; c: var Con): PNode =
|
||||
else:
|
||||
result = arg
|
||||
|
||||
proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
|
||||
if isSink:
|
||||
if arg.kind in nkCallKinds:
|
||||
# recurse but skip the call expression in order to prevent
|
||||
# destructor injections: Rule 5.1 is different from rule 5.4!
|
||||
result = copyNode(arg)
|
||||
let parameters = arg[0].typ
|
||||
let L = if parameters != nil: parameters.len else: 0
|
||||
result.add pArg(arg[0], c, isSink = false)
|
||||
for i in 1..<arg.len:
|
||||
result.add pArg(arg[i], c, i < L and isSinkTypeForParam(parameters[i]))
|
||||
elif arg.containsConstSeq:
|
||||
# const sequences are not mutable and so we need to pass a copy to the
|
||||
# sink parameter (bug #11524). Note that the string implementation is
|
||||
# different and can deal with 'const string sunk into var'.
|
||||
result = passCopyToSink(arg, c)
|
||||
elif arg.kind in nkLiterals:
|
||||
# literals are save to share accross ASTs (for now!)
|
||||
result = arg # literal to sink parameter: nothing to do
|
||||
elif arg.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkClosure}:
|
||||
# object construction to sink parameter: nothing to do
|
||||
result = copyTree(arg)
|
||||
for i in ord(arg.kind in {nkObjConstr, nkClosure})..<arg.len:
|
||||
if arg[i].kind == nkExprColonExpr:
|
||||
result[i][1] = pArg(arg[i][1], c, isSink = true)
|
||||
else:
|
||||
result[i] = pArg(arg[i], c, isSink = true)
|
||||
#if arg.kind == nkClosure:
|
||||
# result = handleClosureCall(result, c)
|
||||
# Not required here because the nkClosure will be consumed!
|
||||
elif arg.kind == nkSym and isSinkParam(arg.sym):
|
||||
# Sinked params can be consumed only once. We need to reset the memory
|
||||
# to disable the destructor which we have not elided
|
||||
sinkParamIsLastReadCheck(c, arg)
|
||||
result = destructiveMoveVar(arg, c)
|
||||
elif isAnalysableFieldAccess(arg, c.owner) and isLastRead(arg, c):
|
||||
# it is the last read, can be sinked. We need to reset the memory
|
||||
# to disable the destructor which we have not elided
|
||||
result = destructiveMoveVar(arg, c)
|
||||
elif arg.kind in {nkStmtListExpr, nkBlockExpr, nkBlockStmt, nkIfExpr, nkIfStmt, nkCaseStmt}:
|
||||
handleNested(arg): pArg(node, c, isSink)
|
||||
elif arg.kind in {nkHiddenSubConv, nkHiddenStdConv, nkConv}:
|
||||
result = copyTree(arg)
|
||||
result[1] = pArg(arg[1], c, isSink = true)
|
||||
elif arg.kind in {nkObjDownConv, nkObjUpConv}:
|
||||
result = copyTree(arg)
|
||||
result[0] = pArg(arg[0], c, isSink = true)
|
||||
else:
|
||||
# an object that is not temporary but passed to a 'sink' parameter
|
||||
# results in a copy.
|
||||
result = passCopyToSink(arg, c)
|
||||
elif arg.kind == nkBracket:
|
||||
# Treat `f([...])` like `f(...)`
|
||||
result = copyNode(arg)
|
||||
for son in arg:
|
||||
result.add pArg(son, c, isSinkTypeForParam(son.typ))
|
||||
elif arg.kind in nkCallKinds and arg.typ != nil and hasDestructor(arg.typ):
|
||||
# produce temp creation
|
||||
result = newNodeIT(nkStmtListExpr, arg.info, arg.typ)
|
||||
let tmp = getTemp(c, arg.typ, arg.info)
|
||||
let res = p(arg, c)
|
||||
var sinkExpr = genSink(c, tmp, res)
|
||||
sinkExpr.add res
|
||||
result.add sinkExpr
|
||||
result.add tmp
|
||||
c.destroys.add genDestroy(c, tmp)
|
||||
else:
|
||||
result = p(arg, c)
|
||||
|
||||
proc isCursor(n: PNode): bool =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
@@ -493,154 +436,172 @@ proc cycleCheck(n: PNode; c: var Con) =
|
||||
message(c.graph.config, n.info, warnCycleCreated, msg)
|
||||
break
|
||||
|
||||
proc p(n: PNode; c: var Con; consumed = false): PNode =
|
||||
case n.kind
|
||||
of nkCallKinds:
|
||||
let parameters = n[0].typ
|
||||
let L = if parameters != nil: parameters.len else: 0
|
||||
result = shallowCopy(n)
|
||||
for i in 1..<n.len:
|
||||
result[i] = pArg(n[i], c, i < L and isSinkTypeForParam(parameters[i]))
|
||||
if n[0].kind == nkSym and n[0].sym.magic in {mNew, mNewFinalize}:
|
||||
result[0] = copyTree(n[0])
|
||||
if c.graph.config.selectedGC in {gcHooks, gcDestructors}:
|
||||
let destroyOld = genDestroy(c, result[1])
|
||||
result = newTree(nkStmtList, destroyOld, result)
|
||||
else:
|
||||
result[0] = pArg(n[0], c, isSink = false)
|
||||
of nkDiscardStmt: # Small optimization
|
||||
result = shallowCopy(n)
|
||||
if n[0].kind != nkEmpty:
|
||||
result[0] = pArg(n[0], c, false)
|
||||
else:
|
||||
result[0] = copyNode(n[0])
|
||||
of nkBracket:
|
||||
result = copyTree(n)
|
||||
for i in 0..<n.len:
|
||||
# everything that is passed to an array constructor is consumed,
|
||||
# so these all act like 'sink' parameters:
|
||||
result[i] = pArg(n[i], c, isSink = true)
|
||||
if not consumed:
|
||||
result = ensureDestruction(result, c)
|
||||
of nkObjConstr:
|
||||
result = copyTree(n)
|
||||
for i in 1..<n.len:
|
||||
# everything that is passed to an object constructor is consumed,
|
||||
# so these all act like 'sink' parameters:
|
||||
result[i][1] = pArg(n[i][1], c, isSink = true)
|
||||
if not consumed:
|
||||
result = ensureDestruction(result, c)
|
||||
of nkTupleConstr, nkClosure:
|
||||
result = copyTree(n)
|
||||
for i in ord(n.kind == nkClosure)..<n.len:
|
||||
# everything that is passed to an tuple constructor is consumed,
|
||||
# so these all act like 'sink' parameters:
|
||||
if n[i].kind == nkExprColonExpr:
|
||||
result[i][1] = pArg(n[i][1], c, isSink = true)
|
||||
proc p(n: PNode; c: var Con; mode: ProcessMode = normal): PNode =
|
||||
if n.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkIfStmt, nkIfExpr, nkCaseStmt, nkWhen}:
|
||||
handleNested(n): p(node, c, mode)
|
||||
elif mode == sinkArg:
|
||||
if n.containsConstSeq:
|
||||
# const sequences are not mutable and so we need to pass a copy to the
|
||||
# sink parameter (bug #11524). Note that the string implementation is
|
||||
# different and can deal with 'const string sunk into var'.
|
||||
result = passCopyToSink(n, c)
|
||||
elif n.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkClosure} + nkCallKinds + nkLiterals:
|
||||
result = p(n, c, consumed)
|
||||
elif n.kind == nkSym and isSinkParam(n.sym):
|
||||
# Sinked params can be consumed only once. We need to reset the memory
|
||||
# to disable the destructor which we have not elided
|
||||
sinkParamIsLastReadCheck(c, n)
|
||||
result = destructiveMoveVar(n, c)
|
||||
elif isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c):
|
||||
# it is the last read, can be sinkArg. We need to reset the memory
|
||||
# to disable the destructor which we have not elided
|
||||
result = destructiveMoveVar(n, c)
|
||||
elif n.kind in {nkHiddenSubConv, nkHiddenStdConv, nkConv}:
|
||||
result = copyTree(n)
|
||||
if n.typ.skipTypes(abstractInst-{tyOwned}).kind != tyOwned and
|
||||
n[1].typ.skipTypes(abstractInst-{tyOwned}).kind == tyOwned:
|
||||
# allow conversions from owned to unowned via this little hack:
|
||||
let nTyp = n[1].typ
|
||||
n[1].typ = n.typ
|
||||
result[1] = p(n[1], c, sinkArg)
|
||||
result[1].typ = nTyp
|
||||
else:
|
||||
result[i] = pArg(n[i], c, isSink = true)
|
||||
if not consumed:
|
||||
result = ensureDestruction(result, c)
|
||||
of nkVarSection, nkLetSection:
|
||||
# transform; var x = y to var x; x op y where op is a move or copy
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
for it in n:
|
||||
var ri = it[^1]
|
||||
if it.kind == nkVarTuple and hasDestructor(ri.typ):
|
||||
let x = lowerTupleUnpacking(c.graph, it, c.owner)
|
||||
result.add p(x, c)
|
||||
elif it.kind == nkIdentDefs and hasDestructor(it[0].typ) and not isCursor(it[0]):
|
||||
for j in 0..<it.len-2:
|
||||
let v = it[j]
|
||||
if v.kind == nkSym:
|
||||
if sfCompileTime in v.sym.flags: continue
|
||||
# move the variable declaration to the top of the frame:
|
||||
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)
|
||||
if ri.kind == nkEmpty and c.inLoop > 0:
|
||||
ri = genDefaultCall(v.typ, c, v.info)
|
||||
if ri.kind != nkEmpty:
|
||||
let r = moveOrCopy(v, ri, c)
|
||||
result.add r
|
||||
else: # keep the var but transform 'ri':
|
||||
var v = copyNode(n)
|
||||
var itCopy = copyNode(it)
|
||||
for j in 0..<it.len-1:
|
||||
itCopy.add it[j]
|
||||
itCopy.add p(it[^1], c)
|
||||
v.add itCopy
|
||||
result.add v
|
||||
of nkAsgn, nkFastAsgn:
|
||||
if hasDestructor(n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda} and
|
||||
not isCursor(n[0]):
|
||||
# rule (self-assignment-removal):
|
||||
if n[1].kind == nkSym and n[0].kind == nkSym and n[0].sym == n[1].sym:
|
||||
result = newNodeI(nkEmpty, n.info)
|
||||
else:
|
||||
if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}:
|
||||
cycleCheck(n, c)
|
||||
result = moveOrCopy(n[0], n[1], c)
|
||||
result[1] = p(n[1], c, sinkArg)
|
||||
elif n.kind in {nkObjDownConv, nkObjUpConv}:
|
||||
result = copyTree(n)
|
||||
result[0] = p(n[0], c, sinkArg)
|
||||
else:
|
||||
result = copyNode(n)
|
||||
result.add copyTree(n[0])
|
||||
result.add p(n[1], c)
|
||||
of nkRaiseStmt:
|
||||
if optOwnedRefs in c.graph.config.globalOptions and n[0].kind != nkEmpty:
|
||||
if n[0].kind in nkCallKinds:
|
||||
let call = p(n[0], c)
|
||||
result = copyNode(n)
|
||||
result.add call
|
||||
else:
|
||||
let tmp = getTemp(c, n[0].typ, n.info)
|
||||
var m = genCopyNoCheck(c, tmp, n[0])
|
||||
m.add p(n[0], c)
|
||||
result = newTree(nkStmtList, genWasMoved(tmp, c), m)
|
||||
var toDisarm = n[0]
|
||||
if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon
|
||||
if toDisarm.kind == nkSym and toDisarm.sym.owner == c.owner:
|
||||
result.add genWasMoved(toDisarm, c)
|
||||
result.add newTree(nkRaiseStmt, tmp)
|
||||
else:
|
||||
result = copyNode(n)
|
||||
result.add p(n[0], c)
|
||||
of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef,
|
||||
nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef,
|
||||
nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, nkExportStmt,
|
||||
nkPragma, nkCommentStmt, nkBreakStmt, nkBreakState:
|
||||
result = n
|
||||
of nkWhileStmt:
|
||||
result = copyNode(n)
|
||||
inc c.inLoop
|
||||
result.add p(n[0], c)
|
||||
result.add p(n[1], c)
|
||||
dec c.inLoop
|
||||
of nkWhen: # This should be a "when nimvm" node.
|
||||
result = copyTree(n)
|
||||
result[1][0] = p(result[1][0], c)
|
||||
of nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkIfStmt, nkIfExpr, nkCaseStmt:
|
||||
handleNested(n): p(node, c, consumed)
|
||||
# copy objects that are not temporary but passed to a 'sink' parameter
|
||||
result = passCopyToSink(n, c)
|
||||
else:
|
||||
result = shallowCopy(n)
|
||||
for i in 0..<n.len:
|
||||
result[i] = p(n[i], c, consumed)
|
||||
case n.kind
|
||||
of nkBracket, nkObjConstr, nkTupleConstr, nkClosure:
|
||||
result = copyTree(n)
|
||||
for i in ord(n.kind in {nkObjConstr, nkClosure})..<n.len:
|
||||
let m = if mode == normal: normal
|
||||
else: sinkArg
|
||||
if n[i].kind == nkExprColonExpr:
|
||||
result[i][1] = p(n[i][1], c, m)
|
||||
else:
|
||||
result[i] = p(n[i], c, m)
|
||||
of nkCallKinds:
|
||||
let parameters = n[0].typ
|
||||
let L = if parameters != nil: parameters.len else: 0
|
||||
result = shallowCopy(n)
|
||||
for i in 1..<n.len:
|
||||
if i < L and isSinkTypeForParam(parameters[i]):
|
||||
result[i] = p(n[i], c, sinkArg)
|
||||
else:
|
||||
result[i] = p(n[i], c, normal)
|
||||
if n[0].kind == nkSym and n[0].sym.magic in {mNew, mNewFinalize}:
|
||||
result[0] = copyTree(n[0])
|
||||
if c.graph.config.selectedGC in {gcHooks, gcDestructors}:
|
||||
let destroyOld = genDestroy(c, result[1])
|
||||
result = newTree(nkStmtList, destroyOld, result)
|
||||
else:
|
||||
result[0] = p(n[0], c, normal)
|
||||
|
||||
if mode == normal:
|
||||
result = ensureDestruction(result, c)
|
||||
of nkDiscardStmt: # Small optimization
|
||||
result = shallowCopy(n)
|
||||
if n[0].kind != nkEmpty:
|
||||
result[0] = p(n[0], c, normal)
|
||||
else:
|
||||
result[0] = copyNode(n[0])
|
||||
of nkVarSection, nkLetSection:
|
||||
# transform; var x = y to var x; x op y where op is a move or copy
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
for it in n:
|
||||
var ri = it[^1]
|
||||
if it.kind == nkVarTuple and hasDestructor(ri.typ):
|
||||
let x = lowerTupleUnpacking(c.graph, it, c.owner)
|
||||
result.add p(x, c, consumed)
|
||||
elif it.kind == nkIdentDefs and hasDestructor(it[0].typ) and not isCursor(it[0]):
|
||||
for j in 0..<it.len-2:
|
||||
let v = it[j]
|
||||
if v.kind == nkSym:
|
||||
if sfCompileTime in v.sym.flags: continue
|
||||
# move the variable declaration to the top of the frame:
|
||||
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)
|
||||
if ri.kind == nkEmpty and c.inLoop > 0:
|
||||
ri = genDefaultCall(v.typ, c, v.info)
|
||||
if ri.kind != nkEmpty:
|
||||
let r = moveOrCopy(v, ri, c)
|
||||
result.add r
|
||||
else: # keep the var but transform 'ri':
|
||||
var v = copyNode(n)
|
||||
var itCopy = copyNode(it)
|
||||
for j in 0..<it.len-1:
|
||||
itCopy.add it[j]
|
||||
itCopy.add p(it[^1], c)
|
||||
v.add itCopy
|
||||
result.add v
|
||||
of nkAsgn, nkFastAsgn:
|
||||
if hasDestructor(n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda} and
|
||||
not isCursor(n[0]):
|
||||
# rule (self-assignment-removal):
|
||||
if n[1].kind == nkSym and n[0].kind == nkSym and n[0].sym == n[1].sym:
|
||||
result = newNodeI(nkEmpty, n.info)
|
||||
else:
|
||||
if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}:
|
||||
cycleCheck(n, c)
|
||||
result = moveOrCopy(n[0], n[1], c)
|
||||
else:
|
||||
result = copyNode(n)
|
||||
result.add copyTree(n[0])
|
||||
result.add p(n[1], c, consumed)
|
||||
of nkRaiseStmt:
|
||||
if optOwnedRefs in c.graph.config.globalOptions and n[0].kind != nkEmpty:
|
||||
if n[0].kind in nkCallKinds:
|
||||
let call = p(n[0], c)
|
||||
result = copyNode(n)
|
||||
result.add call
|
||||
else:
|
||||
let tmp = getTemp(c, n[0].typ, n.info)
|
||||
var m = genCopyNoCheck(c, tmp, n[0])
|
||||
m.add p(n[0], c)
|
||||
result = newTree(nkStmtList, genWasMoved(tmp, c), m)
|
||||
var toDisarm = n[0]
|
||||
if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon
|
||||
if toDisarm.kind == nkSym and toDisarm.sym.owner == c.owner:
|
||||
result.add genWasMoved(toDisarm, c)
|
||||
result.add newTree(nkRaiseStmt, tmp)
|
||||
else:
|
||||
result = copyNode(n)
|
||||
if n[0].kind != nkEmpty:
|
||||
result.add p(n[0], c, sinkArg)
|
||||
else:
|
||||
result.add copyNode(n[0])
|
||||
of nkWhileStmt:
|
||||
result = copyNode(n)
|
||||
inc c.inLoop
|
||||
result.add p(n[0], c)
|
||||
result.add p(n[1], c)
|
||||
dec c.inLoop
|
||||
of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
|
||||
nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
|
||||
nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
|
||||
nkExportStmt, nkPragma, nkCommentStmt, nkBreakStmt, nkBreakState:
|
||||
result = n
|
||||
else:
|
||||
result = shallowCopy(n)
|
||||
for i in 0..<n.len:
|
||||
result[i] = p(n[i], c, mode)
|
||||
|
||||
proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
|
||||
# unfortunately, this needs to be kept consistent with the cases
|
||||
# we handle in the 'case of' statement below:
|
||||
const movableNodeKinds = (nkCallKinds + {nkSym, nkTupleConstr, nkClosure, nkObjConstr,
|
||||
nkBracket, nkBracketExpr, nkNilLit})
|
||||
|
||||
case ri.kind
|
||||
of nkCallKinds:
|
||||
result = genSink(c, dest, ri)
|
||||
result.add p(ri, c, consumed = true)
|
||||
result.add p(ri, c, consumed)
|
||||
of nkBracketExpr:
|
||||
if ri[0].kind == nkSym and isUnpackedTuple(ri[0].sym):
|
||||
# unpacking of tuple: move out the elements
|
||||
result = genSink(c, dest, ri)
|
||||
result.add p(ri, c, consumed = true)
|
||||
result.add p(ri, c, consumed)
|
||||
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c):
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
var snk = genSink(c, dest, ri)
|
||||
@@ -648,17 +609,17 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
|
||||
result = newTree(nkStmtList, snk, genWasMoved(ri, c))
|
||||
else:
|
||||
result = genCopy(c, dest, ri)
|
||||
result.add p(ri, c, consumed = true)
|
||||
result.add p(ri, c, consumed)
|
||||
of nkBracket:
|
||||
# array constructor
|
||||
if ri.len > 0 and isDangerousSeq(ri.typ):
|
||||
result = genCopy(c, dest, ri)
|
||||
else:
|
||||
result = genSink(c, dest, ri)
|
||||
result.add p(ri, c, consumed = true)
|
||||
result.add p(ri, c, consumed)
|
||||
of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit:
|
||||
result = genSink(c, dest, ri)
|
||||
result.add p(ri, c, consumed = true)
|
||||
result.add p(ri, c, consumed)
|
||||
of nkSym:
|
||||
if isSinkParam(ri.sym):
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
@@ -674,18 +635,26 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
|
||||
result = newTree(nkStmtList, snk, genWasMoved(ri, c))
|
||||
else:
|
||||
result = genCopy(c, dest, ri)
|
||||
result.add p(ri, c, consumed = true)
|
||||
result.add p(ri, c, consumed)
|
||||
of nkHiddenSubConv, nkHiddenStdConv, nkConv:
|
||||
result = moveOrCopy(dest, ri[1], c)
|
||||
if not sameType(ri.typ, ri[1].typ):
|
||||
let copyRi = copyTree(ri)
|
||||
copyRi[1] = result[^1]
|
||||
result[^1] = copyRi
|
||||
when false:
|
||||
result = moveOrCopy(dest, ri[1], c)
|
||||
if not sameType(ri.typ, ri[1].typ):
|
||||
let copyRi = copyTree(ri)
|
||||
copyRi[1] = result[^1]
|
||||
result[^1] = copyRi
|
||||
else:
|
||||
result = genSink(c, dest, ri)
|
||||
result.add p(ri, c, sinkArg)
|
||||
of nkObjDownConv, nkObjUpConv:
|
||||
result = moveOrCopy(dest, ri[0], c)
|
||||
let copyRi = copyTree(ri)
|
||||
copyRi[0] = result[^1]
|
||||
result[^1] = copyRi
|
||||
when false:
|
||||
result = moveOrCopy(dest, ri[0], c)
|
||||
let copyRi = copyTree(ri)
|
||||
copyRi[0] = result[^1]
|
||||
result[^1] = copyRi
|
||||
else:
|
||||
result = genSink(c, dest, ri)
|
||||
result.add p(ri, c, sinkArg)
|
||||
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt:
|
||||
handleNested(ri): moveOrCopy(dest, node, c)
|
||||
else:
|
||||
@@ -697,7 +666,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
|
||||
result = newTree(nkStmtList, snk, genWasMoved(ri, c))
|
||||
else:
|
||||
result = genCopy(c, dest, ri)
|
||||
result.add p(ri, c, consumed = true)
|
||||
result.add p(ri, c, consumed)
|
||||
|
||||
proc computeUninit(c: var Con) =
|
||||
if not c.uninitComputed:
|
||||
|
||||
@@ -307,6 +307,8 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
|
||||
if not (t.kind in tyMetaTypes or
|
||||
(t.kind == tyStatic and t.n == nil)):
|
||||
result.flags.excl tfInstClearedFlags
|
||||
else:
|
||||
result.flags.excl tfHasAsgn
|
||||
when false:
|
||||
if newDestructors:
|
||||
result.assignment = nil
|
||||
@@ -380,7 +382,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
||||
for i in 1..<t.len:
|
||||
# if one of the params is not concrete, we cannot do anything
|
||||
# but we already raised an error!
|
||||
rawAddSon(result, header[i])
|
||||
rawAddSon(result, header[i], propagateHasAsgn = false)
|
||||
|
||||
if body.kind == tyError:
|
||||
return
|
||||
|
||||
@@ -30,60 +30,6 @@ template payloadSize(cap): int = cap * sizeof(T) + sizeof(int) + sizeof(Allocato
|
||||
|
||||
# XXX make code memory safe for overflows in '*'
|
||||
|
||||
when false:
|
||||
# this is currently not part of Nim's type bound operators and so it's
|
||||
# built into the tracing proc generation just like before.
|
||||
proc `=trace`[T](s: NimSeqV2[T]) =
|
||||
for i in 0 ..< s.len: `=trace`(s.data[i])
|
||||
|
||||
#[
|
||||
Keep in mind that C optimizers are bad at detecting the connection
|
||||
between ``s.p != nil`` and ``s.len != 0`` and that these are intermingled
|
||||
with user-level code that accesses ``s.len`` only, never ``s.p`` directly.
|
||||
This means the check for whether ``s.p`` needs to be freed should
|
||||
be ``s.len == 0`` even though that feels slightly more awkward.
|
||||
]#
|
||||
|
||||
when not defined(nimV2):
|
||||
proc `=destroy`[T](s: var seq[T]) =
|
||||
var x = cast[ptr NimSeqV2[T]](addr s)
|
||||
var p = x.p
|
||||
if p != nil:
|
||||
mixin `=destroy`
|
||||
when not supportsCopyMem(T):
|
||||
for i in 0..<x.len: `=destroy`(p.data[i])
|
||||
if p.allocator != nil:
|
||||
p.allocator.dealloc(p.allocator, p, payloadSize(p.cap))
|
||||
x.p = nil
|
||||
x.len = 0
|
||||
|
||||
proc `=`[T](x: var seq[T]; y: seq[T]) =
|
||||
mixin `=destroy`
|
||||
var a = cast[ptr NimSeqV2[T]](addr x)
|
||||
var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
|
||||
|
||||
if a.p == b.p: return
|
||||
`=destroy`(x)
|
||||
a.len = b.len
|
||||
if b.p != nil:
|
||||
a.p = cast[type(a.p)](alloc(payloadSize(a.len)))
|
||||
when supportsCopyMem(T):
|
||||
if a.len > 0:
|
||||
copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], a.len * sizeof(T))
|
||||
else:
|
||||
for i in 0..<a.len:
|
||||
a.p.data[i] = b.p.data[i]
|
||||
|
||||
proc `=sink`[T](x: var seq[T]; y: seq[T]) =
|
||||
mixin `=destroy`
|
||||
var a = cast[ptr NimSeqV2[T]](addr x)
|
||||
var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
|
||||
if a.p != nil and a.p != b.p:
|
||||
`=destroy`(x)
|
||||
a.len = b.len
|
||||
a.p = b.p
|
||||
|
||||
|
||||
type
|
||||
PayloadBase = object
|
||||
cap: int
|
||||
@@ -181,36 +127,3 @@ proc setLen[T](s: var seq[T], newlen: Natural) =
|
||||
if xu.p == nil or xu.p.cap < newlen:
|
||||
xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newlen - oldLen, sizeof(T)))
|
||||
xu.len = newlen
|
||||
|
||||
when false:
|
||||
proc resize[T](s: var NimSeqV2[T]) =
|
||||
let old = s.cap
|
||||
if old == 0: s.cap = 8
|
||||
else: s.cap = (s.cap * 3) shr 1
|
||||
s.data = cast[type(s.data)](realloc(s.data, old * sizeof(T), s.cap * sizeof(T)))
|
||||
|
||||
proc reserveSlot[T](x: var NimSeqV2[T]): ptr T =
|
||||
if x.len >= x.cap: resize(x)
|
||||
result = addr(x.data[x.len])
|
||||
inc x.len
|
||||
|
||||
template add*[T](x: var NimSeqV2[T]; y: T) =
|
||||
reserveSlot(x)[] = y
|
||||
|
||||
template `[]`*[T](x: NimSeqV2[T]; i: Natural): T =
|
||||
assert i < x.len
|
||||
x.data[i]
|
||||
|
||||
template `[]=`*[T](x: NimSeqV2[T]; i: Natural; y: T) =
|
||||
assert i < x.len
|
||||
x.data[i] = y
|
||||
|
||||
proc `@`*[T](elems: sink openArray[T]): NimSeqV2[T] =
|
||||
result.cap = elems.len
|
||||
result.len = elems.len
|
||||
result.data = cast[type(result.data)](alloc(result.cap * sizeof(T)))
|
||||
when supportsCopyMem(T):
|
||||
copyMem(result.data, unsafeAddr(elems[0]), result.cap * sizeof(T))
|
||||
else:
|
||||
for i in 0..<result.len:
|
||||
result.data[i] = elems[i]
|
||||
|
||||
@@ -3218,6 +3218,9 @@ proc `<`*[T: tuple](x, y: T): bool =
|
||||
const
|
||||
usesDestructors = defined(gcDestructors) or defined(gcHooks)
|
||||
|
||||
when not usesDestructors:
|
||||
{.pragma: nodestroy.}
|
||||
|
||||
when not defined(nimscript) and hasAlloc:
|
||||
type
|
||||
GC_Strategy* = enum ## The strategy the GC should use for the application.
|
||||
@@ -3671,8 +3674,6 @@ when not defined(JS): #and not defined(nimscript):
|
||||
prev: PSafePoint # points to next safe point ON THE STACK
|
||||
status: int
|
||||
context: C_JmpBuf
|
||||
hasRaiseAction: bool
|
||||
raiseAction: proc (e: ref Exception): bool {.closure.}
|
||||
SafePoint = TSafePoint
|
||||
|
||||
when declared(initAllocator):
|
||||
@@ -3776,11 +3777,15 @@ when not defined(JS): #and not defined(nimscript):
|
||||
## Retrieves the current exception; if there is none, `nil` is returned.
|
||||
result = currException
|
||||
|
||||
proc nimBorrowCurrentException(): ref Exception {.compilerRtl, inl, benign, nodestroy.} =
|
||||
# .nodestroy here so that we do not produce a write barrier as the
|
||||
# C codegen only uses it in a borrowed way:
|
||||
result = currException
|
||||
|
||||
proc getCurrentExceptionMsg*(): string {.inline, benign.} =
|
||||
## Retrieves the error message that was attached to the current
|
||||
## exception; if there is none, `""` is returned.
|
||||
var e = getCurrentException()
|
||||
return if e == nil: "" else: e.msg
|
||||
return if currException == nil: "" else: currException.msg
|
||||
|
||||
proc setCurrentException*(exc: ref Exception) {.inline, benign.} =
|
||||
## Sets the current exception.
|
||||
|
||||
@@ -103,19 +103,20 @@ proc pushGcFrame*(s: GcFrame) {.compilerRtl, inl.} =
|
||||
gcFramePtr = s
|
||||
|
||||
proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} =
|
||||
s.hasRaiseAction = false
|
||||
s.prev = excHandler
|
||||
excHandler = s
|
||||
|
||||
proc popSafePoint {.compilerRtl, inl.} =
|
||||
excHandler = excHandler.prev
|
||||
|
||||
proc pushCurrentException(e: ref Exception) {.compilerRtl, inl.} =
|
||||
proc pushCurrentException(e: sink(ref Exception)) {.compilerRtl, inl.} =
|
||||
e.up = currException
|
||||
currException = e
|
||||
#showErrorMessage "A"
|
||||
|
||||
proc popCurrentException {.compilerRtl, inl.} =
|
||||
currException = currException.up
|
||||
#showErrorMessage "B"
|
||||
|
||||
proc popCurrentExceptionEx(id: uint) {.compilerRtl.} =
|
||||
# in cpp backend exceptions can pop-up in the different order they were raised, example #5628
|
||||
@@ -332,28 +333,13 @@ template unhandled(buf, body) =
|
||||
else:
|
||||
body
|
||||
|
||||
proc raiseExceptionAux(e: ref Exception) =
|
||||
if localRaiseHook != nil:
|
||||
if not localRaiseHook(e): return
|
||||
if globalRaiseHook != nil:
|
||||
if not globalRaiseHook(e): return
|
||||
proc nimLeaveFinally() {.compilerRtl.} =
|
||||
when defined(cpp) and not defined(noCppExceptions):
|
||||
if e == currException:
|
||||
{.emit: "throw;".}
|
||||
else:
|
||||
pushCurrentException(e)
|
||||
raiseCounter.inc
|
||||
if raiseCounter == 0:
|
||||
raiseCounter.inc # skip zero at overflow
|
||||
e.raiseId = raiseCounter
|
||||
{.emit: "`e`->raise();".}
|
||||
elif defined(nimQuirky):
|
||||
pushCurrentException(e)
|
||||
{.emit: "throw;".}
|
||||
else:
|
||||
template e: untyped = currException
|
||||
if excHandler != nil:
|
||||
if not excHandler.hasRaiseAction or excHandler.raiseAction(e):
|
||||
pushCurrentException(e)
|
||||
c_longjmp(excHandler.context, 1)
|
||||
c_longjmp(excHandler.context, 1)
|
||||
else:
|
||||
when hasSomeStackTrace:
|
||||
var buf = newStringOfCap(2000)
|
||||
@@ -367,6 +353,7 @@ proc raiseExceptionAux(e: ref Exception) =
|
||||
unhandled(buf):
|
||||
showErrorMessage(buf)
|
||||
quitOrDebug()
|
||||
`=destroy`(buf)
|
||||
else:
|
||||
# ugly, but avoids heap allocations :-)
|
||||
template xadd(buf, s, slen) =
|
||||
@@ -392,7 +379,68 @@ proc raiseExceptionAux(e: ref Exception) =
|
||||
showErrorMessage(tbuf())
|
||||
quitOrDebug()
|
||||
|
||||
proc raiseExceptionEx(e: ref Exception, ename, procname, filename: cstring, line: int) {.compilerRtl.} =
|
||||
proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} =
|
||||
if localRaiseHook != nil:
|
||||
if not localRaiseHook(e): return
|
||||
if globalRaiseHook != nil:
|
||||
if not globalRaiseHook(e): return
|
||||
when defined(cpp) and not defined(noCppExceptions):
|
||||
if e == currException:
|
||||
{.emit: "throw;".}
|
||||
else:
|
||||
pushCurrentException(e)
|
||||
raiseCounter.inc
|
||||
if raiseCounter == 0:
|
||||
raiseCounter.inc # skip zero at overflow
|
||||
e.raiseId = raiseCounter
|
||||
{.emit: "`e`->raise();".}
|
||||
elif defined(nimQuirky):
|
||||
pushCurrentException(e)
|
||||
else:
|
||||
if excHandler != nil:
|
||||
pushCurrentException(e)
|
||||
c_longjmp(excHandler.context, 1)
|
||||
else:
|
||||
when hasSomeStackTrace:
|
||||
var buf = newStringOfCap(2000)
|
||||
if e.trace.len == 0: rawWriteStackTrace(buf)
|
||||
else: add(buf, $e.trace)
|
||||
add(buf, "Error: unhandled exception: ")
|
||||
add(buf, e.msg)
|
||||
add(buf, " [")
|
||||
add(buf, $e.name)
|
||||
add(buf, "]\n")
|
||||
unhandled(buf):
|
||||
showErrorMessage(buf)
|
||||
quitOrDebug()
|
||||
`=destroy`(buf)
|
||||
else:
|
||||
# ugly, but avoids heap allocations :-)
|
||||
template xadd(buf, s, slen) =
|
||||
if L + slen < high(buf):
|
||||
copyMem(addr(buf[L]), cstring(s), slen)
|
||||
inc L, slen
|
||||
template add(buf, s) =
|
||||
xadd(buf, s, s.len)
|
||||
var buf: array[0..2000, char]
|
||||
var L = 0
|
||||
if e.trace.len != 0:
|
||||
add(buf, $e.trace) # gc allocation
|
||||
add(buf, "Error: unhandled exception: ")
|
||||
add(buf, e.msg)
|
||||
add(buf, " [")
|
||||
xadd(buf, e.name, e.name.len)
|
||||
add(buf, "]\n")
|
||||
when defined(nimNoArrayToCstringConversion):
|
||||
template tbuf(): untyped = addr buf
|
||||
else:
|
||||
template tbuf(): untyped = buf
|
||||
unhandled(tbuf()):
|
||||
showErrorMessage(tbuf())
|
||||
quitOrDebug()
|
||||
|
||||
proc raiseExceptionEx(e: sink(ref Exception), ename, procname, filename: cstring,
|
||||
line: int) {.compilerRtl, nodestroy.} =
|
||||
if e.name.isNil: e.name = ename
|
||||
when hasSomeStackTrace:
|
||||
if e.trace.len == 0:
|
||||
@@ -406,7 +454,7 @@ proc raiseExceptionEx(e: ref Exception, ename, procname, filename: cstring, line
|
||||
e.trace.add StackTraceEntry(procname: procname, filename: filename, line: line)
|
||||
raiseExceptionAux(e)
|
||||
|
||||
proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} =
|
||||
proc raiseException(e: sink(ref Exception), ename: cstring) {.compilerRtl.} =
|
||||
raiseExceptionEx(e, ename, nil, nil, 0)
|
||||
|
||||
proc reraiseException() {.compilerRtl.} =
|
||||
|
||||
29
tests/destructor/texceptions.nim
Normal file
29
tests/destructor/texceptions.nim
Normal file
@@ -0,0 +1,29 @@
|
||||
discard """
|
||||
cmd: '''nim c --gc:arc $file'''
|
||||
output: '''0'''
|
||||
"""
|
||||
|
||||
proc other =
|
||||
raise newException(ValueError, "stuff happening")
|
||||
|
||||
proc indirectViaProcCall =
|
||||
var correct = 0
|
||||
for i in 1 .. 20:
|
||||
try:
|
||||
other()
|
||||
except:
|
||||
let x = getCurrentException()
|
||||
correct += ord(x of ValueError)
|
||||
doAssert correct == 20
|
||||
|
||||
proc direct =
|
||||
for i in 1 .. 20:
|
||||
try:
|
||||
raise newException(ValueError, "stuff happening")
|
||||
except ValueError:
|
||||
discard
|
||||
|
||||
let mem = getOccupiedMem()
|
||||
indirectViaProcCall()
|
||||
direct()
|
||||
echo getOccupiedMem() - mem
|
||||
47
tests/destructor/tgcleak4.nim
Normal file
47
tests/destructor/tgcleak4.nim
Normal file
@@ -0,0 +1,47 @@
|
||||
discard """
|
||||
outputsub: "no leak: "
|
||||
cmd: "nim c --gc:arc $file"
|
||||
"""
|
||||
# bug #12758
|
||||
type
|
||||
TExpr {.inheritable.} = object ## abstract base class for an expression
|
||||
PLiteral = ref TLiteral
|
||||
TLiteral = object of TExpr
|
||||
x: int
|
||||
op1: string
|
||||
TPlusExpr = object of TExpr
|
||||
a, b: ref TExpr
|
||||
op2: string
|
||||
|
||||
method eval(e: ref TExpr): int {.base.} =
|
||||
# override this base method
|
||||
quit "to override!"
|
||||
|
||||
method eval(e: ref TLiteral): int = return e.x
|
||||
|
||||
method eval(e: ref TPlusExpr): int =
|
||||
# watch out: relies on dynamic binding
|
||||
return eval(e.a) + eval(e.b)
|
||||
|
||||
proc newLit(x: int): ref TLiteral =
|
||||
new(result)
|
||||
result.x = x
|
||||
result.op1 = $getOccupiedMem()
|
||||
|
||||
proc newPlus(a, b: ref TExpr): ref TPlusExpr =
|
||||
new(result)
|
||||
result.a = a
|
||||
result.b = b
|
||||
result.op2 = $getOccupiedMem()
|
||||
|
||||
const Limit = when compileOption("gc", "markAndSweep") or compileOption("gc", "boehm"): 5*1024*1024 else: 500_000
|
||||
|
||||
for i in 0..100_000:
|
||||
var s: array[0..11, ref TExpr]
|
||||
for j in 0..high(s):
|
||||
s[j] = newPlus(newPlus(newLit(j), newLit(2)), newLit(4))
|
||||
if eval(s[j]) != j+6:
|
||||
quit "error: wrong result"
|
||||
if getOccupiedMem() > Limit: quit("still a leak!")
|
||||
|
||||
echo "no leak: ", getOccupiedMem()
|
||||
@@ -1,7 +1,7 @@
|
||||
discard """
|
||||
cmd: '''nim c --newruntime $file'''
|
||||
output: '''OK 3
|
||||
5 1'''
|
||||
5 2'''
|
||||
"""
|
||||
|
||||
import strutils, math
|
||||
|
||||
Reference in New Issue
Block a user