fixes subtle interaction between closures and 'yield'
This commit is contained in:
parent
6219ad6a66
commit
e27c675293
1 changed files with 73 additions and 29 deletions
|
|
@ -130,7 +130,8 @@ type
|
||||||
PEnv = ref TEnv
|
PEnv = ref TEnv
|
||||||
TEnv {.final.} = object of TObject
|
TEnv {.final.} = object of TObject
|
||||||
attachedNode, replacementNode: PNode
|
attachedNode, replacementNode: PNode
|
||||||
createdVar: PSym # if != nil it is a used environment
|
createdVar: PNode # if != nil it is a used environment; for closure
|
||||||
|
# iterators this can be 'envParam.env'
|
||||||
createdVarComesFromIter: bool
|
createdVarComesFromIter: bool
|
||||||
capturedVars: seq[PSym] # captured variables in this environment
|
capturedVars: seq[PSym] # captured variables in this environment
|
||||||
up, next: PEnv # outer scope and next to keep all in a list
|
up, next: PEnv # outer scope and next to keep all in a list
|
||||||
|
|
@ -253,10 +254,10 @@ proc addCapturedVar(e: PEnv, v: PSym) =
|
||||||
e.capturedVars.add(v)
|
e.capturedVars.add(v)
|
||||||
addField(e.obj, v)
|
addField(e.obj, v)
|
||||||
|
|
||||||
proc newCall(a, b: PSym): PNode =
|
proc newCall(a: PSym, b: PNode): PNode =
|
||||||
result = newNodeI(nkCall, a.info)
|
result = newNodeI(nkCall, a.info)
|
||||||
result.add newSymNode(a)
|
result.add newSymNode(a)
|
||||||
result.add newSymNode(b)
|
result.add b
|
||||||
|
|
||||||
proc isInnerProc(s, outerProc: PSym): bool =
|
proc isInnerProc(s, outerProc: PSym): bool =
|
||||||
if s.kind in {skProc, skMethod, skConverter, skClosureIterator}:
|
if s.kind in {skProc, skMethod, skConverter, skClosureIterator}:
|
||||||
|
|
@ -433,21 +434,27 @@ proc transformOuterConv(n: PNode): PNode =
|
||||||
if dest.callConv == ccClosure and source.callConv == ccDefault:
|
if dest.callConv == ccClosure and source.callConv == ccDefault:
|
||||||
result = generateThunk(n.sons[1], dest)
|
result = generateThunk(n.sons[1], dest)
|
||||||
|
|
||||||
proc makeClosure(prc, env: PSym, info: TLineInfo): PNode =
|
proc makeClosure(prc: PSym; env: PNode; info: TLineInfo): PNode =
|
||||||
result = newNodeIT(nkClosure, info, prc.typ)
|
result = newNodeIT(nkClosure, info, prc.typ)
|
||||||
result.add(newSymNode(prc))
|
result.add(newSymNode(prc))
|
||||||
if env == nil:
|
if env == nil:
|
||||||
result.add(newNodeIT(nkNilLit, info, getSysType(tyNil)))
|
result.add(newNodeIT(nkNilLit, info, getSysType(tyNil)))
|
||||||
else:
|
else:
|
||||||
result.add(newSymNode(env))
|
result.add(env)
|
||||||
|
|
||||||
proc newClosureCreationVar(e: PEnv): PSym =
|
proc newClosureCreationVar(e: PEnv): PNode =
|
||||||
result = newSym(skVar, getIdent(envName), e.fn, e.attachedNode.info)
|
var v = newSym(skVar, getIdent(envName), e.fn, e.attachedNode.info)
|
||||||
incl(result.flags, sfShadowed)
|
incl(v.flags, sfShadowed)
|
||||||
result.typ = newType(tyRef, e.fn)
|
v.typ = newType(tyRef, e.fn)
|
||||||
result.typ.rawAddSon(e.obj)
|
v.typ.rawAddSon(e.obj)
|
||||||
|
if e.fn.kind == skClosureIterator:
|
||||||
|
let it = initIter(e.fn)
|
||||||
|
addUniqueField(it.obj, v)
|
||||||
|
result = indirectAccess(newSymNode(it.closureParam), v, v.info)
|
||||||
|
else:
|
||||||
|
result = newSymNode(v)
|
||||||
|
|
||||||
proc getClosureVar(e: PEnv): PSym =
|
proc getClosureVar(e: PEnv): PNode =
|
||||||
if e.createdVar == nil:
|
if e.createdVar == nil:
|
||||||
result = newClosureCreationVar(e)
|
result = newClosureCreationVar(e)
|
||||||
e.createdVar = result
|
e.createdVar = result
|
||||||
|
|
@ -470,7 +477,7 @@ proc transformInnerProc(o: POuterContext; e: PEnv, n: PNode): PNode =
|
||||||
let s = n.sym
|
let s = n.sym
|
||||||
if s == e.fn:
|
if s == e.fn:
|
||||||
# recursive calls go through (lambda, hiddenParam):
|
# recursive calls go through (lambda, hiddenParam):
|
||||||
result = makeClosure(s, getEnvParam(s), n.info)
|
result = makeClosure(s, getEnvParam(s).newSymNode, n.info)
|
||||||
elif isInnerProc(s, o.fn) and s.typ.callConv == ccClosure:
|
elif isInnerProc(s, o.fn) and s.typ.callConv == ccClosure:
|
||||||
# ugh: call to some other inner proc;
|
# ugh: call to some other inner proc;
|
||||||
result = makeClosure(s, findEnv(o, s).getClosureVar, n.info)
|
result = makeClosure(s, findEnv(o, s).getClosureVar, n.info)
|
||||||
|
|
@ -552,13 +559,18 @@ proc searchForInnerProcs(o: POuterContext, n: PNode, env: PEnv) =
|
||||||
# of closure creation; however for simplicity we merge closures between
|
# of closure creation; however for simplicity we merge closures between
|
||||||
# branches, in fact, only loop bodies are of interest here as only they
|
# branches, in fact, only loop bodies are of interest here as only they
|
||||||
# yield observable changes in semantics. For Zahary we also
|
# yield observable changes in semantics. For Zahary we also
|
||||||
# include ``nkBlock``.
|
# include ``nkBlock``. We don't do this for closure iterators because
|
||||||
var body = n.len-1
|
# 'yield' can produce wrong code otherwise (XXX show example):
|
||||||
for i in countup(0, body - 1): searchForInnerProcs(o, n.sons[i], env)
|
if env.fn.kind != skClosureIterator:
|
||||||
# special handling for the loop body:
|
var body = n.len-1
|
||||||
let ex = closureCreationPoint(n.sons[body])
|
for i in countup(0, body - 1): searchForInnerProcs(o, n.sons[i], env)
|
||||||
searchForInnerProcs(o, n.sons[body], newEnv(o, env, ex, env.fn))
|
# special handling for the loop body:
|
||||||
n.sons[body] = ex
|
let ex = closureCreationPoint(n.sons[body])
|
||||||
|
searchForInnerProcs(o, n.sons[body], newEnv(o, env, ex, env.fn))
|
||||||
|
n.sons[body] = ex
|
||||||
|
else:
|
||||||
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
|
searchForInnerProcs(o, n.sons[i], env)
|
||||||
of nkVarSection, nkLetSection:
|
of nkVarSection, nkLetSection:
|
||||||
# we need to compute a mapping var->declaredBlock. Note: The definition
|
# we need to compute a mapping var->declaredBlock. Note: The definition
|
||||||
# counts, not the block where it is captured!
|
# counts, not the block where it is captured!
|
||||||
|
|
@ -601,11 +613,12 @@ proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
|
||||||
result.sons[0] = le
|
result.sons[0] = le
|
||||||
result.sons[1] = ri
|
result.sons[1] = ri
|
||||||
|
|
||||||
proc rawClosureCreation(o: POuterContext, scope: PEnv; env: PSym): PNode =
|
proc rawClosureCreation(o: POuterContext, scope: PEnv; env: PNode): PNode =
|
||||||
result = newNodeI(nkStmtList, env.info)
|
result = newNodeI(nkStmtList, env.info)
|
||||||
var v = newNodeI(nkVarSection, env.info)
|
if env.kind == nkSym:
|
||||||
addVar(v, newSymNode(env))
|
var v = newNodeI(nkVarSection, env.info)
|
||||||
result.add(v)
|
addVar(v, env)
|
||||||
|
result.add(v)
|
||||||
# add 'new' statement:
|
# add 'new' statement:
|
||||||
result.add(newCall(getSysSym"internalNew", env))
|
result.add(newCall(getSysSym"internalNew", env))
|
||||||
|
|
||||||
|
|
@ -632,14 +645,14 @@ proc rawClosureCreation(o: POuterContext, scope: PEnv; env: PSym): PNode =
|
||||||
newSymNode(getEnvParam(scope.fn)), env.info))
|
newSymNode(getEnvParam(scope.fn)), env.info))
|
||||||
else:
|
else:
|
||||||
result.add(newAsgnStmt(indirectAccess(env, scope.upField, env.info),
|
result.add(newAsgnStmt(indirectAccess(env, scope.upField, env.info),
|
||||||
newSymNode(getClosureVar(scope.up)), env.info))
|
getClosureVar(scope.up), env.info))
|
||||||
|
|
||||||
proc generateClosureCreation(o: POuterContext, scope: PEnv): PNode =
|
proc generateClosureCreation(o: POuterContext, scope: PEnv): PNode =
|
||||||
var env = getClosureVar(scope)
|
var env = getClosureVar(scope)
|
||||||
result = rawClosureCreation(o, scope, env)
|
result = rawClosureCreation(o, scope, env)
|
||||||
|
|
||||||
proc generateIterClosureCreation(o: POuterContext; env: PEnv;
|
proc generateIterClosureCreation(o: POuterContext; env: PEnv;
|
||||||
scope: PNode): PSym =
|
scope: PNode): PNode =
|
||||||
if env.createdVarComesFromIter or env.createdVar.isNil:
|
if env.createdVarComesFromIter or env.createdVar.isNil:
|
||||||
# we have to create a new closure:
|
# we have to create a new closure:
|
||||||
result = newClosureCreationVar(env)
|
result = newClosureCreationVar(env)
|
||||||
|
|
@ -716,8 +729,25 @@ proc liftIterSym*(n: PNode): PNode =
|
||||||
addVar(v, newSymNode(env))
|
addVar(v, newSymNode(env))
|
||||||
result.add(v)
|
result.add(v)
|
||||||
# add 'new' statement:
|
# add 'new' statement:
|
||||||
result.add newCall(getSysSym"internalNew", env)
|
let envAsNode = env.newSymNode
|
||||||
result.add makeClosure(iter, env, n.info)
|
result.add newCall(getSysSym"internalNew", envAsNode)
|
||||||
|
result.add makeClosure(iter, envAsNode, n.info)
|
||||||
|
|
||||||
|
when false:
|
||||||
|
proc transformRemainingLocals(n: PNode; it: TIter): PNode =
|
||||||
|
assert it.fn.kind == skClosureIterator
|
||||||
|
result = n
|
||||||
|
case n.kind
|
||||||
|
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: discard
|
||||||
|
of nkSym:
|
||||||
|
let local = n.sym
|
||||||
|
if interestingIterVar(local) and it.fn == local.owner:
|
||||||
|
addUniqueField(it.obj, local)
|
||||||
|
result = indirectAccess(newSymNode(it.closureParam), local, n.info)
|
||||||
|
else:
|
||||||
|
result = newNodeI(n.kind, n.info, n.len)
|
||||||
|
for i in 0.. <n.safeLen:
|
||||||
|
result.sons[i] = transformRemainingLocals(n.sons[i], it)
|
||||||
|
|
||||||
template envActive(env): expr =
|
template envActive(env): expr =
|
||||||
(env.capturedVars.len > 0 or env.upField != nil)
|
(env.capturedVars.len > 0 or env.upField != nil)
|
||||||
|
|
@ -748,6 +778,12 @@ proc finishEnvironments(o: POuterContext) =
|
||||||
if scope.sons[0].kind == nkEmpty:
|
if scope.sons[0].kind == nkEmpty:
|
||||||
# change the empty node to contain the closure construction:
|
# change the empty node to contain the closure construction:
|
||||||
scope.sons[0] = env.replacementNode
|
scope.sons[0] = env.replacementNode
|
||||||
|
when false:
|
||||||
|
if env.fn.kind == skClosureIterator:
|
||||||
|
scope.sons[0] = transformRemainingLocals(env.replacementNode,
|
||||||
|
initIter(env.fn))
|
||||||
|
else:
|
||||||
|
scope.sons[0] = env.replacementNode
|
||||||
env = env.next
|
env = env.next
|
||||||
|
|
||||||
proc transformOuterProcBody(o: POuterContext, n: PNode; it: TIter): PNode =
|
proc transformOuterProcBody(o: POuterContext, n: PNode; it: TIter): PNode =
|
||||||
|
|
@ -863,6 +899,14 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
|
||||||
let newBody = transformOuterProcBody(o, body, initIter(local))
|
let newBody = transformOuterProcBody(o, body, initIter(local))
|
||||||
if newBody != nil:
|
if newBody != nil:
|
||||||
local.ast.sons[bodyPos] = newBody
|
local.ast.sons[bodyPos] = newBody
|
||||||
|
when false:
|
||||||
|
if n.sons[1].kind == nkSym:
|
||||||
|
var local = n.sons[1].sym
|
||||||
|
if it.fn.kind == skClosureIterator and interestingIterVar(local) and
|
||||||
|
it.fn == local.owner:
|
||||||
|
# every local goes through the closure:
|
||||||
|
addUniqueField(it.obj, local)
|
||||||
|
n.sons[1] = indirectAccess(newSymNode(it.closureParam), local, n.info)
|
||||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||||
let x = transformOuterProc(o, n.sons[1], it)
|
let x = transformOuterProc(o, n.sons[1], it)
|
||||||
if x != nil: n.sons[1] = x
|
if x != nil: n.sons[1] = x
|
||||||
|
|
@ -962,7 +1006,7 @@ proc liftForLoop*(body: PNode): PNode =
|
||||||
addVar(v, newSymNode(env))
|
addVar(v, newSymNode(env))
|
||||||
result.add(v)
|
result.add(v)
|
||||||
# add 'new' statement:
|
# add 'new' statement:
|
||||||
result.add(newCall(getSysSym"internalNew", env))
|
result.add(newCall(getSysSym"internalNew", env.newSymNode))
|
||||||
|
|
||||||
var loopBody = newNodeI(nkStmtList, body.info, 3)
|
var loopBody = newNodeI(nkStmtList, body.info, 3)
|
||||||
var whileLoop = newNodeI(nkWhileStmt, body.info, 2)
|
var whileLoop = newNodeI(nkWhileStmt, body.info, 2)
|
||||||
|
|
@ -981,7 +1025,7 @@ proc liftForLoop*(body: PNode): PNode =
|
||||||
|
|
||||||
addSon(vpart, ast.emptyNode) # no explicit type
|
addSon(vpart, ast.emptyNode) # no explicit type
|
||||||
if not env.isNil:
|
if not env.isNil:
|
||||||
call.sons[0] = makeClosure(call.sons[0].sym, env, body.info)
|
call.sons[0] = makeClosure(call.sons[0].sym, env.newSymNode, body.info)
|
||||||
addSon(vpart, call)
|
addSon(vpart, call)
|
||||||
addSon(v2, vpart)
|
addSon(v2, vpart)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue