Merge pull request #7973 from yglukhov/async-try-ripout
Async try ripout
This commit is contained in:
commit
68378c857d
4 changed files with 30 additions and 113 deletions
|
|
@ -62,52 +62,6 @@ template createCb(retFutureSym, iteratorNameSym,
|
||||||
|
|
||||||
identName()
|
identName()
|
||||||
#{.pop.}
|
#{.pop.}
|
||||||
proc generateExceptionCheck(futSym,
|
|
||||||
tryStmt, rootReceiver, fromNode: NimNode): NimNode {.compileTime.} =
|
|
||||||
if tryStmt.kind == nnkNilLit:
|
|
||||||
result = rootReceiver
|
|
||||||
else:
|
|
||||||
var exceptionChecks: seq[tuple[cond, body: NimNode]] = @[]
|
|
||||||
let errorNode = newDotExpr(futSym, newIdentNode("error"))
|
|
||||||
for i in 1 ..< tryStmt.len:
|
|
||||||
let exceptBranch = tryStmt[i]
|
|
||||||
if exceptBranch[0].kind == nnkStmtList:
|
|
||||||
exceptionChecks.add((newIdentNode("true"), exceptBranch[0]))
|
|
||||||
else:
|
|
||||||
var exceptIdentCount = 0
|
|
||||||
var ifCond: NimNode
|
|
||||||
for i in 0 ..< exceptBranch.len:
|
|
||||||
let child = exceptBranch[i]
|
|
||||||
if child.kind == nnkIdent:
|
|
||||||
let cond = infix(errorNode, "of", child)
|
|
||||||
if exceptIdentCount == 0:
|
|
||||||
ifCond = cond
|
|
||||||
else:
|
|
||||||
ifCond = infix(ifCond, "or", cond)
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
exceptIdentCount.inc
|
|
||||||
|
|
||||||
expectKind(exceptBranch[exceptIdentCount], nnkStmtList)
|
|
||||||
exceptionChecks.add((ifCond, exceptBranch[exceptIdentCount]))
|
|
||||||
# -> -> else: raise futSym.error
|
|
||||||
exceptionChecks.add((newIdentNode("true"),
|
|
||||||
newNimNode(nnkRaiseStmt).add(errorNode)))
|
|
||||||
# Read the future if there is no error.
|
|
||||||
# -> else: futSym.read
|
|
||||||
let elseNode = newNimNode(nnkElse, fromNode)
|
|
||||||
elseNode.add newNimNode(nnkStmtList, fromNode)
|
|
||||||
elseNode[0].add rootReceiver
|
|
||||||
|
|
||||||
let ifBody = newStmtList()
|
|
||||||
ifBody.add newCall(newIdentNode("setCurrentException"), errorNode)
|
|
||||||
ifBody.add newIfStmt(exceptionChecks)
|
|
||||||
ifBody.add newCall(newIdentNode("setCurrentException"), newNilLit())
|
|
||||||
|
|
||||||
result = newIfStmt(
|
|
||||||
(newDotExpr(futSym, newIdentNode("failed")), ifBody)
|
|
||||||
)
|
|
||||||
result.add elseNode
|
|
||||||
|
|
||||||
template useVar(result: var NimNode, futureVarNode: NimNode, valueReceiver,
|
template useVar(result: var NimNode, futureVarNode: NimNode, valueReceiver,
|
||||||
rootReceiver: untyped, fromNode: NimNode) =
|
rootReceiver: untyped, fromNode: NimNode) =
|
||||||
|
|
@ -123,8 +77,7 @@ template useVar(result: var NimNode, futureVarNode: NimNode, valueReceiver,
|
||||||
result.add newNimNode(nnkYieldStmt, fromNode).add(futureVarNode)
|
result.add newNimNode(nnkYieldStmt, fromNode).add(futureVarNode)
|
||||||
# -> future<x>.read
|
# -> future<x>.read
|
||||||
valueReceiver = newDotExpr(futureVarNode, newIdentNode("read"))
|
valueReceiver = newDotExpr(futureVarNode, newIdentNode("read"))
|
||||||
result.add generateExceptionCheck(futureVarNode, tryStmt, rootReceiver,
|
result.add rootReceiver
|
||||||
fromNode)
|
|
||||||
|
|
||||||
template createVar(result: var NimNode, futSymName: string,
|
template createVar(result: var NimNode, futSymName: string,
|
||||||
asyncProc: NimNode,
|
asyncProc: NimNode,
|
||||||
|
|
@ -154,8 +107,8 @@ proc createFutureVarCompletions(futureVarIdents: seq[NimNode],
|
||||||
)
|
)
|
||||||
|
|
||||||
proc processBody(node, retFutureSym: NimNode,
|
proc processBody(node, retFutureSym: NimNode,
|
||||||
subTypeIsVoid: bool, futureVarIdents: seq[NimNode],
|
subTypeIsVoid: bool,
|
||||||
tryStmt: NimNode): NimNode {.compileTime.} =
|
futureVarIdents: seq[NimNode]): NimNode {.compileTime.} =
|
||||||
#echo(node.treeRepr)
|
#echo(node.treeRepr)
|
||||||
result = node
|
result = node
|
||||||
case node.kind
|
case node.kind
|
||||||
|
|
@ -173,7 +126,7 @@ proc processBody(node, retFutureSym: NimNode,
|
||||||
result.add newCall(newIdentNode("complete"), retFutureSym)
|
result.add newCall(newIdentNode("complete"), retFutureSym)
|
||||||
else:
|
else:
|
||||||
let x = node[0].processBody(retFutureSym, subTypeIsVoid,
|
let x = node[0].processBody(retFutureSym, subTypeIsVoid,
|
||||||
futureVarIdents, tryStmt)
|
futureVarIdents)
|
||||||
if x.kind == nnkYieldStmt: result.add x
|
if x.kind == nnkYieldStmt: result.add x
|
||||||
else:
|
else:
|
||||||
result.add newCall(newIdentNode("complete"), retFutureSym, x)
|
result.add newCall(newIdentNode("complete"), retFutureSym, x)
|
||||||
|
|
@ -224,63 +177,11 @@ proc processBody(node, retFutureSym: NimNode,
|
||||||
var newDiscard = node
|
var newDiscard = node
|
||||||
result.createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1],
|
result.createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1],
|
||||||
newDiscard[0], newDiscard, node)
|
newDiscard[0], newDiscard, node)
|
||||||
of nnkTryStmt:
|
|
||||||
# try: await x; except: ...
|
|
||||||
result = newNimNode(nnkStmtList, node)
|
|
||||||
template wrapInTry(n, tryBody: untyped) =
|
|
||||||
var temp = n
|
|
||||||
n[0] = tryBody
|
|
||||||
tryBody = temp
|
|
||||||
|
|
||||||
# Transform ``except`` body.
|
|
||||||
# TODO: Could we perform some ``await`` transformation here to get it
|
|
||||||
# working in ``except``?
|
|
||||||
tryBody[1] = processBody(n[1], retFutureSym, subTypeIsVoid,
|
|
||||||
futureVarIdents, nil)
|
|
||||||
|
|
||||||
proc processForTry(n: NimNode, i: var int,
|
|
||||||
res: NimNode): bool {.compileTime.} =
|
|
||||||
## Transforms the body of the tryStmt. Does not transform the
|
|
||||||
## body in ``except``.
|
|
||||||
## Returns true if the tryStmt node was transformed into an ifStmt.
|
|
||||||
result = false
|
|
||||||
var skipped = n.skipStmtList()
|
|
||||||
while i < skipped.len:
|
|
||||||
var processed = processBody(skipped[i], retFutureSym,
|
|
||||||
subTypeIsVoid, futureVarIdents, n)
|
|
||||||
|
|
||||||
# Check if we transformed the node into an exception check.
|
|
||||||
# This suggests skipped[i] contains ``await``.
|
|
||||||
if processed.kind != skipped[i].kind or processed.len != skipped[i].len:
|
|
||||||
processed = processed.skipUntilStmtList()
|
|
||||||
expectKind(processed, nnkStmtList)
|
|
||||||
expectKind(processed[2][1], nnkElse)
|
|
||||||
i.inc
|
|
||||||
|
|
||||||
if not processForTry(n, i, processed[2][1][0]):
|
|
||||||
# We need to wrap the nnkElse nodes back into a tryStmt.
|
|
||||||
# As they are executed if an exception does not happen
|
|
||||||
# inside the awaited future.
|
|
||||||
# The following code will wrap the nodes inside the
|
|
||||||
# original tryStmt.
|
|
||||||
wrapInTry(n, processed[2][1][0])
|
|
||||||
|
|
||||||
res.add processed
|
|
||||||
result = true
|
|
||||||
else:
|
|
||||||
res.add skipped[i]
|
|
||||||
i.inc
|
|
||||||
var i = 0
|
|
||||||
if not processForTry(node, i, result):
|
|
||||||
# If the tryStmt hasn't been transformed we can just put the body
|
|
||||||
# back into it.
|
|
||||||
wrapInTry(node, result)
|
|
||||||
return
|
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
for i in 0 ..< result.len:
|
for i in 0 ..< result.len:
|
||||||
result[i] = processBody(result[i], retFutureSym, subTypeIsVoid,
|
result[i] = processBody(result[i], retFutureSym, subTypeIsVoid,
|
||||||
futureVarIdents, nil)
|
futureVarIdents)
|
||||||
|
|
||||||
proc getName(node: NimNode): string {.compileTime.} =
|
proc getName(node: NimNode): string {.compileTime.} =
|
||||||
case node.kind
|
case node.kind
|
||||||
|
|
@ -362,7 +263,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
|
||||||
# -> complete(retFuture, result)
|
# -> complete(retFuture, result)
|
||||||
var iteratorNameSym = genSym(nskIterator, $prcName & "Iter")
|
var iteratorNameSym = genSym(nskIterator, $prcName & "Iter")
|
||||||
var procBody = prc.body.processBody(retFutureSym, subtypeIsVoid,
|
var procBody = prc.body.processBody(retFutureSym, subtypeIsVoid,
|
||||||
futureVarIdents, nil)
|
futureVarIdents)
|
||||||
# don't do anything with forward bodies (empty)
|
# don't do anything with forward bodies (empty)
|
||||||
if procBody.kind != nnkEmpty:
|
if procBody.kind != nnkEmpty:
|
||||||
procBody.add(createFutureVarCompletions(futureVarIdents, nil))
|
procBody.add(createFutureVarCompletions(futureVarIdents, nil))
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ discard """
|
||||||
disabled: "windows"
|
disabled: "windows"
|
||||||
output: "Matched"
|
output: "Matched"
|
||||||
"""
|
"""
|
||||||
import asyncdispatch
|
import asyncdispatch, strutils
|
||||||
|
|
||||||
# Tests to ensure our exception trace backs are friendly.
|
# Tests to ensure our exception trace backs are friendly.
|
||||||
|
|
||||||
|
|
@ -117,10 +117,26 @@ Exception message: bar failure
|
||||||
Exception type:
|
Exception type:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if result.match(re(expected)):
|
let resLines = splitLines(result.strip)
|
||||||
echo("Matched")
|
let expLines = splitLines(expected.strip)
|
||||||
else:
|
|
||||||
echo("Not matched!")
|
if resLines.len != expLines.len:
|
||||||
|
echo("Not matched! Wrong number of lines!")
|
||||||
echo()
|
echo()
|
||||||
echo(result)
|
echo(result)
|
||||||
quit(QuitFailure)
|
quit(QuitFailure)
|
||||||
|
|
||||||
|
var ok = true
|
||||||
|
for i in 0 ..< resLines.len:
|
||||||
|
if not resLines[i].match(re(expLines[i])):
|
||||||
|
echo "Not matched! Line ", i + 1
|
||||||
|
echo "Expected:"
|
||||||
|
echo expLines[i]
|
||||||
|
echo "Actual:"
|
||||||
|
echo resLines[i]
|
||||||
|
ok = false
|
||||||
|
|
||||||
|
if ok:
|
||||||
|
echo("Matched")
|
||||||
|
else:
|
||||||
|
quit(QuitFailure)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ Multiple except branches
|
||||||
Multiple except branches 2
|
Multiple except branches 2
|
||||||
'''
|
'''
|
||||||
"""
|
"""
|
||||||
import asyncdispatch
|
import asyncdispatch, strutils
|
||||||
|
|
||||||
# Here we are testing the ability to catch exceptions.
|
# Here we are testing the ability to catch exceptions.
|
||||||
|
|
||||||
|
|
@ -22,7 +22,7 @@ proc catch() {.async.} =
|
||||||
try:
|
try:
|
||||||
await foobar()
|
await foobar()
|
||||||
except:
|
except:
|
||||||
echo("Generic except: ", getCurrentExceptionMsg())
|
echo("Generic except: ", getCurrentExceptionMsg().splitLines[0])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await foobar()
|
await foobar()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
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: 17
|
line: 14
|
||||||
"""
|
"""
|
||||||
import asyncdispatch
|
import asyncdispatch
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue