asyncmacro: code cleanups (#392)

* prefer `let`
* prefer expressions
* renames
This commit is contained in:
Jacek Sieka 2023-05-23 19:45:12 +02:00 committed by GitHub
parent 4c07da6abb
commit 2fa6df0880
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 56 additions and 55 deletions

View File

@ -11,12 +11,13 @@ import std/[macros]
proc skipUntilStmtList(node: NimNode): NimNode {.compileTime.} = proc skipUntilStmtList(node: NimNode): NimNode {.compileTime.} =
# Skips a nest of StmtList's. # Skips a nest of StmtList's.
result = node
if node[0].kind == nnkStmtList: if node[0].kind == nnkStmtList:
result = skipUntilStmtList(node[0]) skipUntilStmtList(node[0])
else:
node
proc processBody(node, retFutureSym: NimNode, proc processBody(node, retFutureSym: NimNode,
subTypeIsVoid: bool): NimNode {.compileTime.} = baseTypeIsVoid: bool): NimNode {.compileTime.} =
#echo(node.treeRepr) #echo(node.treeRepr)
result = node result = node
case node.kind case node.kind
@ -25,13 +26,13 @@ proc processBody(node, retFutureSym: NimNode,
# As I've painfully found out, the order here really DOES matter. # As I've painfully found out, the order here really DOES matter.
if node[0].kind == nnkEmpty: if node[0].kind == nnkEmpty:
if not subTypeIsVoid: if not baseTypeIsVoid:
result.add newCall(newIdentNode("complete"), retFutureSym, result.add newCall(newIdentNode("complete"), retFutureSym,
newIdentNode("result")) newIdentNode("result"))
else: else:
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, baseTypeIsVoid)
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)
@ -47,7 +48,7 @@ proc processBody(node, retFutureSym: NimNode,
# We must not transform nested procedures of any form, otherwise # We must not transform nested procedures of any form, otherwise
# `retFutureSym` will be used for all nested procedures as their own # `retFutureSym` will be used for all nested procedures as their own
# `retFuture`. # `retFuture`.
result[i] = processBody(result[i], retFutureSym, subTypeIsVoid) result[i] = processBody(result[i], retFutureSym, baseTypeIsVoid)
proc getName(node: NimNode): string {.compileTime.} = proc getName(node: NimNode): string {.compileTime.} =
case node.kind case node.kind
@ -62,11 +63,8 @@ proc getName(node: NimNode): string {.compileTime.} =
else: else:
error("Unknown name.") error("Unknown name.")
proc isInvalidReturnType(typeName: string): bool =
return typeName notin ["Future"] #, "FutureStream"]
proc verifyReturnType(typeName: string) {.compileTime.} = proc verifyReturnType(typeName: string) {.compileTime.} =
if typeName.isInvalidReturnType: if typeName != "Future":
error("Expected return type of 'Future' got '" & typeName & "'") error("Expected return type of 'Future' got '" & typeName & "'")
macro unsupported(s: static[string]): untyped = macro unsupported(s: static[string]): untyped =
@ -112,7 +110,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
else: else:
raiseAssert("Unhandled async return type: " & $prc.kind) raiseAssert("Unhandled async return type: " & $prc.kind)
let subtypeIsVoid = baseType.eqIdent("void") let baseTypeIsVoid = baseType.eqIdent("void")
if prc.kind in {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo}: if prc.kind in {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo}:
let let
@ -123,57 +121,64 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
if prc.body.len > 0 and prc.body[0].kind == nnkCommentStmt: if prc.body.len > 0 and prc.body[0].kind == nnkCommentStmt:
outerProcBody.add(prc.body[0]) outerProcBody.add(prc.body[0])
# -> iterator nameIter(chronosInternalRetFuture: Future[T]): FutureBase {.closure.} =
# -> {.push warning[resultshadowed]: off.}
# -> var result: T
# -> {.pop.}
# -> <proc_body>
# -> complete(chronosInternalRetFuture, result)
let let
internalFutureSym = ident "chronosInternalRetFuture" internalFutureSym = ident "chronosInternalRetFuture"
iteratorNameSym = genSym(nskIterator, $prcName) procBody = prc.body.processBody(internalFutureSym, baseTypeIsVoid)
var
procBody = prc.body.processBody(internalFutureSym, subtypeIsVoid)
# don't do anything with forward bodies (empty) # don't do anything with forward bodies (empty)
if procBody.kind != nnkEmpty: if procBody.kind != nnkEmpty:
if subtypeIsVoid: # fix #13899, `defer` should not escape its original scope
let procBodyBlck =
newStmtList(newTree(nnkBlockStmt, newEmptyNode(), procBody))
# Avoid too much quote do to not lose original line numbers
let closureBody = if baseTypeIsVoid:
let resultTemplate = quote do: let resultTemplate = quote do:
template result: auto {.used.} = template result: auto {.used.} =
{.fatal: "You should not reference the `result` variable inside" & {.fatal: "You should not reference the `result` variable inside" &
" a void async proc".} " a void async proc".}
procBody = newStmtList(resultTemplate, procBody)
# fix #13899, `defer` should not escape its original scope
procBody = newStmtList(newTree(nnkBlockStmt, newEmptyNode(), procBody))
if not subtypeIsVoid:
procBody.insert(0, newNimNode(nnkPragma).add(newIdentNode("push"),
newNimNode(nnkExprColonExpr).add(newNimNode(nnkBracketExpr).add(
newIdentNode("warning"), newIdentNode("resultshadowed")),
newIdentNode("off")))) # -> {.push warning[resultshadowed]: off.}
procBody.insert(1, newNimNode(nnkVarSection, prc.body).add(
newIdentDefs(newIdentNode("result"), baseType))) # -> var result: T
procBody.insert(2, newNimNode(nnkPragma).add(
newIdentNode("pop"))) # -> {.pop.})
procBody.add(
newCall(newIdentNode("complete"),
internalFutureSym, newIdentNode("result"))) # -> complete(chronosInternalRetFuture, result)
else:
# -> complete(chronosInternalRetFuture) # -> complete(chronosInternalRetFuture)
procBody.add(newCall(newIdentNode("complete"), internalFutureSym)) let complete =
newCall(newIdentNode("complete"), internalFutureSym)
newStmtList(resultTemplate, procBodyBlck, complete)
else:
# -> iterator nameIter(chronosInternalRetFuture: Future[T]): FutureBase {.closure.} =
# -> {.push warning[resultshadowed]: off.}
# -> var result: T
# -> {.pop.}
# -> <proc_body>
# -> complete(chronosInternalRetFuture, result)
newStmtList(
# -> {.push warning[resultshadowed]: off.}
newNimNode(nnkPragma).add(newIdentNode("push"),
newNimNode(nnkExprColonExpr).add(newNimNode(nnkBracketExpr).add(
newIdentNode("warning"), newIdentNode("resultshadowed")),
newIdentNode("off"))),
# -> var result: T
newNimNode(nnkVarSection, prc.body).add(
newIdentDefs(newIdentNode("result"), baseType)),
# -> {.pop.})
newNimNode(nnkPragma).add(
newIdentNode("pop")),
procBodyBlck,
# -> complete(chronosInternalRetFuture, result)
newCall(newIdentNode("complete"),
internalFutureSym, newIdentNode("result")))
let let
internalFutureType = internalFutureType =
if subtypeIsVoid: if baseTypeIsVoid:
newNimNode(nnkBracketExpr, prc).add(newIdentNode("Future")).add(newIdentNode("void")) newNimNode(nnkBracketExpr, prc).add(newIdentNode("Future")).add(newIdentNode("void"))
else: returnType else: returnType
internalFutureParameter = nnkIdentDefs.newTree(internalFutureSym, internalFutureType, newEmptyNode()) internalFutureParameter = nnkIdentDefs.newTree(internalFutureSym, internalFutureType, newEmptyNode())
iteratorNameSym = genSym(nskIterator, $prcName)
closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase"), internalFutureParameter], closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase"), internalFutureParameter],
procBody, nnkIteratorDef) closureBody, nnkIteratorDef)
closureIterator.pragma = newNimNode(nnkPragma, lineInfoFrom=prc.body) closureIterator.pragma = newNimNode(nnkPragma, lineInfoFrom=prc.body)
closureIterator.addPragma(newIdentNode("closure")) closureIterator.addPragma(newIdentNode("closure"))
@ -211,21 +216,17 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
closureIterator.addPragma(newIdentNode("gcsafe")) closureIterator.addPragma(newIdentNode("gcsafe"))
outerProcBody.add(closureIterator) outerProcBody.add(closureIterator)
# -> var resultFuture = newFuture[T]() # -> let resultFuture = newFuture[T]()
# declared at the end to be sure that the closure # declared at the end to be sure that the closure
# doesn't reference it, avoid cyclic ref (#203) # doesn't reference it, avoid cyclic ref (#203)
var retFutureSym = ident "resultFuture" let
var subRetType = retFutureSym = ident "resultFuture"
if returnType.kind == nnkEmpty:
newIdentNode("void")
else:
baseType
# Do not change this code to `quote do` version because `instantiationInfo` # Do not change this code to `quote do` version because `instantiationInfo`
# will be broken for `newFuture()` call. # will be broken for `newFuture()` call.
outerProcBody.add( outerProcBody.add(
newVarStmt( newLetStmt(
retFutureSym, retFutureSym,
newCall(newTree(nnkBracketExpr, ident "newFuture", subRetType), newCall(newTree(nnkBracketExpr, ident "newFuture", baseType),
newLit(prcName)) newLit(prcName))
) )
) )
@ -262,7 +263,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
raises raises
)) ))
if subtypeIsVoid: if baseTypeIsVoid:
# Add discardable pragma. # Add discardable pragma.
if returnType.kind == nnkEmpty: if returnType.kind == nnkEmpty:
# Add Future[void] # Add Future[void]