diff options
-rw-r--r-- | compiler/dfa.nim | 38 | ||||
-rw-r--r-- | tests/destructor/tuse_ownedref_after_move.nim | 60 |
2 files changed, 90 insertions, 8 deletions
diff --git a/compiler/dfa.nim b/compiler/dfa.nim index cdb912163..436fd699f 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -32,7 +32,6 @@ import ast, astalgo, types, intsets, tables, msgs, options, lineinfos, renderer from patterns import sameTrees -from aliases import isPartOf, TAnalysisResult type InstrKind* = enum @@ -570,7 +569,8 @@ const InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp} PathKinds* = {nkDotExpr, nkCheckedFieldExpr, nkBracketExpr, nkDerefExpr, nkHiddenDeref, - nkAddr, nkHiddenAddr} + nkAddr, nkHiddenAddr, + nkHiddenStdConv, nkHiddenSubConv, nkObjDownConv, nkObjUpConv} proc genUse(c: var Con; orig: PNode) = var n = orig @@ -581,6 +581,27 @@ proc genUse(c: var Con; orig: PNode) = 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) +proc aliases(obj, field: PNode): bool = + var n = field + var obj = obj + while obj.kind in {nkHiddenSubConv, nkHiddenStdConv, nkObjDownConv, nkObjUpConv}: + obj = obj[0] + while true: + if sameTrees(obj, n): return true + case n.kind + of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv, + nkObjDownConv, nkObjUpConv, nkHiddenDeref: + n = n[0] + of nkBracketExpr: + let x = n[0] + if x.typ != nil and x.typ.skipTypes(abstractInst).kind == tyTuple: + n = x + else: + break + else: + break + return false + proc instrTargets*(ins: Instr; loc: PNode): bool = assert ins.kind in {def, use} if ins.sym != nil and loc.kind == nkSym: @@ -593,14 +614,16 @@ proc instrTargets*(ins: Instr; loc: PNode): bool = # def x; question: does it affect the 'x.f'? Yes. # use x.f; question: does it affect the full 'x'? No. # use x; question does it affect 'x.f'? Yes. - result = isPartOf(ins.n, loc) == arYes + result = aliases(ins.n, loc) or aliases(loc, ins.n) proc isAnalysableFieldAccess*(n: PNode; owner: PSym): bool = var n = n while true: - if n.kind in {nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv, nkObjDownConv, nkObjUpConv}: + case n.kind + of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv, + nkObjDownConv, nkObjUpConv, nkHiddenDeref: n = n[0] - elif n.kind == nkBracketExpr: + of nkBracketExpr: let x = n[0] if x.typ != nil and x.typ.skipTypes(abstractInst).kind == tyTuple: n = x @@ -710,10 +733,9 @@ proc gen(c: var Con; n: PNode) = for x in n: gen(c, x) of nkPragmaBlock: gen(c, n.lastSon) of nkDiscardStmt: gen(c, n.sons[0]) - of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkExprColonExpr, nkExprEqExpr, - nkCast: + of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast: gen(c, n.sons[1]) - of nkObjDownConv, nkStringToCString, nkCStringToString: gen(c, n.sons[0]) + of nkStringToCString, nkCStringToString: gen(c, n.sons[0]) of nkVarSection, nkLetSection: genVarSection(c, n) of nkDefer: doAssert false, "dfa construction pass requires the elimination of 'defer'" diff --git a/tests/destructor/tuse_ownedref_after_move.nim b/tests/destructor/tuse_ownedref_after_move.nim new file mode 100644 index 000000000..148696ee2 --- /dev/null +++ b/tests/destructor/tuse_ownedref_after_move.nim @@ -0,0 +1,60 @@ +discard """ + cmd: '''nim c --newruntime $file''' + errormsg: "'=' is not available for type <owned Widget>; requires a copy because it's not the last read of ':env.b1()'; another read is done here: tuse_ownedref_after_move.nim(53, 4)" + line: 49 +""" + +import core / allocators +import system / ansi_c + +type + Widget* = ref object of RootObj + drawImpl: owned(proc (self: Widget)) + + Button* = ref object of Widget + caption: string + onclick: owned(proc()) + + Window* = ref object of Widget + elements: seq[owned Widget] + + +proc newButton(caption: string; onclick: owned(proc())): owned Button = + proc draw(self: Widget) = + let b = Button(self) + echo b.caption + + result = Button(drawImpl: draw, caption: caption, onclick: onclick) + +proc newWindow(): owned Window = + proc draw(self: Widget) = + let w = Window(self) + for e in w.elements: + if not e.drawImpl.isNil: e.drawImpl(e) + + result = Window(drawImpl: draw, elements: @[]) + +proc draw(w: Widget) = + if not w.drawImpl.isNil: w.drawImpl(w) + +proc add*(w: Window; elem: owned Widget) = + w.elements.add elem + +proc main = + var w = newWindow() + + var b = newButton("button", nil) + b.onclick = proc () = + b.caption = "clicked!" + w.add b + + w.draw() + # simulate button click: + b.onclick() + + w.draw() + +main() + +let (a, d) = allocCounters() +discard cprintf("%ld %ld new: %ld\n", a, d, allocs) |