make move-analysis smarter; see tuse_ownedref_after_move test case

This commit is contained in:
Araq
2019-04-16 10:35:30 +02:00
parent a520eb7766
commit ce024a73bf
2 changed files with 90 additions and 8 deletions

View File

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

View File

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