Closure iter transformation
This commit is contained in:
parent
9048bcc54b
commit
d71f69ab50
10 changed files with 713 additions and 44 deletions
|
|
@ -2326,7 +2326,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||||
of nkParForStmt: genParForStmt(p, n)
|
of nkParForStmt: genParForStmt(p, n)
|
||||||
of nkState: genState(p, n)
|
of nkState: genState(p, n)
|
||||||
of nkGotoState: genGotoState(p, n)
|
of nkGotoState: genGotoState(p, n)
|
||||||
of nkBreakState: genBreakState(p, n)
|
of nkBreakState: genBreakState(p, n, d)
|
||||||
else: internalError(n.info, "expr(" & $n.kind & "); unknown node kind")
|
else: internalError(n.info, "expr(" & $n.kind & "); unknown node kind")
|
||||||
|
|
||||||
proc genNamedConstExpr(p: BProc, n: PNode): Rope =
|
proc genNamedConstExpr(p: BProc, n: PNode): Rope =
|
||||||
|
|
|
||||||
|
|
@ -177,17 +177,17 @@ proc genGotoState(p: BProc, n: PNode) =
|
||||||
lineF(p, cpsStmts, "case $2: goto $1$2;$n", [prefix, rope(i)])
|
lineF(p, cpsStmts, "case $2: goto $1$2;$n", [prefix, rope(i)])
|
||||||
lineF(p, cpsStmts, "}$n", [])
|
lineF(p, cpsStmts, "}$n", [])
|
||||||
|
|
||||||
proc genBreakState(p: BProc, n: PNode) =
|
proc genBreakState(p: BProc, n: PNode, d: var TLoc) =
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
|
initLoc(d, locExpr, n, OnUnknown)
|
||||||
|
|
||||||
if n.sons[0].kind == nkClosure:
|
if n.sons[0].kind == nkClosure:
|
||||||
# XXX this produces quite inefficient code!
|
|
||||||
initLocExpr(p, n.sons[0].sons[1], a)
|
initLocExpr(p, n.sons[0].sons[1], a)
|
||||||
lineF(p, cpsStmts, "if (((NI*) $1)[1] < 0) break;$n", [rdLoc(a)])
|
d.r = "(((NI*) $1)[1] < 0)" % [rdLoc(a)]
|
||||||
else:
|
else:
|
||||||
initLocExpr(p, n.sons[0], a)
|
initLocExpr(p, n.sons[0], a)
|
||||||
# the environment is guaranteed to contain the 'state' field at offset 1:
|
# the environment is guaranteed to contain the 'state' field at offset 1:
|
||||||
lineF(p, cpsStmts, "if ((((NI*) $1.ClE_0)[1]) < 0) break;$n", [rdLoc(a)])
|
d.r = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)]
|
||||||
# lineF(p, cpsStmts, "if (($1) < 0) break;$n", [rdLoc(a)])
|
|
||||||
|
|
||||||
proc genGotoVar(p: BProc; value: PNode) =
|
proc genGotoVar(p: BProc; value: PNode) =
|
||||||
if value.kind notin {nkCharLit..nkUInt64Lit}:
|
if value.kind notin {nkCharLit..nkUInt64Lit}:
|
||||||
|
|
|
||||||
632
compiler/closureiters.nim
Normal file
632
compiler/closureiters.nim
Normal file
|
|
@ -0,0 +1,632 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The Nim Compiler
|
||||||
|
# (c) Copyright 2018 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
# This file implements closure iterator transformations.
|
||||||
|
# The main idea is to split the closure iterator body to top level statements.
|
||||||
|
# The body is split by yield statement.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# while a > 0:
|
||||||
|
# echo "hi"
|
||||||
|
# yield a
|
||||||
|
# dec a
|
||||||
|
#
|
||||||
|
# Should be transformed to:
|
||||||
|
# STATE0:
|
||||||
|
# if a > 0:
|
||||||
|
# echo "hi"
|
||||||
|
# :state = 1 # Next state
|
||||||
|
# return a # yield
|
||||||
|
# else:
|
||||||
|
# :state = 2 # Next state
|
||||||
|
# break :stateLoop # Proceed to the next state
|
||||||
|
# STATE1:
|
||||||
|
# dec a
|
||||||
|
# :state = 0 # Next state
|
||||||
|
# break :stateLoop # Proceed to the next state
|
||||||
|
# STATE2:
|
||||||
|
# :state = -1 # End of execution
|
||||||
|
|
||||||
|
# The transformation should play well with lambdalifting, however depending
|
||||||
|
# on situation, it can be called either before or after lambdalifting
|
||||||
|
# transformation. As such we behave slightly differently, when accessing
|
||||||
|
# iterator state, or using temp variables. If lambdalifting did not happen,
|
||||||
|
# we just create local variables, so that they will be lifted further on.
|
||||||
|
# Otherwise, we utilize existing env, created by lambdalifting.
|
||||||
|
|
||||||
|
# Lambdalifting treats :state variable specially, it should always end up
|
||||||
|
# as the first field in env. Currently C codegen depends on this behavior.
|
||||||
|
|
||||||
|
# One special subtransformation is nkStmtListExpr lowering.
|
||||||
|
# Example:
|
||||||
|
# template foo(): int =
|
||||||
|
# yield 1
|
||||||
|
# 2
|
||||||
|
#
|
||||||
|
# iterator it(): int {.closure.} =
|
||||||
|
# if foo() == 2:
|
||||||
|
# yield 3
|
||||||
|
#
|
||||||
|
# If a nkStmtListExpr has yield inside, it has first to be lowered to:
|
||||||
|
# yield 1
|
||||||
|
# :tmpSlLower = 2
|
||||||
|
# if :tmpSlLower == 2:
|
||||||
|
# yield 3
|
||||||
|
|
||||||
|
|
||||||
|
import
|
||||||
|
intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os, options,
|
||||||
|
idents, renderer, types, magicsys, rodread, lowerings, tables, sequtils,
|
||||||
|
lambdalifting
|
||||||
|
|
||||||
|
type ClosureIteratorTransformationContext = object
|
||||||
|
fn: PSym
|
||||||
|
stateVarSym: PSym # :state variable. nil if env already introduced by lambdalifting
|
||||||
|
states: seq[PNode] # The resulting states. Every state is an nkState node.
|
||||||
|
blockLevel: int # Temp used to transform break and continue stmts
|
||||||
|
stateLoopLabel: PSym # Label to break on, when jumping between states.
|
||||||
|
exitStateIdx: int # index of the last state
|
||||||
|
tempVarId: int # unique name counter
|
||||||
|
tempVars: PNode # Temp var decls, nkVarSection
|
||||||
|
loweredStmtListExpr: PNode # Temporary used for nkStmtListExpr lowering
|
||||||
|
|
||||||
|
proc newStateAssgn(ctx: var ClosureIteratorTransformationContext, stateNo: int = -2): PNode =
|
||||||
|
# Creates state assignmen:
|
||||||
|
# :state = stateNo
|
||||||
|
|
||||||
|
result = newNode(nkAsgn)
|
||||||
|
if ctx.stateVarSym.isNil:
|
||||||
|
let state = getStateField(ctx.fn)
|
||||||
|
assert state != nil
|
||||||
|
result.add(rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)),
|
||||||
|
state, result.info))
|
||||||
|
else:
|
||||||
|
result.add(newSymNode(ctx.stateVarSym))
|
||||||
|
result.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt)))
|
||||||
|
|
||||||
|
proc setStateInAssgn(stateAssgn: PNode, stateNo: int) =
|
||||||
|
assert stateAssgn.kind == nkAsgn
|
||||||
|
assert stateAssgn[1].kind == nkIntLit
|
||||||
|
stateAssgn[1].intVal = stateNo
|
||||||
|
|
||||||
|
proc newState(ctx: var ClosureIteratorTransformationContext, n, gotoOut: PNode): int =
|
||||||
|
# Creates a new state, adds it to the context fills out `gotoOut` so that it
|
||||||
|
# will goto this state.
|
||||||
|
# Returns index of the newly created state
|
||||||
|
|
||||||
|
result = ctx.states.len
|
||||||
|
let resLit = newIntLit(result)
|
||||||
|
let s = newNodeI(nkState, n.info)
|
||||||
|
s.add(resLit)
|
||||||
|
s.add(n)
|
||||||
|
ctx.states.add(s)
|
||||||
|
if not gotoOut.isNil:
|
||||||
|
assert(gotoOut.len == 0)
|
||||||
|
gotoOut.add(newIntLit(result))
|
||||||
|
|
||||||
|
proc toStmtList(n: PNode): PNode =
|
||||||
|
result = n
|
||||||
|
if result.kind notin {nkStmtList, nkStmtListExpr}:
|
||||||
|
result = newNodeI(nkStmtList, n.info)
|
||||||
|
result.add(n)
|
||||||
|
|
||||||
|
proc addGotoOut(n: PNode, gotoOut: PNode): PNode =
|
||||||
|
# Make sure `n` is a stmtlist, and ends with `gotoOut`
|
||||||
|
|
||||||
|
result = toStmtList(n)
|
||||||
|
if result.len != 0 and result.sons[^1].kind != nkGotoState:
|
||||||
|
result.add(gotoOut)
|
||||||
|
|
||||||
|
proc newTempVarAccess(ctx: var ClosureIteratorTransformationContext, typ: PType, i: TLineInfo): PNode =
|
||||||
|
if not ctx.stateVarSym.isNil:
|
||||||
|
# We haven't gone through labmda lifting yet, so just create a local var,
|
||||||
|
# it will be lifted later
|
||||||
|
let s = newSym(skVar, getIdent(":tmpSlLower" & $ctx.tempVarId), ctx.fn, i)
|
||||||
|
s.typ = typ
|
||||||
|
|
||||||
|
if ctx.tempVars.isNil:
|
||||||
|
ctx.tempVars = newNode(nkVarSection)
|
||||||
|
addVar(ctx.tempVars, newSymNode(s))
|
||||||
|
|
||||||
|
result = newSymNode(s)
|
||||||
|
else:
|
||||||
|
# Lambda lifting is done, insert temp var to env.
|
||||||
|
let s = newSym(skVar, getIdent(":tmpSlLower" & $ctx.tempVarId), ctx.fn, i)
|
||||||
|
s.typ = typ
|
||||||
|
result = freshVarForClosureIter(s, ctx.fn)
|
||||||
|
|
||||||
|
inc ctx.tempVarId
|
||||||
|
|
||||||
|
proc hasYields(n: PNode): bool =
|
||||||
|
# TODO: This is very inefficient. It traverses the node, looking for nkYieldStmt.
|
||||||
|
case n.kind
|
||||||
|
of nkYieldStmt:
|
||||||
|
result = true
|
||||||
|
of nkCharLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkStrLit..nkTripleStrLit,
|
||||||
|
nkSym, nkIdent, procDefs, nkTemplateDef:
|
||||||
|
discard
|
||||||
|
else:
|
||||||
|
for c in n:
|
||||||
|
if c.hasYields:
|
||||||
|
result = true
|
||||||
|
break
|
||||||
|
|
||||||
|
proc transformBreaksAndContinuesInWhile(ctx: var ClosureIteratorTransformationContext, n: PNode, before, after: PNode): PNode =
|
||||||
|
result = n
|
||||||
|
case n.kind
|
||||||
|
of nkCharLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkStrLit..nkTripleStrLit,
|
||||||
|
nkSym, nkIdent, procDefs, nkTemplateDef:
|
||||||
|
discard
|
||||||
|
of nkWhileStmt: discard # Do not recurse into nested whiles
|
||||||
|
of nkContinueStmt:
|
||||||
|
result = before
|
||||||
|
of nkBlockStmt:
|
||||||
|
inc ctx.blockLevel
|
||||||
|
result[1] = ctx.transformBreaksAndContinuesInWhile(result[1], before, after)
|
||||||
|
dec ctx.blockLevel
|
||||||
|
of nkBreakStmt:
|
||||||
|
if ctx.blockLevel == 0:
|
||||||
|
result = after
|
||||||
|
else:
|
||||||
|
for i in 0 ..< n.len:
|
||||||
|
n[i] = ctx.transformBreaksAndContinuesInWhile(n[i], before, after)
|
||||||
|
|
||||||
|
proc transformBreaksInBlock(ctx: var ClosureIteratorTransformationContext, n: PNode, label, after: PNode): PNode =
|
||||||
|
result = n
|
||||||
|
case n.kind
|
||||||
|
of nkCharLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkStrLit..nkTripleStrLit,
|
||||||
|
nkSym, nkIdent, procDefs, nkTemplateDef:
|
||||||
|
discard
|
||||||
|
of nkBlockStmt, nkWhileStmt:
|
||||||
|
inc ctx.blockLevel
|
||||||
|
result[1] = ctx.transformBreaksInBlock(result[1], label, after)
|
||||||
|
dec ctx.blockLevel
|
||||||
|
of nkBreakStmt:
|
||||||
|
if n[0].kind == nkEmpty:
|
||||||
|
if ctx.blockLevel == 0:
|
||||||
|
result = after
|
||||||
|
else:
|
||||||
|
if label.kind == nkSym and n[0].sym == label.sym:
|
||||||
|
result = after
|
||||||
|
else:
|
||||||
|
for i in 0 ..< n.len:
|
||||||
|
n[i] = ctx.transformBreaksInBlock(n[i], label, after)
|
||||||
|
|
||||||
|
proc collectExceptState(n: PNode): PNode =
|
||||||
|
var ifStmt = newNode(nkIfStmt)
|
||||||
|
for c in n:
|
||||||
|
if c.kind == nkExceptBranch:
|
||||||
|
var ifBranch: PNode
|
||||||
|
var branchBody: PNode
|
||||||
|
|
||||||
|
if c[0].kind == nkType:
|
||||||
|
assert(c.len == 2)
|
||||||
|
ifBranch = newNode(nkElifBranch)
|
||||||
|
let expression = newNodeI(nkCall, n.info)
|
||||||
|
expression.add(callCodegenProc("getCurrentException", emptyNode))
|
||||||
|
expression.add(c[0])
|
||||||
|
ifBranch.add(expression)
|
||||||
|
branchBody = c[1]
|
||||||
|
else:
|
||||||
|
assert(c.len == 1)
|
||||||
|
if ifStmt.len == 0:
|
||||||
|
ifStmt = newNode(nkStmtList)
|
||||||
|
ifBranch = newNode(nkStmtList)
|
||||||
|
else:
|
||||||
|
ifBranch = newNode(nkElse)
|
||||||
|
branchBody = c[0]
|
||||||
|
|
||||||
|
ifBranch.add(branchBody)
|
||||||
|
ifStmt.add(ifBranch)
|
||||||
|
|
||||||
|
if ifStmt.len != 0:
|
||||||
|
result = newNode(nkStmtList)
|
||||||
|
result.add(ifStmt)
|
||||||
|
else:
|
||||||
|
result = emptyNode
|
||||||
|
|
||||||
|
proc getFinallyNode(n: PNode): PNode =
|
||||||
|
result = n[^1]
|
||||||
|
if result.kind == nkFinally:
|
||||||
|
result = result[0]
|
||||||
|
else:
|
||||||
|
result = emptyNode
|
||||||
|
|
||||||
|
proc hasYieldsInExpressions(n: PNode): bool =
|
||||||
|
case n.kind
|
||||||
|
of nkCharLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkStrLit..nkTripleStrLit,
|
||||||
|
nkSym, nkIdent, procDefs, nkTemplateDef:
|
||||||
|
discard
|
||||||
|
of nkStmtListExpr:
|
||||||
|
result = n.hasYields
|
||||||
|
of nkStmtList, nkWhileStmt, nkCaseStmt, nkIfStmt:
|
||||||
|
discard
|
||||||
|
else:
|
||||||
|
for c in n:
|
||||||
|
if c.hasYieldsInExpressions:
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc lowerStmtListExpr(ctx: var ClosureIteratorTransformationContext, n: PNode): PNode =
|
||||||
|
result = n
|
||||||
|
case n.kind
|
||||||
|
of nkCharLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkStrLit..nkTripleStrLit,
|
||||||
|
nkSym, nkIdent, procDefs, nkTemplateDef:
|
||||||
|
discard
|
||||||
|
of nkStmtListExpr:
|
||||||
|
if n.hasYields:
|
||||||
|
for i in 0 .. n.len - 2:
|
||||||
|
ctx.loweredStmtListExpr.add(n[i])
|
||||||
|
|
||||||
|
let tv = ctx.newTempVarAccess(n.typ, n[^1].info)
|
||||||
|
let asgn = newNode(nkAsgn)
|
||||||
|
asgn.add(tv)
|
||||||
|
asgn.add(n[^1])
|
||||||
|
ctx.loweredStmtListExpr.add(asgn)
|
||||||
|
result = tv
|
||||||
|
|
||||||
|
else:
|
||||||
|
for i in 0 ..< n.len:
|
||||||
|
n[i] = ctx.lowerStmtListExpr(n[i])
|
||||||
|
|
||||||
|
proc transformClosureIteratorBody(ctx: var ClosureIteratorTransformationContext, n: PNode, gotoOut: PNode): PNode =
|
||||||
|
result = n
|
||||||
|
case n.kind:
|
||||||
|
of nkCharLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkStrLit..nkTripleStrLit,
|
||||||
|
nkSym, nkIdent, procDefs, nkTemplateDef:
|
||||||
|
discard
|
||||||
|
|
||||||
|
of nkStmtList:
|
||||||
|
result = addGotoOut(result, gotoOut)
|
||||||
|
for i in 0 ..< n.len:
|
||||||
|
if n[i].hasYieldsInExpressions:
|
||||||
|
# Lower nkStmtListExpr nodes inside `n[i]` first
|
||||||
|
assert(ctx.loweredStmtListExpr.isNil)
|
||||||
|
ctx.loweredStmtListExpr = newNodeI(nkStmtList, n.info)
|
||||||
|
n[i] = ctx.lowerStmtListExpr(n[i])
|
||||||
|
ctx.loweredStmtListExpr.add(n[i])
|
||||||
|
n[i] = ctx.loweredStmtListExpr
|
||||||
|
ctx.loweredStmtListExpr = nil
|
||||||
|
|
||||||
|
if n[i].hasYields:
|
||||||
|
# Create a new split
|
||||||
|
let go = newNode(nkGotoState)
|
||||||
|
n[i] = ctx.transformClosureIteratorBody(n[i], go)
|
||||||
|
|
||||||
|
let s = newNode(nkStmtList)
|
||||||
|
for j in i + 1 ..< n.len:
|
||||||
|
s.add(n[j])
|
||||||
|
|
||||||
|
n.sons.setLen(i + 1)
|
||||||
|
discard ctx.newState(s, go)
|
||||||
|
discard ctx.transformClosureIteratorBody(s, gotoOut)
|
||||||
|
break
|
||||||
|
|
||||||
|
of nkStmtListExpr:
|
||||||
|
assert(false, "nkStmtListExpr not lowered")
|
||||||
|
|
||||||
|
of nkYieldStmt:
|
||||||
|
# echo "YIELD!"
|
||||||
|
result = newNodeI(nkStmtList, n.info)
|
||||||
|
result.add(n)
|
||||||
|
result.add(gotoOut)
|
||||||
|
|
||||||
|
of nkElse, nkElseExpr:
|
||||||
|
result[0] = addGotoOut(result[0], gotoOut)
|
||||||
|
result[0] = ctx.transformClosureIteratorBody(result[0], gotoOut)
|
||||||
|
|
||||||
|
of nkElifBranch, nkElifExpr, nkOfBranch:
|
||||||
|
result[1] = addGotoOut(result[1], gotoOut)
|
||||||
|
result[1] = ctx.transformClosureIteratorBody(result[1], gotoOut)
|
||||||
|
|
||||||
|
of nkIfStmt, nkCaseStmt:
|
||||||
|
for i in 0 ..< n.len:
|
||||||
|
n[i] = ctx.transformClosureIteratorBody(n[i], gotoOut)
|
||||||
|
if n[^1].kind != nkElse:
|
||||||
|
# We don't have an else branch, but every possible branch has to end with
|
||||||
|
# gotoOut, so add else here.
|
||||||
|
let elseBranch = newNode(nkElse)
|
||||||
|
elseBranch.add(gotoOut)
|
||||||
|
n.add(elseBranch)
|
||||||
|
|
||||||
|
of nkWhileStmt:
|
||||||
|
# while e:
|
||||||
|
# s
|
||||||
|
# ->
|
||||||
|
# BEGIN_STATE:
|
||||||
|
# if e:
|
||||||
|
# s
|
||||||
|
# goto BEGIN_STATE
|
||||||
|
# else:
|
||||||
|
# goto OUT
|
||||||
|
|
||||||
|
result = newNodeI(nkGotoState, n.info)
|
||||||
|
|
||||||
|
let s = newNodeI(nkStmtList, n.info)
|
||||||
|
discard ctx.newState(s, result)
|
||||||
|
let ifNode = newNodeI(nkIfStmt, n.info)
|
||||||
|
let elifBranch = newNodeI(nkElifBranch, n.info)
|
||||||
|
elifBranch.add(n[0])
|
||||||
|
|
||||||
|
var body = addGotoOut(n[1], result)
|
||||||
|
|
||||||
|
body = ctx.transformBreaksAndContinuesInWhile(body, result, gotoOut)
|
||||||
|
body = ctx.transformClosureIteratorBody(body, result)
|
||||||
|
|
||||||
|
elifBranch.add(body)
|
||||||
|
ifNode.add(elifBranch)
|
||||||
|
|
||||||
|
let elseBranch = newNode(nkElse)
|
||||||
|
elseBranch.add(gotoOut)
|
||||||
|
ifNode.add(elseBranch)
|
||||||
|
s.add(ifNode)
|
||||||
|
|
||||||
|
of nkBlockStmt:
|
||||||
|
result[1] = addGotoOut(result[1], gotoOut)
|
||||||
|
result[1] = ctx.transformBreaksInBlock(result[1], result[0], gotoOut)
|
||||||
|
result[1] = ctx.transformClosureIteratorBody(result[1], gotoOut)
|
||||||
|
|
||||||
|
of nkTryStmt:
|
||||||
|
var tryBody = toStmtList(n[0])
|
||||||
|
|
||||||
|
# let popTry = newNode(nkPar)
|
||||||
|
# popTry.add(newIdentNode(getIdent("popTry"), n.info))
|
||||||
|
var finallyBody = newNode(nkStmtList)
|
||||||
|
# finallyBody.add(popTry)
|
||||||
|
finallyBody.add(getFinallyNode(n))
|
||||||
|
|
||||||
|
var tryCatchOut = newNode(nkGotoState)
|
||||||
|
|
||||||
|
tryBody = ctx.transformClosureIteratorBody(tryBody, tryCatchOut)
|
||||||
|
var exceptBody = collectExceptState(n)
|
||||||
|
|
||||||
|
var exceptIdx = -1
|
||||||
|
if exceptBody.kind != nkEmpty:
|
||||||
|
exceptBody = ctx.transformClosureIteratorBody(exceptBody, tryCatchOut)
|
||||||
|
exceptIdx = ctx.newState(exceptBody, nil)
|
||||||
|
|
||||||
|
finallyBody = ctx.transformClosureIteratorBody(finallyBody, gotoOut)
|
||||||
|
let finallyIdx = ctx.newState(finallyBody, tryCatchOut)
|
||||||
|
|
||||||
|
# let pushTry = newNode(nkPar) #newCall(newSym("pushTry"), newIntLit(exceptIdx))
|
||||||
|
# pushTry.add(newIdentNode(getIdent("pushTry"), n.info))
|
||||||
|
# pushTry.add(newIntLit(exceptIdx))
|
||||||
|
# pushTry.add(newIntLit(finallyIdx))
|
||||||
|
# tryBody.sons.insert(pushTry, 0)
|
||||||
|
|
||||||
|
result = tryBody
|
||||||
|
|
||||||
|
of nkGotoState, nkForStmt:
|
||||||
|
internalError("closure iter " & $n.kind)
|
||||||
|
|
||||||
|
else:
|
||||||
|
for i in 0 ..< n.len:
|
||||||
|
n[i] = ctx.transformClosureIteratorBody(n[i], gotoOut)
|
||||||
|
|
||||||
|
proc stateFromGotoState(n: PNode): int =
|
||||||
|
assert(n.kind == nkGotoState)
|
||||||
|
result = n[0].intVal.int
|
||||||
|
|
||||||
|
proc tranformStateAssignments(ctx: var ClosureIteratorTransformationContext, n: PNode): PNode =
|
||||||
|
# This transforms 3 patterns:
|
||||||
|
########################## 1
|
||||||
|
# yield e
|
||||||
|
# goto STATE
|
||||||
|
# ->
|
||||||
|
# :state = STATE
|
||||||
|
# return e
|
||||||
|
########################## 2
|
||||||
|
# goto STATE
|
||||||
|
# ->
|
||||||
|
# :state = STATE
|
||||||
|
# break :stateLoop
|
||||||
|
########################## 3
|
||||||
|
# return e
|
||||||
|
# ->
|
||||||
|
# :state = -1
|
||||||
|
# return e
|
||||||
|
#
|
||||||
|
result = n
|
||||||
|
case n.kind
|
||||||
|
of nkStmtList, nkStmtListExpr:
|
||||||
|
if n.len != 0 and n[0].kind == nkYieldStmt:
|
||||||
|
assert(n.len == 2)
|
||||||
|
assert(n[1].kind == nkGotoState)
|
||||||
|
|
||||||
|
result = newNodeI(nkStmtList, n.info)
|
||||||
|
result.add(ctx.newStateAssgn(stateFromGotoState(n[1])))
|
||||||
|
|
||||||
|
var retStmt = newNodeI(nkReturnStmt, n.info)
|
||||||
|
if n[0].sons[0].kind != nkEmpty:
|
||||||
|
var a = newNodeI(nkAsgn, n[0].sons[0].info)
|
||||||
|
var retVal = n[0].sons[0] #liftCapturedVars(n.sons[0], owner, d, c)
|
||||||
|
addSon(a, newSymNode(getClosureIterResult(ctx.fn)))
|
||||||
|
addSon(a, retVal)
|
||||||
|
retStmt.add(a)
|
||||||
|
else:
|
||||||
|
retStmt.add(emptyNode)
|
||||||
|
|
||||||
|
result.add(retStmt)
|
||||||
|
else:
|
||||||
|
for i in 0 ..< n.len:
|
||||||
|
n[i] = ctx.tranformStateAssignments(n[i])
|
||||||
|
|
||||||
|
of nkCharLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkStrLit..nkTripleStrLit,
|
||||||
|
nkSym, nkIdent, procDefs, nkTemplateDef:
|
||||||
|
discard
|
||||||
|
|
||||||
|
of nkReturnStmt:
|
||||||
|
|
||||||
|
result = newNodeI(nkStmtList, n.info)
|
||||||
|
result.add(ctx.newStateAssgn(-1))
|
||||||
|
result.add(n)
|
||||||
|
|
||||||
|
of nkGotoState:
|
||||||
|
result = newNodeI(nkStmtList, n.info)
|
||||||
|
result.add(ctx.newStateAssgn(stateFromGotoState(n)))
|
||||||
|
|
||||||
|
let breakState = newNodeI(nkBreakStmt, n.info)
|
||||||
|
breakState.add(newSymNode(ctx.stateLoopLabel))
|
||||||
|
result.add(breakState)
|
||||||
|
|
||||||
|
else:
|
||||||
|
for i in 0 ..< n.len:
|
||||||
|
n[i] = ctx.tranformStateAssignments(n[i])
|
||||||
|
|
||||||
|
proc skipStmtList(n: PNode): PNode =
|
||||||
|
result = n
|
||||||
|
while result.kind in {nkStmtList}:
|
||||||
|
if result.len == 0: return emptyNode
|
||||||
|
result = result[0]
|
||||||
|
|
||||||
|
proc skipThroughEmptyStates(ctx: var ClosureIteratorTransformationContext, n: PNode): PNode =
|
||||||
|
result = n
|
||||||
|
case n.kind
|
||||||
|
of nkCharLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkStrLit..nkTripleStrLit,
|
||||||
|
nkSym, nkIdent, procDefs, nkTemplateDef:
|
||||||
|
discard
|
||||||
|
of nkGotoState:
|
||||||
|
var maxJumps = ctx.states.len # maxJumps used only for debugging purposes.
|
||||||
|
result = copyTree(n)
|
||||||
|
while true:
|
||||||
|
let label = result[0].intVal.int
|
||||||
|
if label == ctx.exitStateIdx: break
|
||||||
|
var newLabel = label
|
||||||
|
if label == -1:
|
||||||
|
newLabel = ctx.exitStateIdx
|
||||||
|
else:
|
||||||
|
let fs = ctx.states[label][1].skipStmtList()
|
||||||
|
if fs.kind == nkGotoState:
|
||||||
|
newLabel = fs[0].intVal.int
|
||||||
|
if label == newLabel: break
|
||||||
|
result[0].intVal = newLabel
|
||||||
|
dec maxJumps
|
||||||
|
if maxJumps == 0:
|
||||||
|
assert(false, "Internal error")
|
||||||
|
|
||||||
|
let label = result[0].intVal.int
|
||||||
|
result[0].intVal = ctx.states[label][0].intVal
|
||||||
|
else:
|
||||||
|
for i in 0 ..< n.len:
|
||||||
|
n[i] = ctx.skipThroughEmptyStates(n[i])
|
||||||
|
|
||||||
|
proc wrapIntoStateLoop(ctx: var ClosureIteratorTransformationContext, n: PNode): PNode =
|
||||||
|
result = newNode(nkWhileStmt)
|
||||||
|
result.add(newSymNode(getSysSym("true")))
|
||||||
|
|
||||||
|
let loopBody = newNodeI(nkStmtList, n.info)
|
||||||
|
result.add(loopBody)
|
||||||
|
|
||||||
|
if not ctx.stateVarSym.isNil:
|
||||||
|
let varSect = newNodeI(nkVarSection, n.info)
|
||||||
|
addVar(varSect, newSymNode(ctx.stateVarSym))
|
||||||
|
loopBody.add(varSect)
|
||||||
|
|
||||||
|
if not ctx.tempVars.isNil:
|
||||||
|
loopBody.add(ctx.tempVars)
|
||||||
|
|
||||||
|
let blockStmt = newNodeI(nkBlockStmt, n.info)
|
||||||
|
blockStmt.add(newSymNode(ctx.stateLoopLabel))
|
||||||
|
|
||||||
|
let blockBody = newNodeI(nkStmtList, n.info)
|
||||||
|
blockStmt.add(blockBody)
|
||||||
|
|
||||||
|
let gs = newNodeI(nkGotoState, n.info)
|
||||||
|
if ctx.stateVarSym.isNil:
|
||||||
|
gs.add(rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)), getStateField(ctx.fn), n.info))
|
||||||
|
else:
|
||||||
|
gs.add(newSymNode(ctx.stateVarSym))
|
||||||
|
|
||||||
|
gs.add(newIntLit(ctx.states.len - 1))
|
||||||
|
blockBody.add(gs)
|
||||||
|
blockBody.add(n)
|
||||||
|
# gs.add(rawIndirectAccess(newSymNode(ctx.fn.getHiddenParam), getStateField(ctx.fn), n.info))
|
||||||
|
|
||||||
|
loopBody.add(blockStmt)
|
||||||
|
|
||||||
|
proc deleteEmptyStates(ctx: var ClosureIteratorTransformationContext) =
|
||||||
|
let goOut = newNode(nkGotoState)
|
||||||
|
goOut.add(newIntLit(-1))
|
||||||
|
|
||||||
|
ctx.exitStateIdx = ctx.newState(goOut, nil)
|
||||||
|
|
||||||
|
# Apply new state indexes and mark unused states with -1
|
||||||
|
var iValid = 0
|
||||||
|
for i, s in ctx.states:
|
||||||
|
let body = s[1].skipStmtList()
|
||||||
|
if body.kind == nkGotoState and i != ctx.states.len - 1:
|
||||||
|
# This is an empty state. Mark with -1.
|
||||||
|
s[0].intVal = -1
|
||||||
|
else:
|
||||||
|
s[0].intVal = iValid
|
||||||
|
inc iValid
|
||||||
|
|
||||||
|
for i, s in ctx.states:
|
||||||
|
let body = s[1].skipStmtList()
|
||||||
|
if body.kind != nkGotoState:
|
||||||
|
discard ctx.skipThroughEmptyStates(s)
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
while i < ctx.states.len - 1:
|
||||||
|
let fs = ctx.states[i][1].skipStmtList()
|
||||||
|
if fs.kind == nkGotoState:
|
||||||
|
ctx.states.delete(i)
|
||||||
|
else:
|
||||||
|
inc i
|
||||||
|
|
||||||
|
proc transformClosureIterator*(fn: PSym, n: PNode): PNode =
|
||||||
|
var ctx: ClosureIteratorTransformationContext
|
||||||
|
ctx.fn = fn
|
||||||
|
|
||||||
|
if getEnvParam(fn).isNil:
|
||||||
|
# Lambda lifting was not done yet. Use temporary :state sym, which
|
||||||
|
# be handled specially by lambda lifting. Local temp vars (if needed)
|
||||||
|
# should folllow the same logic.
|
||||||
|
ctx.stateVarSym = newSym(skVar, getIdent(":state"), fn, fn.info)
|
||||||
|
ctx.stateVarSym.typ = createClosureIterStateType(fn)
|
||||||
|
|
||||||
|
ctx.states = @[]
|
||||||
|
ctx.stateLoopLabel = newSym(skLabel, getIdent(":stateLoop"), fn, fn.info)
|
||||||
|
let n = n.toStmtList
|
||||||
|
|
||||||
|
discard ctx.newState(n, nil)
|
||||||
|
let gotoOut = newNode(nkGotoState)
|
||||||
|
gotoOut.add(newIntLit(-1))
|
||||||
|
|
||||||
|
# Splitting transformation
|
||||||
|
discard ctx.transformClosureIteratorBody(n, gotoOut)
|
||||||
|
|
||||||
|
# Optimize empty states away
|
||||||
|
ctx.deleteEmptyStates()
|
||||||
|
|
||||||
|
# Make new body by concating the list of states
|
||||||
|
result = newNode(nkStmtList)
|
||||||
|
for i, s in ctx.states:
|
||||||
|
# result.add(s)
|
||||||
|
let body = s[1]
|
||||||
|
s.sons.del(1)
|
||||||
|
result.add(s)
|
||||||
|
result.add(body)
|
||||||
|
|
||||||
|
result = ctx.tranformStateAssignments(result)
|
||||||
|
|
||||||
|
# Add excpetion handling
|
||||||
|
var hasExceptions = false
|
||||||
|
if hasExceptions:
|
||||||
|
discard # TODO:
|
||||||
|
# result = wrapIntoTryCatch(result)
|
||||||
|
|
||||||
|
# while true:
|
||||||
|
# block :stateLoop:
|
||||||
|
# gotoState
|
||||||
|
# body
|
||||||
|
result = ctx.wrapIntoStateLoop(result)
|
||||||
|
|
||||||
|
# echo "TRANSFORM TO STATES2: "
|
||||||
|
# debug(result)
|
||||||
|
# echo renderTree(result)
|
||||||
|
|
@ -7,11 +7,11 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
# This include file implements lambda lifting for the transformator.
|
# This file implements lambda lifting for the transformator.
|
||||||
|
|
||||||
import
|
import
|
||||||
intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os,
|
intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os, options,
|
||||||
idents, renderer, types, magicsys, rodread, lowerings, tables
|
idents, renderer, types, magicsys, rodread, lowerings, tables, sequtils
|
||||||
|
|
||||||
discard """
|
discard """
|
||||||
The basic approach is that captured vars need to be put on the heap and
|
The basic approach is that captured vars need to be put on the heap and
|
||||||
|
|
@ -125,7 +125,7 @@ proc newCall(a: PSym, b: PNode): PNode =
|
||||||
result.add newSymNode(a)
|
result.add newSymNode(a)
|
||||||
result.add b
|
result.add b
|
||||||
|
|
||||||
proc createStateType(iter: PSym): PType =
|
proc createClosureIterStateType*(iter: PSym): PType =
|
||||||
var n = newNodeI(nkRange, iter.info)
|
var n = newNodeI(nkRange, iter.info)
|
||||||
addSon(n, newIntNode(nkIntLit, -1))
|
addSon(n, newIntNode(nkIntLit, -1))
|
||||||
addSon(n, newIntNode(nkIntLit, 0))
|
addSon(n, newIntNode(nkIntLit, 0))
|
||||||
|
|
@ -137,7 +137,7 @@ proc createStateType(iter: PSym): PType =
|
||||||
|
|
||||||
proc createStateField(iter: PSym): PSym =
|
proc createStateField(iter: PSym): PSym =
|
||||||
result = newSym(skField, getIdent(":state"), iter, iter.info)
|
result = newSym(skField, getIdent(":state"), iter, iter.info)
|
||||||
result.typ = createStateType(iter)
|
result.typ = createClosureIterStateType(iter)
|
||||||
|
|
||||||
proc createEnvObj(owner: PSym; info: TLineInfo): PType =
|
proc createEnvObj(owner: PSym; info: TLineInfo): PType =
|
||||||
# YYY meh, just add the state field for every closure for now, it's too
|
# YYY meh, just add the state field for every closure for now, it's too
|
||||||
|
|
@ -145,7 +145,7 @@ proc createEnvObj(owner: PSym; info: TLineInfo): PType =
|
||||||
result = createObj(owner, info, final=false)
|
result = createObj(owner, info, final=false)
|
||||||
rawAddField(result, createStateField(owner))
|
rawAddField(result, createStateField(owner))
|
||||||
|
|
||||||
proc getIterResult(iter: PSym): PSym =
|
proc getClosureIterResult*(iter: PSym): PSym =
|
||||||
if resultPos < iter.ast.len:
|
if resultPos < iter.ast.len:
|
||||||
result = iter.ast.sons[resultPos].sym
|
result = iter.ast.sons[resultPos].sym
|
||||||
else:
|
else:
|
||||||
|
|
@ -397,6 +397,10 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||||
if not c.capturedVars.containsOrIncl(s.id):
|
if not c.capturedVars.containsOrIncl(s.id):
|
||||||
let obj = getHiddenParam(owner).typ.lastSon
|
let obj = getHiddenParam(owner).typ.lastSon
|
||||||
#let obj = c.getEnvTypeForOwner(s.owner).lastSon
|
#let obj = c.getEnvTypeForOwner(s.owner).lastSon
|
||||||
|
|
||||||
|
if s.name == getIdent(":state"):
|
||||||
|
obj.n[0].sym.id = -s.id
|
||||||
|
else:
|
||||||
addField(obj, s)
|
addField(obj, s)
|
||||||
# but always return because the rest of the proc is only relevant when
|
# but always return because the rest of the proc is only relevant when
|
||||||
# ow != owner:
|
# ow != owner:
|
||||||
|
|
@ -461,6 +465,7 @@ type
|
||||||
processed: IntSet
|
processed: IntSet
|
||||||
envVars: Table[int, PNode]
|
envVars: Table[int, PNode]
|
||||||
inContainer: int
|
inContainer: int
|
||||||
|
features: set[Feature]
|
||||||
|
|
||||||
proc initLiftingPass(fn: PSym): LiftingPass =
|
proc initLiftingPass(fn: PSym): LiftingPass =
|
||||||
result.processed = initIntSet()
|
result.processed = initIntSet()
|
||||||
|
|
@ -595,7 +600,7 @@ proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
|
||||||
localError(n.info, "internal error: not part of closure object type")
|
localError(n.info, "internal error: not part of closure object type")
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
proc getStateField(owner: PSym): PSym =
|
proc getStateField*(owner: PSym): PSym =
|
||||||
getHiddenParam(owner).typ.sons[0].n.sons[0].sym
|
getHiddenParam(owner).typ.sons[0].n.sons[0].sym
|
||||||
|
|
||||||
proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
|
proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
|
||||||
|
|
@ -621,7 +626,7 @@ proc transformYield(n: PNode; owner: PSym; d: DetectionPass;
|
||||||
if n.sons[0].kind != nkEmpty:
|
if n.sons[0].kind != nkEmpty:
|
||||||
var a = newNodeI(nkAsgn, n.sons[0].info)
|
var a = newNodeI(nkAsgn, n.sons[0].info)
|
||||||
var retVal = liftCapturedVars(n.sons[0], owner, d, c)
|
var retVal = liftCapturedVars(n.sons[0], owner, d, c)
|
||||||
addSon(a, newSymNode(getIterResult(owner)))
|
addSon(a, newSymNode(getClosureIterResult(owner)))
|
||||||
addSon(a, retVal)
|
addSon(a, retVal)
|
||||||
retStmt.add(a)
|
retStmt.add(a)
|
||||||
else:
|
else:
|
||||||
|
|
@ -713,7 +718,9 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
|
||||||
# echo renderTree(s.getBody, {renderIds})
|
# echo renderTree(s.getBody, {renderIds})
|
||||||
let oldInContainer = c.inContainer
|
let oldInContainer = c.inContainer
|
||||||
c.inContainer = 0
|
c.inContainer = 0
|
||||||
let body = wrapIterBody(liftCapturedVars(s.getBody, s, d, c), s)
|
var body = liftCapturedVars(s.getBody, s, d, c)
|
||||||
|
if oldIterTransf in c.features:
|
||||||
|
body = wrapIterBody(body, s)
|
||||||
if c.envvars.getOrDefault(s.id).isNil:
|
if c.envvars.getOrDefault(s.id).isNil:
|
||||||
s.ast.sons[bodyPos] = body
|
s.ast.sons[bodyPos] = body
|
||||||
else:
|
else:
|
||||||
|
|
@ -756,9 +763,9 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
|
||||||
if n[1].kind == nkClosure: result = n[1]
|
if n[1].kind == nkClosure: result = n[1]
|
||||||
else:
|
else:
|
||||||
if owner.isIterator:
|
if owner.isIterator:
|
||||||
if n.kind == nkYieldStmt:
|
if oldIterTransf in c.features and n.kind == nkYieldStmt:
|
||||||
return transformYield(n, owner, d, c)
|
return transformYield(n, owner, d, c)
|
||||||
elif n.kind == nkReturnStmt:
|
elif oldIterTransf in c.features and n.kind == nkReturnStmt:
|
||||||
return transformReturn(n, owner, d, c)
|
return transformReturn(n, owner, d, c)
|
||||||
elif nfLL in n.flags:
|
elif nfLL in n.flags:
|
||||||
# special case 'when nimVm' due to bug #3636:
|
# special case 'when nimVm' due to bug #3636:
|
||||||
|
|
@ -805,7 +812,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; tooEarly: var bool): PNode =
|
proc liftLambdas*(features: set[Feature], 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 ...
|
||||||
|
|
||||||
|
|
@ -815,6 +822,7 @@ proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode =
|
||||||
if body.kind == nkEmpty or (
|
if body.kind == nkEmpty or (
|
||||||
gCmd == cmdCompileToJS and not isCompileTime) or
|
gCmd == cmdCompileToJS and not isCompileTime) or
|
||||||
fn.skipGenericOwner.kind != skModule:
|
fn.skipGenericOwner.kind != skModule:
|
||||||
|
|
||||||
# ignore forward declaration:
|
# ignore forward declaration:
|
||||||
result = body
|
result = body
|
||||||
tooEarly = true
|
tooEarly = true
|
||||||
|
|
@ -826,10 +834,13 @@ proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode =
|
||||||
d.somethingToDo = true
|
d.somethingToDo = true
|
||||||
if d.somethingToDo:
|
if d.somethingToDo:
|
||||||
var c = initLiftingPass(fn)
|
var c = initLiftingPass(fn)
|
||||||
var newBody = liftCapturedVars(body, fn, d, c)
|
c.features = features
|
||||||
|
result = liftCapturedVars(body, fn, d, c)
|
||||||
if c.envvars.getOrDefault(fn.id) != nil:
|
if c.envvars.getOrDefault(fn.id) != nil:
|
||||||
newBody = newTree(nkStmtList, rawClosureCreation(fn, d, c), newBody)
|
result = newTree(nkStmtList, rawClosureCreation(fn, d, c), result)
|
||||||
result = wrapIterBody(newBody, fn)
|
|
||||||
|
if oldIterTransf in features:
|
||||||
|
result = wrapIterBody(result, fn)
|
||||||
else:
|
else:
|
||||||
result = body
|
result = body
|
||||||
#if fn.name.s == "get2":
|
#if fn.name.s == "get2":
|
||||||
|
|
@ -870,7 +881,9 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
|
||||||
cl = createClosure()
|
cl = createClosure()
|
||||||
while true:
|
while true:
|
||||||
let i = foo(cl)
|
let i = foo(cl)
|
||||||
nkBreakState(cl.state)
|
if cl.state < 0:
|
||||||
|
break
|
||||||
|
# nkBreakState(cl.state)
|
||||||
...
|
...
|
||||||
"""
|
"""
|
||||||
if liftingHarmful(owner): return body
|
if liftingHarmful(owner): return body
|
||||||
|
|
@ -930,5 +943,16 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
|
||||||
loopBody.sons[0] = v2
|
loopBody.sons[0] = v2
|
||||||
var bs = newNodeI(nkBreakState, body.info)
|
var bs = newNodeI(nkBreakState, body.info)
|
||||||
bs.addSon(call.sons[0])
|
bs.addSon(call.sons[0])
|
||||||
loopBody.sons[1] = bs
|
|
||||||
|
let ibs = newNode(nkIfStmt)
|
||||||
|
let elifBranch = newNode(nkElifBranch)
|
||||||
|
elifBranch.add(bs)
|
||||||
|
|
||||||
|
let br = newNode(nkBreakStmt)
|
||||||
|
br.add(emptyNode)
|
||||||
|
|
||||||
|
elifBranch.add(br)
|
||||||
|
ibs.add(elifBranch)
|
||||||
|
|
||||||
|
loopBody.sons[1] = ibs
|
||||||
loopBody.sons[2] = body[L-1]
|
loopBody.sons[2] = body[L-1]
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,8 @@ type
|
||||||
callOperator,
|
callOperator,
|
||||||
parallel,
|
parallel,
|
||||||
destructor,
|
destructor,
|
||||||
notnil
|
notnil,
|
||||||
|
oldIterTransf
|
||||||
|
|
||||||
ConfigRef* = ref object ## eventually all global configuration should be moved here
|
ConfigRef* = ref object ## eventually all global configuration should be moved here
|
||||||
linesCompiled*: int # all lines that have been compiled
|
linesCompiled*: int # all lines that have been compiled
|
||||||
|
|
|
||||||
|
|
@ -1411,11 +1411,21 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||||
put(g, tkParLe, "(ComesFrom|")
|
put(g, tkParLe, "(ComesFrom|")
|
||||||
gsub(g, n, 0)
|
gsub(g, n, 0)
|
||||||
put(g, tkParRi, ")")
|
put(g, tkParRi, ")")
|
||||||
of nkGotoState, nkState:
|
of nkGotoState:
|
||||||
var c: TContext
|
var c: TContext
|
||||||
initContext c
|
initContext c
|
||||||
putWithSpace g, tkSymbol, if n.kind == nkState: "state" else: "goto"
|
putWithSpace g, tkSymbol, "goto"
|
||||||
gsons(g, n, c)
|
gsons(g, n, c)
|
||||||
|
of nkState:
|
||||||
|
var c: TContext
|
||||||
|
initContext c
|
||||||
|
putWithSpace g, tkSymbol, "state"
|
||||||
|
gsub(g, n[0], c)
|
||||||
|
putWithSpace(g, tkColon, ":")
|
||||||
|
indentNL(g)
|
||||||
|
gsons(g, n, c, 1)
|
||||||
|
dedent(g)
|
||||||
|
|
||||||
of nkBreakState:
|
of nkBreakState:
|
||||||
put(g, tkTuple, "breakstate")
|
put(g, tkTuple, "breakstate")
|
||||||
of nkTypeClassTy:
|
of nkTypeClassTy:
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) =
|
||||||
freshGenSyms(b, result, orig, symMap)
|
freshGenSyms(b, result, orig, symMap)
|
||||||
b = semProcBody(c, b)
|
b = semProcBody(c, b)
|
||||||
b = hloBody(c, b)
|
b = hloBody(c, b)
|
||||||
n.sons[bodyPos] = transformBody(c.module, b, result)
|
n.sons[bodyPos] = transformBody(c, b, result)
|
||||||
#echo "code instantiated ", result.name.s
|
#echo "code instantiated ", result.name.s
|
||||||
excl(result.flags, sfForward)
|
excl(result.flags, sfForward)
|
||||||
dec c.inGenericInst
|
dec c.inGenericInst
|
||||||
|
|
|
||||||
|
|
@ -1236,7 +1236,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
addResult(c, s.typ.sons[0], n.info, skProc)
|
addResult(c, s.typ.sons[0], n.info, skProc)
|
||||||
addResultNode(c, n)
|
addResultNode(c, n)
|
||||||
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
|
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
|
||||||
n.sons[bodyPos] = transformBody(c.module, semBody, s)
|
n.sons[bodyPos] = transformBody(c, semBody, s)
|
||||||
popProcCon(c)
|
popProcCon(c)
|
||||||
elif efOperand notin flags:
|
elif efOperand notin flags:
|
||||||
localError(n.info, errGenericLambdaNotAllowed)
|
localError(n.info, errGenericLambdaNotAllowed)
|
||||||
|
|
@ -1277,7 +1277,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
|
||||||
addResult(c, n.typ.sons[0], n.info, skProc)
|
addResult(c, n.typ.sons[0], n.info, skProc)
|
||||||
addResultNode(c, n)
|
addResultNode(c, n)
|
||||||
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
|
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
|
||||||
n.sons[bodyPos] = transformBody(c.module, semBody, s)
|
n.sons[bodyPos] = transformBody(c, semBody, s)
|
||||||
popProcCon(c)
|
popProcCon(c)
|
||||||
popOwner(c)
|
popOwner(c)
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
|
|
@ -1590,7 +1590,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||||
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
|
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
|
||||||
# unfortunately we cannot skip this step when in 'system.compiles'
|
# unfortunately we cannot skip this step when in 'system.compiles'
|
||||||
# context as it may even be evaluated in 'system.compiles':
|
# context as it may even be evaluated in 'system.compiles':
|
||||||
n.sons[bodyPos] = transformBody(c.module, semBody, s)
|
n.sons[bodyPos] = transformBody(c, semBody, s)
|
||||||
else:
|
else:
|
||||||
if s.typ.sons[0] != nil and kind != skIterator:
|
if s.typ.sons[0] != nil and kind != skIterator:
|
||||||
addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info))
|
addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info))
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,10 @@
|
||||||
# * transforms 'defer' into a 'try finally' statement
|
# * transforms 'defer' into a 'try finally' statement
|
||||||
|
|
||||||
import
|
import
|
||||||
intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os,
|
intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os, lookups,
|
||||||
idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread,
|
idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread, semdata,
|
||||||
lambdalifting, sempass2, lowerings, lookups, destroyer, liftlocals
|
lambdalifting, sempass2, lowerings, destroyer, liftlocals, closureiters
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
PTransNode* = distinct PNode
|
PTransNode* = distinct PNode
|
||||||
|
|
@ -967,20 +968,22 @@ template liftDefer(c, root) =
|
||||||
if c.deferDetected:
|
if c.deferDetected:
|
||||||
liftDeferAux(root)
|
liftDeferAux(root)
|
||||||
|
|
||||||
proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
|
proc transformBody*(ctx: PContext, n: PNode, prc: PSym): PNode =
|
||||||
if nfTransf in n.flags or prc.kind in {skTemplate}:
|
|
||||||
result = n
|
result = n
|
||||||
else:
|
if nfTransf notin n.flags and prc.kind notin {skTemplate}:
|
||||||
var c = openTransf(module, "")
|
var c = openTransf(ctx.module, "")
|
||||||
result = liftLambdas(prc, n, c.tooEarly)
|
result = liftLambdas(ctx.features, prc, result, c.tooEarly)
|
||||||
#result = n
|
|
||||||
result = processTransf(c, result, prc)
|
result = processTransf(c, result, prc)
|
||||||
liftDefer(c, result)
|
liftDefer(c, result)
|
||||||
#result = liftLambdas(prc, result)
|
|
||||||
when useEffectSystem: trackProc(prc, result)
|
when useEffectSystem: trackProc(prc, result)
|
||||||
result = liftLocalsIfRequested(prc, result)
|
result = liftLocalsIfRequested(prc, result)
|
||||||
if c.needsDestroyPass: #and newDestructors:
|
if c.needsDestroyPass: #and newDestructors:
|
||||||
result = injectDestructorCalls(prc, result)
|
result = injectDestructorCalls(prc, result)
|
||||||
|
|
||||||
|
if prc.isIterator and oldIterTransf notin ctx.features:
|
||||||
|
result = transformClosureIterator(prc, result)
|
||||||
|
|
||||||
incl(result.flags, nfTransf)
|
incl(result.flags, nfTransf)
|
||||||
#if prc.name.s == "testbody":
|
#if prc.name.s == "testbody":
|
||||||
# echo renderTree(result)
|
# echo renderTree(result)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
errormsg: "invalid control flow: 'yield' within a constructor"
|
output: "@[1, 2, 3, 4]"
|
||||||
line: 16
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# bug #5314, bug #6626
|
# bug #5314, bug #6626
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue