next steps to produce working closure iterators

This commit is contained in:
Andreas Rumpf 2016-01-03 15:23:07 +01:00
commit 1ad69be988
3 changed files with 111 additions and 68 deletions

View file

@ -1966,6 +1966,8 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
genProc(p.module, sym) genProc(p.module, sym)
putLocIntoDest(p, d, sym.loc) putLocIntoDest(p, d, sym.loc)
of skProc, skConverter, skIterator: of skProc, skConverter, skIterator:
#if sym.kind == skIterator:
# echo renderTree(sym.getBody, {renderIds})
if sfCompileTime in sym.flags: if sfCompileTime in sym.flags:
localError(n.info, "request to generate code for .compileTime proc: " & localError(n.info, "request to generate code for .compileTime proc: " &
sym.name.s) sym.name.s)

View file

@ -238,6 +238,8 @@ proc liftIterSym*(n: PNode; owner: PSym): PNode =
var v = newNodeI(nkVarSection, n.info) var v = newNodeI(nkVarSection, n.info)
addVar(v, newSymNode(env)) addVar(v, newSymNode(env))
result.add(v) result.add(v)
# leave room for optional up reference setup:
result.add(ast.emptyNode)
# add 'new' statement: # add 'new' statement:
let envAsNode = env.newSymNode let envAsNode = env.newSymNode
result.add newCall(getSysSym"internalNew", envAsNode) result.add newCall(getSysSym"internalNew", envAsNode)
@ -299,9 +301,35 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym) =
result.typ = fieldType result.typ = fieldType
rawAddField(obj, result) rawAddField(obj, result)
discard """
There are a couple of possibilities of how to implement closure
iterators that capture outer variables in a traditional sense
(aka closure closure iterators).
1. Transform iter() to iter(state, capturedEnv). So use 2 hidden
parameters.
2. Add the captured vars directly to 'state'.
3. Make capturedEnv an up-reference of 'state'.
We do (3) here because (2) is obviously wrong and (1) is wrong too.
Consider:
proc outer =
var xx = 9
iterator foo() =
var someState = 3
proc bar = echo someState
proc baz = someState = 0
baz()
bar()
"""
proc addClosureParam(c: var DetectionPass; fn: PSym) = proc addClosureParam(c: var DetectionPass; fn: PSym) =
var cp = getEnvParam(fn) var cp = getEnvParam(fn)
let owner = fn.skipGenericOwner let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner
let t = c.getEnvTypeForOwner(owner) let t = c.getEnvTypeForOwner(owner)
if cp == nil: if cp == nil:
cp = newSym(skParam, getIdent(paramName), fn, fn.info) cp = newSym(skParam, getIdent(paramName), fn, fn.info)
@ -310,6 +338,7 @@ proc addClosureParam(c: var DetectionPass; fn: PSym) =
addHiddenParam(fn, cp) addHiddenParam(fn, cp)
elif cp.typ != t: elif cp.typ != t:
localError(fn.info, "internal error: inconsistent environment type") localError(fn.info, "internal error: inconsistent environment type")
echo "adding closure to ", fn.name.s
proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
case n.kind case n.kind
@ -360,7 +389,8 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
addField(obj, s) addField(obj, s)
# create required upFields: # create required upFields:
var w = owner.skipGenericOwner var w = owner.skipGenericOwner
if isInnerProc(w): if isInnerProc(w) or owner.isIterator:
if owner.isIterator: w = owner
let last = if ow.isIterator: ow.skipGenericOwner else: ow let last = if ow.isIterator: ow.skipGenericOwner else: ow
while w != nil and w.kind != skModule and last != w: while w != nil and w.kind != skModule and last != w:
discard """ discard """
@ -373,7 +403,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
# give it the same env type that outer's env var gets: # give it the same env type that outer's env var gets:
""" """
let up = w.skipGenericOwner let up = w.skipGenericOwner
#echo "up for ", w.name.s, " up ", up.name.s echo "up for ", w.name.s, " up ", up.name.s
markAsClosure(w, n) markAsClosure(w, n)
addClosureParam(c, w) # , ow addClosureParam(c, w) # , ow
createUpField(c, w, up) createUpField(c, w, up)
@ -393,13 +423,12 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
type type
LiftingPass = object LiftingPass = object
processed: IntSet processed: IntSet
envCreation: PNode envVars: Table[int, PNode]
envVar: PNode
owner: PSym # the owner of the 'envVar'
proc initLiftingPass(fn: PSym): LiftingPass = proc initLiftingPass(fn: PSym): LiftingPass =
result.processed = initIntSet() result.processed = initIntSet()
result.processed.incl(fn.id) result.processed.incl(fn.id)
result.envVars = initTable[int, PNode]()
proc accessViaEnvParam(n: PNode; owner: PSym): PNode = proc accessViaEnvParam(n: PNode; owner: PSym): PNode =
let s = n.sym let s = n.sym
@ -419,10 +448,33 @@ proc accessViaEnvParam(n: PNode; owner: PSym): PNode =
localError(n.info, "internal error: environment misses: " & s.name.s) localError(n.info, "internal error: environment misses: " & s.name.s)
result = n result = n
proc rawClosureCreation(env: PNode; owner: PSym; proc newEnvVar(owner: PSym; typ: PType): PNode =
d: DetectionPass; var v = newSym(skVar, getIdent(envName), owner, owner.info)
obj: PType): PNode = incl(v.flags, sfShadowed)
result = newNodeI(nkStmtList, env.info) v.typ = typ
if owner.kind == skIterator and owner.typ.callConv == ccClosure:
let it = getHiddenParam(owner)
addUniqueField(it.typ.sons[0], v)
result = indirectAccess(newSymNode(it), v, v.info)
else:
result = newSymNode(v)
proc setupEnvVar(owner: PSym; d: DetectionPass;
c: var LiftingPass): PNode =
result = c.envvars.getOrDefault(owner.id)
if result.isNil:
let envVarType = d.ownerToType.getOrDefault(owner.id)
if envVarType.isNil:
localError owner.info, "internal error: could not determine closure type"
result = newEnvVar(owner, envVarType)
c.envVars[owner.id] = result
proc rawClosureCreation(owner: PSym;
d: DetectionPass; c: var LiftingPass): PNode =
result = newNodeI(nkStmtList, owner.info)
#if owner.isIterator: return result
let env = setupEnvVar(owner, d, c)
if env.kind == nkSym: if env.kind == nkSym:
var v = newNodeI(nkVarSection, env.info) var v = newNodeI(nkVarSection, env.info)
addVar(v, env) addVar(v, env)
@ -437,41 +489,46 @@ proc rawClosureCreation(env: PNode; owner: PSym;
# add ``env.param = param`` # add ``env.param = param``
result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info)) result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info))
let upField = lookupInRecord(obj.n, getIdent(upName)) let upField = lookupInRecord(env.typ.lastSon.n, getIdent(upName))
if upField != nil: if upField != nil:
let param = getHiddenParam(owner) let param = getEnvParam(owner)
if upField.typ == param.typ: if param != nil and upField.typ == param.typ:
result.add(newAsgnStmt(rawIndirectAccess(env, upField, env.info), result.add(newAsgnStmt(rawIndirectAccess(env, upField, env.info),
newSymNode(param), env.info)) newSymNode(param), env.info))
#elif oldenv != nil and oldenv.typ == upField.typ:
# result.add(newAsgnStmt(rawIndirectAccess(env, upField, env.info),
# oldenv, env.info))
else: else:
echo owner.name.s
localError(env.info, "internal error: cannot create up reference") localError(env.info, "internal error: cannot create up reference")
proc newEnvVar(owner: PSym; typ: PType): PNode = proc closureCreationForIter(iter: PNode;
var v = newSym(skVar, getIdent(envName), owner, owner.info) d: DetectionPass; c: var LiftingPass): PNode =
result = newNodeI(nkStmtListExpr, iter.info)
let owner = iter.sym.skipGenericOwner
var v = newSym(skVar, getIdent(envName), owner, iter.info)
incl(v.flags, sfShadowed) incl(v.flags, sfShadowed)
v.typ = typ v.typ = getHiddenParam(iter.sym).typ
if owner.kind == skIterator and owner.typ.callConv == ccClosure: let vnode = v.newSymNode
let it = getHiddenParam(owner)
addUniqueField(it.typ.sons[0], v)
result = indirectAccess(newSymNode(it), v, v.info)
else:
result = newSymNode(v)
proc setupEnvVar(owner: PSym; d: DetectionPass; var vs = newNodeI(nkVarSection, iter.info)
c: var LiftingPass) = addVar(vs, vnode)
if c.owner != owner or c.envVar.isNil: result.add(vs)
let envVarType = d.ownerToType.getOrDefault(owner.id) result.add(newCall(getSysSym"internalNew", vnode))
if envVarType.isNil:
localError owner.info, "internal error: could not determine closure type" let upField = lookupInRecord(v.typ.lastSon.n, getIdent(upName))
c.envVar = newEnvVar(owner, envVarType) if upField != nil:
c.envCreation = rawClosureCreation(c.envVar, owner, d, let u = setupEnvVar(owner, d, c)
envVarType.sons[0]) if u.typ == upField.typ:
c.owner = owner result.add(newAsgnStmt(rawIndirectAccess(vnode, upField, iter.info),
u, iter.info))
else:
localError(iter.info, "internal error: cannot create up reference for iter")
result.add makeClosure(iter.sym, vnode, iter.info)
proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass; proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
c: var LiftingPass): PNode = c: var LiftingPass): PNode =
setupEnvVar(owner, d, c) let access = setupEnvVar(owner, d, c)
let access = c.envVar
let obj = access.typ.sons[0] let obj = access.typ.sons[0]
let field = getFieldFromObj(obj, n.sym) let field = getFieldFromObj(obj, n.sym)
if field != nil: if field != nil:
@ -561,18 +618,16 @@ proc wrapIterBody(n: PNode; owner: PSym): PNode =
proc symToClosure(n: PNode; owner: PSym; d: DetectionPass; proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
c: var LiftingPass): PNode = c: var LiftingPass): PNode =
let s = n.sym let s = n.sym
# direct dependency, so use the outer's env variable:
if s.skipGenericOwner == owner: if s == owner:
if c.owner != owner or c.envVar.isNil:
setupEnvVar(owner, d, c)
if c.owner != owner or c.envVar.isNil:
localError(n.info, "internal error: no environment var found")
return n
result = makeClosure(s, c.envVar, n.info)
elif s == owner:
# recursive calls go through (lambda, hiddenParam): # recursive calls go through (lambda, hiddenParam):
let available = getHiddenParam(owner) let available = getHiddenParam(owner)
result = makeClosure(s, available.newSymNode, n.info) result = makeClosure(s, available.newSymNode, n.info)
elif s.isIterator:
result = closureCreationForIter(n, d, c)
elif s.skipGenericOwner == owner:
# direct dependency, so use the outer's env variable:
result = makeClosure(s, setupEnvVar(owner, d, c), n.info)
else: else:
let available = getHiddenParam(owner) let available = getHiddenParam(owner)
let wanted = getHiddenParam(s).typ let wanted = getHiddenParam(s).typ
@ -594,28 +649,13 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
case n.kind case n.kind
of nkSym: of nkSym:
let s = n.sym let s = n.sym
# if s.kind == skIterator and s.typ.callConv == ccClosure:
# consider: [i1, i2, i1] Since we merged the iterator's closure
# with the captured owning variables, we need to generate the
# closure generation code again: XXX think about this more,
# closure iterators are really strange in this regard.
if isInnerProc(s): if isInnerProc(s):
if not c.processed.containsOrIncl(s.id): if not c.processed.containsOrIncl(s.id):
let oldEnvVar = c.envVar
let oldEnvCreation = c.envCreation
let oldOwner = c.owner
c.envVar = nil
c.envCreation = nil
c.owner = s
let body = wrapIterBody(liftCapturedVars(s.getBody, s, d, c), s) let body = wrapIterBody(liftCapturedVars(s.getBody, s, d, c), s)
if c.envCreation.isNil: if c.envvars.getOrDefault(s.id).isNil:
s.ast.sons[bodyPos] = body s.ast.sons[bodyPos] = body
else: else:
s.ast.sons[bodyPos] = newTree(nkStmtList, c.envCreation, body) s.ast.sons[bodyPos] = newTree(nkStmtList, rawClosureCreation(s, d, c), body)
c.envCreation = nil
c.envVar = oldEnvVar
c.envCreation = oldEnvCreation
c.owner = oldOwner
if s.typ.callConv == ccClosure: if s.typ.callConv == ccClosure:
result = symToClosure(n, owner, d, c) result = symToClosure(n, owner, d, c)
elif s.id in d.capturedVars: elif s.id in d.capturedVars:
@ -671,7 +711,7 @@ proc liftIterToProc*(fn: PSym; body: PNode; ptrType: PType): PNode =
fn.kind = oldKind fn.kind = oldKind
fn.typ.callConv = oldCC fn.typ.callConv = oldCC
proc liftLambdas*(fn: PSym, body: PNode): PNode = proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode =
# XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs # XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs
# the transformation even when compiling to JS ... # the transformation even when compiling to JS ...
@ -682,6 +722,7 @@ proc liftLambdas*(fn: PSym, body: PNode): PNode =
fn.skipGenericOwner.kind != skModule: fn.skipGenericOwner.kind != skModule:
# ignore forward declaration: # ignore forward declaration:
result = body result = body
tooEarly = true
else: else:
var d = initDetectionPass(fn) var d = initDetectionPass(fn)
var c = initLiftingPass(fn) var c = initLiftingPass(fn)
@ -691,9 +732,8 @@ proc liftLambdas*(fn: PSym, body: PNode): PNode =
d.somethingToDo = true d.somethingToDo = true
if d.somethingToDo: if d.somethingToDo:
var newBody = liftCapturedVars(body, fn, d, c) var newBody = liftCapturedVars(body, fn, d, c)
if not c.envCreation.isNil: if c.envvars.getOrDefault(fn.id) != nil:
newBody = newTree(nkStmtList, c.envCreation, newBody) newBody = newTree(nkStmtList, rawClosureCreation(fn, d, c), newBody)
c.envCreation = nil
result = wrapIterBody(newBody, fn) result = wrapIterBody(newBody, fn)
else: else:
result = body result = body

View file

@ -45,7 +45,7 @@ type
inlining: int # > 0 if we are in inlining context (copy vars) inlining: int # > 0 if we are in inlining context (copy vars)
nestedProcs: int # > 0 if we are in a nested proc nestedProcs: int # > 0 if we are in a nested proc
contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
deferDetected: bool deferDetected, tooEarly: bool
PTransf = ref TTransfContext PTransf = ref TTransfContext
proc newTransNode(a: PNode): PTransNode {.inline.} = proc newTransNode(a: PNode): PTransNode {.inline.} =
@ -112,7 +112,8 @@ proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode =
proc transformSymAux(c: PTransf, n: PNode): PNode = proc transformSymAux(c: PTransf, n: PNode): PNode =
if n.sym.kind == skIterator and n.sym.typ.callConv == ccClosure: if n.sym.kind == skIterator and n.sym.typ.callConv == ccClosure:
return liftIterSym(n, getCurrOwner(c)) if c.tooEarly: return n
else: return liftIterSym(n, getCurrOwner(c))
var b: PNode var b: PNode
var tc = c.transCon var tc = c.transCon
if sfBorrow in n.sym.flags and n.sym.kind in routineKinds: if sfBorrow in n.sym.flags and n.sym.kind in routineKinds:
@ -883,9 +884,9 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
if nfTransf in n.flags or prc.kind in {skTemplate}: if nfTransf in n.flags or prc.kind in {skTemplate}:
result = n result = n
else: else:
result = liftLambdas(prc, n)
#result = n
var c = openTransf(module, "") var c = openTransf(module, "")
result = liftLambdas(prc, n, c.tooEarly)
#result = n
result = processTransf(c, result, prc) result = processTransf(c, result, prc)
liftDefer(c, result) liftDefer(c, result)
#result = liftLambdas(prc, result) #result = liftLambdas(prc, result)