Skip to content

Commit 873cd78

Browse files
committed
Make await a template
1 parent 6914de0 commit 873cd78

File tree

3 files changed

+46
-99
lines changed

3 files changed

+46
-99
lines changed

lib/pure/asyncmacro.nim

+31-97
Original file line numberDiff line numberDiff line change
@@ -128,51 +128,6 @@ proc processBody(node, retFutureSym: NimNode,
128128

129129
result.add newNimNode(nnkReturnStmt, node).add(newNilLit())
130130
return # Don't process the children of this return stmt
131-
of nnkCommand, nnkCall:
132-
if node[0].eqIdent("await"):
133-
case node[1].kind
134-
of nnkIdent, nnkInfix, nnkDotExpr, nnkCall, nnkCommand:
135-
# await x
136-
# await x or y
137-
# await foo(p, x)
138-
# await foo p, x
139-
var futureValue: NimNode
140-
result.createVar("future" & $node[1][0].toStrLit, node[1], futureValue,
141-
futureValue, node)
142-
else:
143-
error("Invalid node kind in 'await', got: " & $node[1].kind)
144-
elif node.len > 1 and node[1].kind == nnkCommand and
145-
node[1][0].eqIdent("await"):
146-
# foo await x
147-
var newCommand = node
148-
result.createVar("future" & $node[0].toStrLit, node[1][1], newCommand[1],
149-
newCommand, node)
150-
151-
of nnkVarSection, nnkLetSection:
152-
case node[0][^1].kind
153-
of nnkCommand:
154-
if node[0][^1][0].eqIdent("await"):
155-
# var x = await y
156-
var newVarSection = node # TODO: Should this use copyNimNode?
157-
result.createVar("future" & node[0][0].strVal, node[0][^1][1],
158-
newVarSection[0][^1], newVarSection, node)
159-
else: discard
160-
of nnkAsgn:
161-
case node[1].kind
162-
of nnkCommand:
163-
if node[1][0].eqIdent("await"):
164-
# x = await y
165-
var newAsgn = node
166-
result.createVar("future" & $node[0].toStrLit, node[1][1], newAsgn[1],
167-
newAsgn, node)
168-
else: discard
169-
of nnkDiscardStmt:
170-
# discard await x
171-
if node[0].kind == nnkCommand and
172-
node[0][0].eqIdent("await"):
173-
var newDiscard = node
174-
result.createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1],
175-
newDiscard[0], newDiscard, node)
176131
of RoutineNodes-{nnkTemplateDef}:
177132
# skip all the nested procedure definitions
178133
return
@@ -182,6 +137,8 @@ proc processBody(node, retFutureSym: NimNode,
182137
result[i] = processBody(result[i], retFutureSym, subTypeIsVoid,
183138
futureVarIdents)
184139

140+
# echo result.repr
141+
185142
proc getName(node: NimNode): string {.compileTime.} =
186143
case node.kind
187144
of nnkPostfix:
@@ -257,7 +214,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
257214
outerProcBody.add(prc.body[0])
258215

259216
# -> var retFuture = newFuture[T]()
260-
var retFutureSym = genSym(nskVar, "retFuture")
217+
var retFutureSym = ident"retFuture"
261218
var subRetType =
262219
if returnType.kind == nnkEmpty: newIdentNode("void")
263220
else: baseType
@@ -350,50 +307,6 @@ macro async*(prc: untyped): untyped =
350307
when defined(nimDumpAsync):
351308
echo repr result
352309

353-
354-
# Multisync
355-
proc emptyNoop[T](x: T): T =
356-
# The ``await``s are replaced by a call to this for simplicity.
357-
when T isnot void:
358-
return x
359-
360-
proc stripAwait(node: NimNode): NimNode =
361-
## Strips out all ``await`` commands from a procedure body, replaces them
362-
## with ``emptyNoop`` for simplicity.
363-
result = node
364-
365-
let emptyNoopSym = bindSym("emptyNoop")
366-
367-
case node.kind
368-
of nnkCommand, nnkCall:
369-
if node[0].eqIdent("await"):
370-
node[0] = emptyNoopSym
371-
elif node.len > 1 and node[1].kind == nnkCommand and node[1][0].eqIdent("await"):
372-
# foo await x
373-
node[1][0] = emptyNoopSym
374-
of nnkVarSection, nnkLetSection:
375-
case node[0][^1].kind
376-
of nnkCommand:
377-
if node[0][^1][0].eqIdent("await"):
378-
# var x = await y
379-
node[0][^1][0] = emptyNoopSym
380-
else: discard
381-
of nnkAsgn:
382-
case node[1].kind
383-
of nnkCommand:
384-
if node[1][0].eqIdent("await"):
385-
# x = await y
386-
node[1][0] = emptyNoopSym
387-
else: discard
388-
of nnkDiscardStmt:
389-
# discard await x
390-
if node[0].kind == nnkCommand and node[0][0].eqIdent("await"):
391-
node[0][0] = emptyNoopSym
392-
else: discard
393-
394-
for i in 0 ..< result.len:
395-
result[i] = stripAwait(result[i])
396-
397310
proc splitParamType(paramType: NimNode, async: bool): NimNode =
398311
result = paramType
399312
if paramType.kind == nnkInfix and paramType[0].strVal in ["|", "or"]:
@@ -426,8 +339,9 @@ proc splitProc(prc: NimNode): (NimNode, NimNode) =
426339
for i in 1 ..< result[0][3].len:
427340
# Sync proc (0) -> FormalParams (3) -> IdentDefs, the parameter (i) ->
428341
# parameter type (1).
429-
result[0][3][i][1] = splitParamType(result[0][3][i][1], async = false)
430-
result[0][6] = stripAwait(result[0][6])
342+
result[0][3][i][1] = splitParamType(result[0][3][i][1], async=false)
343+
var inMultisync = newVarStmt(ident"inMultisync", newLit(true))
344+
result[0][^1] = nnkStmtList.newTree(inMultisync, result[0][^1])
431345

432346
result[1] = prc.copyNimTree()
433347
if result[1][3][0].kind == nnkBracketExpr:
@@ -447,8 +361,28 @@ macro multisync*(prc: untyped): untyped =
447361
result = newStmtList()
448362
result.add(asyncSingleProc(asyncPrc))
449363
result.add(sync)
450-
451-
proc await*[T](x: T) =
452-
## The 'await' keyword is also defined here for technical
453-
## reasons. (Generic symbol lookup prepass.)
454-
{.error: "Await only available within .async".}
364+
# echo result.repr
365+
366+
# overload for type which is not Future[T]
367+
template await*(f: untyped): untyped =
368+
when declared(retFuture):
369+
static:
370+
assert(false, "await expects Future[T], got " & $typeof(f))
371+
elif not declared(inMultisync):
372+
static:
373+
assert(false, "await only available within {.async.}")
374+
else:
375+
f
376+
377+
# based on the yglukhov's patch to chronos: https://github.com/status-im/nim-chronos/pull/47
378+
template await*[T](f: Future[T]): auto =
379+
when declared(retFuture):
380+
when not declared(internalTmpFuture):
381+
var internalTmpFuture: FutureBase
382+
internalTmpFuture = f
383+
yield internalTmpFuture
384+
(cast[type(f)](internalTmpFuture)).read()
385+
else:
386+
static:
387+
assert(false, "await only available within {.async.}")
388+

tests/async/tasync_nofuture.nim

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
discard """
2+
errormsg: "await expects Future[T], got int"
3+
cmd: "nim c $file"
4+
file: "fatal.nim"
5+
"""
6+
import async
7+
8+
proc a {.async.} =
9+
await 0
10+
11+
waitFor a()

tests/async/tasync_traceback.nim

+4-2
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ Async traceback:
8282
asyncmacro\.nim\(\d+?\)\s+?a
8383
asyncmacro\.nim\(\d+?\)\s+?aNimAsyncContinue
8484
## Resumes an async procedure
85-
tasync_traceback\.nim\(\d+?\)\s+?aIter
85+
asyncmacro\.nim\(\d+?\)\s+?aIter
8686
asyncfutures\.nim\(\d+?\)\s+?read
8787
\]#
8888
Exception message: b failure
@@ -110,13 +110,15 @@ Async traceback:
110110
## Executes pending callbacks
111111
asyncmacro\.nim\(\d+?\)\s+?fooNimAsyncContinue
112112
## Resumes an async procedure
113-
tasync_traceback\.nim\(\d+?\)\s+?fooIter
113+
asyncmacro\.nim\(\d+?\)\s+?fooIter
114114
asyncfutures\.nim\(\d+?\)\s+?read
115115
\]#
116116
Exception message: bar failure
117117
Exception type:
118118
"""
119119

120+
# TODO: is asyncmacro good enough location for fooIter traceback/debugging? just put the callsite info for all?
121+
120122
let resLines = splitLines(result.strip)
121123
let expLines = splitLines(expected.strip)
122124

0 commit comments

Comments
 (0)