commit
3cbc07ac78
14 changed files with 1790 additions and 77 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(p.config, n.info, "expr(" & $n.kind & "); unknown node kind")
|
else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind")
|
||||||
|
|
||||||
proc genNamedConstExpr(p: BProc, n: PNode): Rope =
|
proc genNamedConstExpr(p: BProc, n: PNode): Rope =
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,39 @@ proc genState(p: BProc, n: PNode) =
|
||||||
elif n0.kind == nkStrLit:
|
elif n0.kind == nkStrLit:
|
||||||
linefmt(p, cpsStmts, "$1: ;$n", n0.strVal.rope)
|
linefmt(p, cpsStmts, "$1: ;$n", n0.strVal.rope)
|
||||||
|
|
||||||
|
proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
|
||||||
|
# Called by return and break stmts.
|
||||||
|
# Deals with issues faced when jumping out of try/except/finally stmts,
|
||||||
|
|
||||||
|
var stack = newSeq[tuple[n: PNode, inExcept: bool]](0)
|
||||||
|
|
||||||
|
for i in countup(1, howManyTrys):
|
||||||
|
let tryStmt = p.nestedTryStmts.pop
|
||||||
|
if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
|
||||||
|
# Pop safe points generated by try
|
||||||
|
if not tryStmt.inExcept:
|
||||||
|
linefmt(p, cpsStmts, "#popSafePoint();$n")
|
||||||
|
|
||||||
|
# Pop this try-stmt of the list of nested trys
|
||||||
|
# so we don't infinite recurse on it in the next step.
|
||||||
|
stack.add(tryStmt)
|
||||||
|
|
||||||
|
# Find finally-stmt for this try-stmt
|
||||||
|
# and generate a copy of its sons
|
||||||
|
var finallyStmt = lastSon(tryStmt.n)
|
||||||
|
if finallyStmt.kind == nkFinally:
|
||||||
|
genStmts(p, finallyStmt.sons[0])
|
||||||
|
|
||||||
|
# push old elements again:
|
||||||
|
for i in countdown(howManyTrys-1, 0):
|
||||||
|
p.nestedTryStmts.add(stack[i])
|
||||||
|
|
||||||
|
if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
|
||||||
|
# Pop exceptions that was handled by the
|
||||||
|
# except-blocks we are in
|
||||||
|
for i in countdown(howManyExcepts-1, 0):
|
||||||
|
linefmt(p, cpsStmts, "#popCurrentException();$n")
|
||||||
|
|
||||||
proc genGotoState(p: BProc, n: PNode) =
|
proc genGotoState(p: BProc, n: PNode) =
|
||||||
# we resist the temptation to translate it into duff's device as it later
|
# we resist the temptation to translate it into duff's device as it later
|
||||||
# will be translated into computed gotos anyway for GCC at least:
|
# will be translated into computed gotos anyway for GCC at least:
|
||||||
|
|
@ -167,7 +200,11 @@ proc genGotoState(p: BProc, n: PNode) =
|
||||||
initLocExpr(p, n.sons[0], a)
|
initLocExpr(p, n.sons[0], a)
|
||||||
lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)])
|
lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)])
|
||||||
p.beforeRetNeeded = true
|
p.beforeRetNeeded = true
|
||||||
lineF(p, cpsStmts, "case -1: goto BeforeRet_;$n", [])
|
lineF(p, cpsStmts, "case -1:$n", [])
|
||||||
|
blockLeaveActions(p,
|
||||||
|
howManyTrys = p.nestedTryStmts.len,
|
||||||
|
howManyExcepts = p.inExceptBlockLen)
|
||||||
|
lineF(p, cpsStmts, " goto BeforeRet_;$n", [])
|
||||||
var statesCounter = lastOrd(n.sons[0].typ)
|
var statesCounter = lastOrd(n.sons[0].typ)
|
||||||
if n.len >= 2 and n[1].kind == nkIntLit:
|
if n.len >= 2 and n[1].kind == nkIntLit:
|
||||||
statesCounter = n[1].intVal
|
statesCounter = n[1].intVal
|
||||||
|
|
@ -177,17 +214,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}:
|
||||||
|
|
@ -328,40 +365,6 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
|
||||||
else: internalError(p.config, n.info, "genIf()")
|
else: internalError(p.config, n.info, "genIf()")
|
||||||
if sonsLen(n) > 1: fixLabel(p, lend)
|
if sonsLen(n) > 1: fixLabel(p, lend)
|
||||||
|
|
||||||
|
|
||||||
proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
|
|
||||||
# Called by return and break stmts.
|
|
||||||
# Deals with issues faced when jumping out of try/except/finally stmts,
|
|
||||||
|
|
||||||
var stack = newSeq[tuple[n: PNode, inExcept: bool]](0)
|
|
||||||
|
|
||||||
for i in countup(1, howManyTrys):
|
|
||||||
let tryStmt = p.nestedTryStmts.pop
|
|
||||||
if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
|
|
||||||
# Pop safe points generated by try
|
|
||||||
if not tryStmt.inExcept:
|
|
||||||
linefmt(p, cpsStmts, "#popSafePoint();$n")
|
|
||||||
|
|
||||||
# Pop this try-stmt of the list of nested trys
|
|
||||||
# so we don't infinite recurse on it in the next step.
|
|
||||||
stack.add(tryStmt)
|
|
||||||
|
|
||||||
# Find finally-stmt for this try-stmt
|
|
||||||
# and generate a copy of its sons
|
|
||||||
var finallyStmt = lastSon(tryStmt.n)
|
|
||||||
if finallyStmt.kind == nkFinally:
|
|
||||||
genStmts(p, finallyStmt.sons[0])
|
|
||||||
|
|
||||||
# push old elements again:
|
|
||||||
for i in countdown(howManyTrys-1, 0):
|
|
||||||
p.nestedTryStmts.add(stack[i])
|
|
||||||
|
|
||||||
if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
|
|
||||||
# Pop exceptions that was handled by the
|
|
||||||
# except-blocks we are in
|
|
||||||
for i in countdown(howManyExcepts-1, 0):
|
|
||||||
linefmt(p, cpsStmts, "#popCurrentException();$n")
|
|
||||||
|
|
||||||
proc genReturnStmt(p: BProc, t: PNode) =
|
proc genReturnStmt(p: BProc, t: PNode) =
|
||||||
if nfPreventCg in t.flags: return
|
if nfPreventCg in t.flags: return
|
||||||
p.beforeRetNeeded = true
|
p.beforeRetNeeded = true
|
||||||
|
|
|
||||||
1287
compiler/closureiters.nim
Normal file
1287
compiler/closureiters.nim
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -7,12 +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,
|
||||||
idents, renderer, types, magicsys, rodread, lowerings, tables,
|
idents, renderer, types, magicsys, rodread, lowerings, tables, modulegraphs
|
||||||
modulegraphs
|
|
||||||
|
|
||||||
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
|
||||||
|
|
@ -126,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(g: ModuleGraph; iter: PSym): PType =
|
proc createClosureIterStateType*(g: ModuleGraph; 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,8 +136,8 @@ proc createStateType(g: ModuleGraph; iter: PSym): PType =
|
||||||
rawAddSon(result, intType)
|
rawAddSon(result, intType)
|
||||||
|
|
||||||
proc createStateField(g: ModuleGraph; iter: PSym): PSym =
|
proc createStateField(g: ModuleGraph; iter: PSym): PSym =
|
||||||
result = newSym(skField, getIdent(":state"), iter, iter.info, {})
|
result = newSym(skField, getIdent(":state"), iter, iter.info)
|
||||||
result.typ = createStateType(g, iter)
|
result.typ = createClosureIterStateType(g, iter)
|
||||||
|
|
||||||
proc createEnvObj(g: ModuleGraph; owner: PSym; info: TLineInfo): PType =
|
proc createEnvObj(g: ModuleGraph; 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
|
||||||
|
|
@ -146,7 +145,7 @@ proc createEnvObj(g: ModuleGraph; owner: PSym; info: TLineInfo): PType =
|
||||||
result = createObj(g, owner, info, final=false)
|
result = createObj(g, owner, info, final=false)
|
||||||
rawAddField(result, createStateField(g, owner))
|
rawAddField(result, createStateField(g, 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:
|
||||||
|
|
@ -400,7 +399,11 @@ 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(c.graph, owner).typ.lastSon
|
let obj = getHiddenParam(c.graph, owner).typ.lastSon
|
||||||
#let obj = c.getEnvTypeForOwner(s.owner).lastSon
|
#let obj = c.getEnvTypeForOwner(s.owner).lastSon
|
||||||
addField(obj, s)
|
|
||||||
|
if s.name == getIdent(":state"):
|
||||||
|
obj.n[0].sym.id = -s.id
|
||||||
|
else:
|
||||||
|
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:
|
||||||
return
|
return
|
||||||
|
|
@ -598,7 +601,7 @@ proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
|
||||||
localError(d.graph.config, n.info, "internal error: not part of closure object type")
|
localError(d.graph.config, n.info, "internal error: not part of closure object type")
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
proc getStateField(g: ModuleGraph; owner: PSym): PSym =
|
proc getStateField*(g: ModuleGraph; owner: PSym): PSym =
|
||||||
getHiddenParam(g, owner).typ.sons[0].n.sons[0].sym
|
getHiddenParam(g, owner).typ.sons[0].n.sons[0].sym
|
||||||
|
|
||||||
proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
|
proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
|
||||||
|
|
@ -625,7 +628,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:
|
||||||
|
|
@ -718,7 +721,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(d.graph, liftCapturedVars(s.getBody, s, d, c), s)
|
var body = liftCapturedVars(s.getBody, s, d, c)
|
||||||
|
if oldIterTransf in d.graph.config.features:
|
||||||
|
body = wrapIterBody(d.graph, 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:
|
||||||
|
|
@ -761,9 +766,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 d.graph.config.features and n.kind == nkYieldStmt:
|
||||||
return transformYield(n, owner, d, c)
|
return transformYield(n, owner, d, c)
|
||||||
elif n.kind == nkReturnStmt:
|
elif oldIterTransf in d.graph.config.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:
|
||||||
|
|
@ -811,7 +816,7 @@ proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType): PNo
|
||||||
fn.typ.callConv = oldCC
|
fn.typ.callConv = oldCC
|
||||||
|
|
||||||
proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PNode =
|
proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PNode =
|
||||||
# XXX conf.cmd == 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 ...
|
||||||
|
|
||||||
# However we can do lifting for the stuff which is *only* compiletime.
|
# However we can do lifting for the stuff which is *only* compiletime.
|
||||||
|
|
@ -820,6 +825,7 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PN
|
||||||
if body.kind == nkEmpty or (
|
if body.kind == nkEmpty or (
|
||||||
g.config.cmd == cmdCompileToJS and not isCompileTime) or
|
g.config.cmd == 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
|
||||||
|
|
@ -831,10 +837,12 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PN
|
||||||
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)
|
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(g, newBody, fn)
|
|
||||||
|
if oldIterTransf in g.config.features:
|
||||||
|
result = wrapIterBody(g, result, fn)
|
||||||
else:
|
else:
|
||||||
result = body
|
result = body
|
||||||
#if fn.name.s == "get2":
|
#if fn.name.s == "get2":
|
||||||
|
|
@ -872,7 +880,8 @@ proc liftForLoop*(g: ModuleGraph; 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 (nkBreakState(cl.state)):
|
||||||
|
break
|
||||||
...
|
...
|
||||||
"""
|
"""
|
||||||
if liftingHarmful(g.config, owner): return body
|
if liftingHarmful(g.config, owner): return body
|
||||||
|
|
@ -932,5 +941,16 @@ proc liftForLoop*(g: ModuleGraph; 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]
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,8 @@ type
|
||||||
callOperator,
|
callOperator,
|
||||||
parallel,
|
parallel,
|
||||||
destructor,
|
destructor,
|
||||||
notnil
|
notnil,
|
||||||
|
oldIterTransf
|
||||||
|
|
||||||
SymbolFilesOption* = enum
|
SymbolFilesOption* = enum
|
||||||
disabledSf, enabledSf, writeOnlySf, readOnlySf, v2Sf
|
disabledSf, enabledSf, writeOnlySf, readOnlySf, v2Sf
|
||||||
|
|
|
||||||
|
|
@ -1413,11 +1413,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:
|
||||||
|
|
|
||||||
|
|
@ -1556,7 +1556,7 @@ proc semYield(c: PContext, n: PNode): PNode =
|
||||||
checkSonsLen(n, 1, c.config)
|
checkSonsLen(n, 1, c.config)
|
||||||
if c.p.owner == nil or c.p.owner.kind != skIterator:
|
if c.p.owner == nil or c.p.owner.kind != skIterator:
|
||||||
localError(c.config, n.info, errYieldNotAllowedHere)
|
localError(c.config, n.info, errYieldNotAllowedHere)
|
||||||
elif c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline:
|
elif oldIterTransf in c.features and c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline:
|
||||||
localError(c.config, n.info, errYieldNotAllowedInTryStmt)
|
localError(c.config, n.info, errYieldNotAllowedInTryStmt)
|
||||||
elif n.sons[0].kind != nkEmpty:
|
elif n.sons[0].kind != nkEmpty:
|
||||||
n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility:
|
n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility:
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,9 @@
|
||||||
# * 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, lookups,
|
||||||
idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread,
|
idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread,
|
||||||
lambdalifting, sempass2, lowerings, lookups, destroyer, liftlocals,
|
lambdalifting, sempass2, lowerings, destroyer, liftlocals, closureiters,
|
||||||
modulegraphs
|
modulegraphs
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -984,6 +984,10 @@ proc transformBody*(g: ModuleGraph; module: PSym, n: PNode, prc: PSym): PNode =
|
||||||
result = liftLocalsIfRequested(prc, result, g.config)
|
result = liftLocalsIfRequested(prc, result, g.config)
|
||||||
if c.needsDestroyPass: #and newDestructors:
|
if c.needsDestroyPass: #and newDestructors:
|
||||||
result = injectDestructorCalls(g, prc, result)
|
result = injectDestructorCalls(g, prc, result)
|
||||||
|
|
||||||
|
if prc.isIterator and oldIterTransf notin g.config.features:
|
||||||
|
result = g.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)
|
||||||
|
|
|
||||||
|
|
@ -41,3 +41,6 @@ proc reraiseException() {.compilerRtl.} =
|
||||||
proc writeStackTrace() = discard
|
proc writeStackTrace() = discard
|
||||||
|
|
||||||
proc setControlCHook(hook: proc () {.noconv.}) = discard
|
proc setControlCHook(hook: proc () {.noconv.}) = discard
|
||||||
|
|
||||||
|
proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} =
|
||||||
|
sysFatal(ReraiseError, "exception handling is not available")
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,10 @@ proc popCurrentExceptionEx(id: uint) {.compilerRtl.} =
|
||||||
quitOrDebug()
|
quitOrDebug()
|
||||||
prev.up = cur.up
|
prev.up = cur.up
|
||||||
|
|
||||||
|
proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} =
|
||||||
|
if not e.isNil:
|
||||||
|
currException = e
|
||||||
|
|
||||||
# some platforms have native support for stack traces:
|
# some platforms have native support for stack traces:
|
||||||
const
|
const
|
||||||
nativeStackTraceSupported* = (defined(macosx) or defined(linux)) and
|
nativeStackTraceSupported* = (defined(macosx) or defined(linux)) and
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,25 @@
|
||||||
discard """
|
discard """
|
||||||
errormsg: "invalid control flow: 'yield' within a constructor"
|
output: '''
|
||||||
line: 16
|
@[1, 2, 3, 4]
|
||||||
|
123
|
||||||
|
'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# bug #5314, bug #6626
|
# bug #5314, bug #6626
|
||||||
|
|
||||||
import asyncdispatch
|
import asyncdispatch
|
||||||
|
|
||||||
proc bar(): Future[int] {.async.} =
|
proc bar(i: int): Future[int] {.async.} =
|
||||||
await sleepAsync(500)
|
await sleepAsync(2)
|
||||||
result = 3
|
result = i
|
||||||
|
|
||||||
proc foo(): Future[seq[int]] {.async.} =
|
proc foo(): Future[seq[int]] {.async.} =
|
||||||
await sleepAsync(500)
|
await sleepAsync(2)
|
||||||
result = @[1, 2, await bar(), 4] # <--- The bug is here
|
result = @[1, 2, await bar(3), 4] # <--- The bug is here
|
||||||
|
|
||||||
|
proc foo2() {.async.} =
|
||||||
|
await sleepAsync(2)
|
||||||
|
echo(await bar(1), await bar(2), await bar(3))
|
||||||
|
|
||||||
echo waitFor foo()
|
echo waitFor foo()
|
||||||
|
waitFor foo2()
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ Async traceback:
|
||||||
asyncmacro\.nim\(\d+?\)\s+?a
|
asyncmacro\.nim\(\d+?\)\s+?a
|
||||||
asyncmacro\.nim\(\d+?\)\s+?a_continue
|
asyncmacro\.nim\(\d+?\)\s+?a_continue
|
||||||
## Resumes an async procedure
|
## Resumes an async procedure
|
||||||
asyncmacro\.nim\(\d+?\)\s+?aIter
|
tasync_traceback\.nim\(\d+?\)\s+?aIter
|
||||||
asyncfutures\.nim\(\d+?\)\s+?read
|
asyncfutures\.nim\(\d+?\)\s+?read
|
||||||
\]#
|
\]#
|
||||||
Exception message: b failure
|
Exception message: b failure
|
||||||
|
|
@ -110,7 +110,7 @@ Async traceback:
|
||||||
## Executes pending callbacks
|
## Executes pending callbacks
|
||||||
asyncmacro\.nim\(\d+?\)\s+?foo_continue
|
asyncmacro\.nim\(\d+?\)\s+?foo_continue
|
||||||
## Resumes an async procedure
|
## Resumes an async procedure
|
||||||
asyncmacro\.nim\(\d+?\)\s+?fooIter
|
tasync_traceback\.nim\(\d+?\)\s+?fooIter
|
||||||
asyncfutures\.nim\(\d+?\)\s+?read
|
asyncfutures\.nim\(\d+?\)\s+?read
|
||||||
\]#
|
\]#
|
||||||
Exception message: bar failure
|
Exception message: bar failure
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
discard """
|
discard """
|
||||||
file: "tasynctry2.nim"
|
file: "tasynctry2.nim"
|
||||||
errormsg: "\'yield\' cannot be used within \'try\' in a non-inlined iterator"
|
errormsg: "\'yield\' cannot be used within \'try\' in a non-inlined iterator"
|
||||||
line: 15
|
line: 17
|
||||||
"""
|
"""
|
||||||
import asyncdispatch
|
import asyncdispatch
|
||||||
|
|
||||||
|
{.experimental: "oldIterTransf".}
|
||||||
|
|
||||||
proc foo(): Future[bool] {.async.} = discard
|
proc foo(): Future[bool] {.async.} = discard
|
||||||
|
|
||||||
proc test5(): Future[int] {.async.} =
|
proc test5(): Future[int] {.async.} =
|
||||||
|
|
|
||||||
372
tests/iter/tyieldintry.nim
Normal file
372
tests/iter/tyieldintry.nim
Normal file
|
|
@ -0,0 +1,372 @@
|
||||||
|
discard """
|
||||||
|
targets: "c cpp"
|
||||||
|
output: "ok"
|
||||||
|
"""
|
||||||
|
var closureIterResult = newSeq[int]()
|
||||||
|
|
||||||
|
proc checkpoint(arg: int) =
|
||||||
|
closureIterResult.add(arg)
|
||||||
|
|
||||||
|
type
|
||||||
|
TestException = object of Exception
|
||||||
|
AnotherException = object of Exception
|
||||||
|
|
||||||
|
proc testClosureIterAux(it: iterator(): int, exceptionExpected: bool, expectedResults: varargs[int]) =
|
||||||
|
closureIterResult.setLen(0)
|
||||||
|
|
||||||
|
var exceptionCaught = false
|
||||||
|
|
||||||
|
try:
|
||||||
|
for i in it():
|
||||||
|
closureIterResult.add(i)
|
||||||
|
except TestException:
|
||||||
|
exceptionCaught = true
|
||||||
|
|
||||||
|
if closureIterResult != @expectedResults or exceptionCaught != exceptionExpected:
|
||||||
|
if closureIterResult != @expectedResults:
|
||||||
|
echo "Expected: ", @expectedResults
|
||||||
|
echo "Actual: ", closureIterResult
|
||||||
|
if exceptionCaught != exceptionExpected:
|
||||||
|
echo "Expected exception: ", exceptionExpected
|
||||||
|
echo "Got exception: ", exceptionCaught
|
||||||
|
doAssert(false)
|
||||||
|
|
||||||
|
proc test(it: iterator(): int, expectedResults: varargs[int]) =
|
||||||
|
testClosureIterAux(it, false, expectedResults)
|
||||||
|
|
||||||
|
proc testExc(it: iterator(): int, expectedResults: varargs[int]) =
|
||||||
|
testClosureIterAux(it, true, expectedResults)
|
||||||
|
|
||||||
|
proc raiseException() =
|
||||||
|
raise newException(TestException, "Test exception!")
|
||||||
|
|
||||||
|
block:
|
||||||
|
iterator it(): int {.closure.} =
|
||||||
|
var i = 5
|
||||||
|
while i != 0:
|
||||||
|
yield i
|
||||||
|
if i == 3:
|
||||||
|
yield 123
|
||||||
|
dec i
|
||||||
|
|
||||||
|
test(it, 5, 4, 3, 123, 2, 1)
|
||||||
|
|
||||||
|
block:
|
||||||
|
iterator it(): int {.closure.} =
|
||||||
|
yield 0
|
||||||
|
try:
|
||||||
|
checkpoint(1)
|
||||||
|
raiseException()
|
||||||
|
except TestException:
|
||||||
|
checkpoint(2)
|
||||||
|
yield 3
|
||||||
|
checkpoint(4)
|
||||||
|
finally:
|
||||||
|
checkpoint(5)
|
||||||
|
|
||||||
|
checkpoint(6)
|
||||||
|
|
||||||
|
test(it, 0, 1, 2, 3, 4, 5, 6)
|
||||||
|
|
||||||
|
block:
|
||||||
|
iterator it(): int {.closure.} =
|
||||||
|
yield 0
|
||||||
|
try:
|
||||||
|
yield 1
|
||||||
|
checkpoint(2)
|
||||||
|
finally:
|
||||||
|
checkpoint(3)
|
||||||
|
yield 4
|
||||||
|
checkpoint(5)
|
||||||
|
yield 6
|
||||||
|
|
||||||
|
test(it, 0, 1, 2, 3, 4, 5, 6)
|
||||||
|
|
||||||
|
block:
|
||||||
|
iterator it(): int {.closure.} =
|
||||||
|
yield 0
|
||||||
|
try:
|
||||||
|
yield 1
|
||||||
|
raiseException()
|
||||||
|
yield 2
|
||||||
|
finally:
|
||||||
|
checkpoint(3)
|
||||||
|
yield 4
|
||||||
|
checkpoint(5)
|
||||||
|
yield 6
|
||||||
|
|
||||||
|
testExc(it, 0, 1, 3, 4, 5, 6)
|
||||||
|
|
||||||
|
block:
|
||||||
|
iterator it(): int {.closure.} =
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
raiseException()
|
||||||
|
except AnotherException:
|
||||||
|
yield 123
|
||||||
|
finally:
|
||||||
|
checkpoint(3)
|
||||||
|
finally:
|
||||||
|
checkpoint(4)
|
||||||
|
|
||||||
|
testExc(it, 3, 4)
|
||||||
|
|
||||||
|
block:
|
||||||
|
iterator it(): int {.closure.} =
|
||||||
|
try:
|
||||||
|
yield 1
|
||||||
|
raiseException()
|
||||||
|
except AnotherException:
|
||||||
|
checkpoint(123)
|
||||||
|
finally:
|
||||||
|
checkpoint(2)
|
||||||
|
checkpoint(3)
|
||||||
|
|
||||||
|
testExc(it, 1, 2)
|
||||||
|
|
||||||
|
block:
|
||||||
|
iterator it(): int {.closure.} =
|
||||||
|
try:
|
||||||
|
yield 0
|
||||||
|
try:
|
||||||
|
yield 1
|
||||||
|
try:
|
||||||
|
yield 2
|
||||||
|
raiseException()
|
||||||
|
except AnotherException:
|
||||||
|
yield 123
|
||||||
|
finally:
|
||||||
|
yield 3
|
||||||
|
except AnotherException:
|
||||||
|
yield 124
|
||||||
|
finally:
|
||||||
|
yield 4
|
||||||
|
checkpoint(1234)
|
||||||
|
except:
|
||||||
|
yield 5
|
||||||
|
checkpoint(6)
|
||||||
|
finally:
|
||||||
|
checkpoint(7)
|
||||||
|
yield 8
|
||||||
|
checkpoint(9)
|
||||||
|
|
||||||
|
test(it, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
|
||||||
|
|
||||||
|
block:
|
||||||
|
iterator it(): int {.closure.} =
|
||||||
|
try:
|
||||||
|
yield 0
|
||||||
|
return 2
|
||||||
|
finally:
|
||||||
|
checkpoint(1)
|
||||||
|
checkpoint(123)
|
||||||
|
|
||||||
|
test(it, 0, 1)
|
||||||
|
|
||||||
|
block:
|
||||||
|
iterator it(): int {.closure.} =
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
yield 0
|
||||||
|
raiseException()
|
||||||
|
finally:
|
||||||
|
checkpoint(1)
|
||||||
|
except TestException:
|
||||||
|
yield 2
|
||||||
|
return
|
||||||
|
finally:
|
||||||
|
yield 3
|
||||||
|
|
||||||
|
checkpoint(123)
|
||||||
|
|
||||||
|
test(it, 0, 1, 2, 3)
|
||||||
|
|
||||||
|
block:
|
||||||
|
iterator it(): int {.closure.} =
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
yield 0
|
||||||
|
raiseException()
|
||||||
|
finally:
|
||||||
|
return # Return in finally should stop exception propagation
|
||||||
|
except AnotherException:
|
||||||
|
yield 2
|
||||||
|
return
|
||||||
|
finally:
|
||||||
|
yield 3
|
||||||
|
checkpoint(123)
|
||||||
|
|
||||||
|
test(it, 0, 3)
|
||||||
|
|
||||||
|
block: # Yield in yield
|
||||||
|
iterator it(): int {.closure.} =
|
||||||
|
template foo(): int =
|
||||||
|
yield 1
|
||||||
|
2
|
||||||
|
|
||||||
|
for i in 0 .. 2:
|
||||||
|
checkpoint(0)
|
||||||
|
yield foo()
|
||||||
|
|
||||||
|
test(it, 0, 1, 2, 0, 1, 2, 0, 1, 2)
|
||||||
|
|
||||||
|
block:
|
||||||
|
iterator it(): int {.closure.} =
|
||||||
|
let i = if true:
|
||||||
|
yield 0
|
||||||
|
1
|
||||||
|
else:
|
||||||
|
2
|
||||||
|
yield i
|
||||||
|
|
||||||
|
test(it, 0, 1)
|
||||||
|
|
||||||
|
block:
|
||||||
|
iterator it(): int {.closure.} =
|
||||||
|
var foo = 123
|
||||||
|
let i = try:
|
||||||
|
yield 0
|
||||||
|
raiseException()
|
||||||
|
1
|
||||||
|
except TestException as e:
|
||||||
|
assert(e.msg == "Test exception!")
|
||||||
|
case foo
|
||||||
|
of 1:
|
||||||
|
yield 123
|
||||||
|
2
|
||||||
|
of 123:
|
||||||
|
yield 5
|
||||||
|
6
|
||||||
|
else:
|
||||||
|
7
|
||||||
|
yield i
|
||||||
|
|
||||||
|
test(it, 0, 5, 6)
|
||||||
|
|
||||||
|
block:
|
||||||
|
iterator it(): int {.closure.} =
|
||||||
|
proc voidFoo(i1, i2, i3: int) =
|
||||||
|
checkpoint(i1)
|
||||||
|
checkpoint(i2)
|
||||||
|
checkpoint(i3)
|
||||||
|
|
||||||
|
proc foo(i1, i2, i3: int): int =
|
||||||
|
voidFoo(i1, i2, i3)
|
||||||
|
i3
|
||||||
|
|
||||||
|
proc bar(i1: int): int =
|
||||||
|
checkpoint(i1)
|
||||||
|
|
||||||
|
template tryexcept: int =
|
||||||
|
try:
|
||||||
|
yield 1
|
||||||
|
raiseException()
|
||||||
|
123
|
||||||
|
except TestException:
|
||||||
|
yield 2
|
||||||
|
checkpoint(3)
|
||||||
|
4
|
||||||
|
|
||||||
|
let e1 = true
|
||||||
|
|
||||||
|
template ifelse1: int =
|
||||||
|
if e1:
|
||||||
|
yield 10
|
||||||
|
11
|
||||||
|
else:
|
||||||
|
12
|
||||||
|
|
||||||
|
template ifelse2: int =
|
||||||
|
if ifelse1() == 12:
|
||||||
|
yield 20
|
||||||
|
21
|
||||||
|
else:
|
||||||
|
yield 22
|
||||||
|
23
|
||||||
|
|
||||||
|
let i = foo(bar(0), tryexcept, ifelse2)
|
||||||
|
discard foo(bar(0), tryexcept, ifelse2)
|
||||||
|
voidFoo(bar(0), tryexcept, ifelse2)
|
||||||
|
yield i
|
||||||
|
|
||||||
|
test(it,
|
||||||
|
|
||||||
|
# let i = foo(bar(0), tryexcept, ifelse2)
|
||||||
|
0, # bar(0)
|
||||||
|
1, 2, 3, # tryexcept
|
||||||
|
10, # ifelse1
|
||||||
|
22, # ifelse22
|
||||||
|
0, 4, 23, # foo
|
||||||
|
|
||||||
|
# discard foo(bar(0), tryexcept, ifelse2)
|
||||||
|
0, # bar(0)
|
||||||
|
1, 2, 3, # tryexcept
|
||||||
|
10, # ifelse1
|
||||||
|
22, # ifelse22
|
||||||
|
0, 4, 23, # foo
|
||||||
|
|
||||||
|
# voidFoo(bar(0), tryexcept, ifelse2)
|
||||||
|
0, # bar(0)
|
||||||
|
1, 2, 3, # tryexcept
|
||||||
|
10, # ifelse1
|
||||||
|
22, # ifelse22
|
||||||
|
0, 4, 23, # foo
|
||||||
|
|
||||||
|
23 # i
|
||||||
|
)
|
||||||
|
|
||||||
|
block:
|
||||||
|
iterator it(): int {.closure.} =
|
||||||
|
checkpoint(0)
|
||||||
|
for i in 0 .. 1:
|
||||||
|
try:
|
||||||
|
yield 1
|
||||||
|
raiseException()
|
||||||
|
except TestException as e:
|
||||||
|
doAssert(e.msg == "Test exception!")
|
||||||
|
yield 2
|
||||||
|
except AnotherException:
|
||||||
|
yield 123
|
||||||
|
except:
|
||||||
|
yield 1234
|
||||||
|
finally:
|
||||||
|
yield 3
|
||||||
|
checkpoint(4)
|
||||||
|
yield 5
|
||||||
|
|
||||||
|
test(it, 0, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5)
|
||||||
|
|
||||||
|
block:
|
||||||
|
iterator it(): int {.closure.} =
|
||||||
|
var i = 5
|
||||||
|
template foo(): bool =
|
||||||
|
yield i
|
||||||
|
true
|
||||||
|
|
||||||
|
while foo():
|
||||||
|
dec i
|
||||||
|
if i == 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
test(it, 5, 4, 3, 2, 1)
|
||||||
|
|
||||||
|
block: # Short cirquits
|
||||||
|
iterator it(): int {.closure.} =
|
||||||
|
template trueYield: bool =
|
||||||
|
yield 1
|
||||||
|
true
|
||||||
|
|
||||||
|
template falseYield: bool =
|
||||||
|
yield 0
|
||||||
|
false
|
||||||
|
|
||||||
|
if trueYield or falseYield:
|
||||||
|
discard falseYield and trueYield
|
||||||
|
|
||||||
|
if falseYield and trueYield:
|
||||||
|
checkpoint(123)
|
||||||
|
|
||||||
|
test(it, 1, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
echo "ok"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue