diff options
-rw-r--r-- | compiler/ccgexprs.nim | 2 | ||||
-rw-r--r-- | compiler/lambdalifting.nim | 168 | ||||
-rw-r--r-- | compiler/transf.nim | 9 |
3 files changed, 111 insertions, 68 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index afe3ec3cf..7191c0f12 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1966,6 +1966,8 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = genProc(p.module, sym) putLocIntoDest(p, d, sym.loc) of skProc, skConverter, skIterator: + #if sym.kind == skIterator: + # echo renderTree(sym.getBody, {renderIds}) if sfCompileTime in sym.flags: localError(n.info, "request to generate code for .compileTime proc: " & sym.name.s) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 724d3b6d6..694b2447b 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -238,6 +238,8 @@ proc liftIterSym*(n: PNode; owner: PSym): PNode = var v = newNodeI(nkVarSection, n.info) addVar(v, newSymNode(env)) result.add(v) + # leave room for optional up reference setup: + result.add(ast.emptyNode) # add 'new' statement: let envAsNode = env.newSymNode result.add newCall(getSysSym"internalNew", envAsNode) @@ -299,9 +301,35 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym) = result.typ = fieldType 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) = var cp = getEnvParam(fn) - let owner = fn.skipGenericOwner + let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner let t = c.getEnvTypeForOwner(owner) if cp == nil: cp = newSym(skParam, getIdent(paramName), fn, fn.info) @@ -310,6 +338,7 @@ proc addClosureParam(c: var DetectionPass; fn: PSym) = addHiddenParam(fn, cp) elif cp.typ != t: localError(fn.info, "internal error: inconsistent environment type") + echo "adding closure to ", fn.name.s proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = case n.kind @@ -360,7 +389,8 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = addField(obj, s) # create required upFields: 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 while w != nil and w.kind != skModule and last != w: 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: """ 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) addClosureParam(c, w) # , ow createUpField(c, w, up) @@ -393,13 +423,12 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = type LiftingPass = object processed: IntSet - envCreation: PNode - envVar: PNode - owner: PSym # the owner of the 'envVar' + envVars: Table[int, PNode] proc initLiftingPass(fn: PSym): LiftingPass = result.processed = initIntSet() result.processed.incl(fn.id) + result.envVars = initTable[int, PNode]() proc accessViaEnvParam(n: PNode; owner: PSym): PNode = 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) result = n -proc rawClosureCreation(env: PNode; owner: PSym; - d: DetectionPass; - obj: PType): PNode = - result = newNodeI(nkStmtList, env.info) +proc newEnvVar(owner: PSym; typ: PType): PNode = + var v = newSym(skVar, getIdent(envName), owner, owner.info) + incl(v.flags, sfShadowed) + 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: var v = newNodeI(nkVarSection, env.info) addVar(v, env) @@ -437,41 +489,46 @@ proc rawClosureCreation(env: PNode; owner: PSym; # add ``env.param = param`` 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: - let param = getHiddenParam(owner) - if upField.typ == param.typ: + let param = getEnvParam(owner) + if param != nil and upField.typ == param.typ: result.add(newAsgnStmt(rawIndirectAccess(env, upField, 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: + echo owner.name.s localError(env.info, "internal error: cannot create up reference") -proc newEnvVar(owner: PSym; typ: PType): PNode = - var v = newSym(skVar, getIdent(envName), owner, owner.info) +proc closureCreationForIter(iter: PNode; + 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) - 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) + v.typ = getHiddenParam(iter.sym).typ + let vnode = v.newSymNode -proc setupEnvVar(owner: PSym; d: DetectionPass; - c: var LiftingPass) = - if c.owner != owner or c.envVar.isNil: - let envVarType = d.ownerToType.getOrDefault(owner.id) - if envVarType.isNil: - localError owner.info, "internal error: could not determine closure type" - c.envVar = newEnvVar(owner, envVarType) - c.envCreation = rawClosureCreation(c.envVar, owner, d, - envVarType.sons[0]) - c.owner = owner + var vs = newNodeI(nkVarSection, iter.info) + addVar(vs, vnode) + result.add(vs) + result.add(newCall(getSysSym"internalNew", vnode)) + + let upField = lookupInRecord(v.typ.lastSon.n, getIdent(upName)) + if upField != nil: + let u = setupEnvVar(owner, d, c) + if u.typ == upField.typ: + 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; c: var LiftingPass): PNode = - setupEnvVar(owner, d, c) - let access = c.envVar + let access = setupEnvVar(owner, d, c) let obj = access.typ.sons[0] let field = getFieldFromObj(obj, n.sym) if field != nil: @@ -561,18 +618,16 @@ proc wrapIterBody(n: PNode; owner: PSym): PNode = proc symToClosure(n: PNode; owner: PSym; d: DetectionPass; c: var LiftingPass): PNode = let s = n.sym - # direct dependency, so use the outer's env variable: - if s.skipGenericOwner == 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: + + if s == owner: # recursive calls go through (lambda, hiddenParam): let available = getHiddenParam(owner) 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: let available = getHiddenParam(owner) let wanted = getHiddenParam(s).typ @@ -594,28 +649,13 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; case n.kind of nkSym: 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 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) - if c.envCreation.isNil: + if c.envvars.getOrDefault(s.id).isNil: s.ast.sons[bodyPos] = body else: - s.ast.sons[bodyPos] = newTree(nkStmtList, c.envCreation, body) - c.envCreation = nil - c.envVar = oldEnvVar - c.envCreation = oldEnvCreation - c.owner = oldOwner + s.ast.sons[bodyPos] = newTree(nkStmtList, rawClosureCreation(s, d, c), body) if s.typ.callConv == ccClosure: result = symToClosure(n, owner, d, c) elif s.id in d.capturedVars: @@ -671,7 +711,7 @@ proc liftIterToProc*(fn: PSym; body: PNode; ptrType: PType): PNode = fn.kind = oldKind 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 # the transformation even when compiling to JS ... @@ -682,6 +722,7 @@ proc liftLambdas*(fn: PSym, body: PNode): PNode = fn.skipGenericOwner.kind != skModule: # ignore forward declaration: result = body + tooEarly = true else: var d = initDetectionPass(fn) var c = initLiftingPass(fn) @@ -691,9 +732,8 @@ proc liftLambdas*(fn: PSym, body: PNode): PNode = d.somethingToDo = true if d.somethingToDo: var newBody = liftCapturedVars(body, fn, d, c) - if not c.envCreation.isNil: - newBody = newTree(nkStmtList, c.envCreation, newBody) - c.envCreation = nil + if c.envvars.getOrDefault(fn.id) != nil: + newBody = newTree(nkStmtList, rawClosureCreation(fn, d, c), newBody) result = wrapIterBody(newBody, fn) else: result = body diff --git a/compiler/transf.nim b/compiler/transf.nim index 9a762e04e..9ba52bb2d 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -45,7 +45,7 @@ type inlining: int # > 0 if we are in inlining context (copy vars) nestedProcs: int # > 0 if we are in a nested proc contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' - deferDetected: bool + deferDetected, tooEarly: bool PTransf = ref TTransfContext 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 = 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 tc = c.transCon 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}: result = n else: - result = liftLambdas(prc, n) - #result = n var c = openTransf(module, "") + result = liftLambdas(prc, n, c.tooEarly) + #result = n result = processTransf(c, result, prc) liftDefer(c, result) #result = liftLambdas(prc, result) |