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
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 import ast, astalgo, types, intsets, tables, msgs, options, lineinfos, renderer
from patterns import sameTrees from patterns import sameTrees
from aliases import isPartOf, TAnalysisResult
type type
InstrKind* = enum InstrKind* = enum
@ -570,7 +569,8 @@ const
InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp} InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp}
PathKinds* = {nkDotExpr, nkCheckedFieldExpr, PathKinds* = {nkDotExpr, nkCheckedFieldExpr,
nkBracketExpr, nkDerefExpr, nkHiddenDeref, nkBracketExpr, nkDerefExpr, nkHiddenDeref,
nkAddr, nkHiddenAddr} nkAddr, nkHiddenAddr,
nkHiddenStdConv, nkHiddenSubConv, nkObjDownConv, nkObjUpConv}
proc genUse(c: var Con; orig: PNode) = proc genUse(c: var Con; orig: PNode) =
var n = orig var n = orig
@ -581,6 +581,27 @@ proc genUse(c: var Con; orig: PNode) =
if n.kind == nkSym and n.sym.kind in InterestingSyms: 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 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 = proc instrTargets*(ins: Instr; loc: PNode): bool =
assert ins.kind in {def, use} assert ins.kind in {def, use}
if ins.sym != nil and loc.kind == nkSym: 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. # def x; question: does it affect the 'x.f'? Yes.
# use x.f; question: does it affect the full 'x'? No. # use x.f; question: does it affect the full 'x'? No.
# use x; question does it affect 'x.f'? Yes. # 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 = proc isAnalysableFieldAccess*(n: PNode; owner: PSym): bool =
var n = n var n = n
while true: 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] n = n[0]
elif n.kind == nkBracketExpr: of nkBracketExpr:
let x = n[0] let x = n[0]
if x.typ != nil and x.typ.skipTypes(abstractInst).kind == tyTuple: if x.typ != nil and x.typ.skipTypes(abstractInst).kind == tyTuple:
n = x n = x
@ -710,10 +733,9 @@ proc gen(c: var Con; n: PNode) =
for x in n: gen(c, x) for x in n: gen(c, x)
of nkPragmaBlock: gen(c, n.lastSon) of nkPragmaBlock: gen(c, n.lastSon)
of nkDiscardStmt: gen(c, n.sons[0]) of nkDiscardStmt: gen(c, n.sons[0])
of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkExprColonExpr, nkExprEqExpr, of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast:
nkCast:
gen(c, n.sons[1]) 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 nkVarSection, nkLetSection: genVarSection(c, n)
of nkDefer: of nkDefer:
doAssert false, "dfa construction pass requires the elimination of 'defer'" 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)