diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index 308be7cba878..5783bde23071 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -159,12 +159,29 @@ proc verifyReturnType(typeName: string, node: NimNode = nil) = error("Expected return type of 'Future' got '$1'" % typeName, node) +proc isAsyncPrc0(n: NimNode): bool = + if n.kind == nnkBlockStmt and n[0].strVal == "asynctrack": + return true + if n.kind in RoutineNodes: + return false + for i in 0 .. n.len-1: + if isAsyncPrc0(n[i]): + return true + return false + +proc isAsyncPrc(n: NimNode): bool = + for i in 0 .. n.len-1: + if isAsyncPrc0(n[i]): + return true + return false + macro withRaises[T](f: Future[T], body: untyped): untyped = #echo repr f.kind # XXX support more cases let prcSym = case f.kind of nnkSym: if f.getImpl.kind == nnkIdentDefs and f.getImpl[^1].kind == nnkCall: + #echo repr f.getImpl[^1][0] f.getImpl[^1][0] else: nil @@ -173,7 +190,8 @@ macro withRaises[T](f: Future[T], body: untyped): untyped = else: nil #echo repr prcSym - if prcSym != nil: + #echo repr prcSym.getImpl + if prcSym != nil and isAsyncPrc(prcSym.getImpl): let raisesList = getRaisesList(prcSym) var raisesListTyp = newNimNode(nnkBracket) if raisesList.len > 0: @@ -361,8 +379,10 @@ proc asyncSingleProc(prc: NimNode): NimNode = # based on the yglukhov's patch to chronos: https://github.com/status-im/nim-chronos/pull/47 if procBody.kind != nnkEmpty: + let asynctrack = ident"asynctrack" body2.add quote do: - `outerProcBody` + block `asynctrack`: + `outerProcBody` result.body = body2 macro async*(prc: untyped): untyped = @@ -436,15 +456,15 @@ macro multisync*(prc: untyped): untyped = result.add(sync) macro toFutureEx*(prc: typed): untyped = - # XXX error instead of asserts - #echo repr getRaisesList(prc[0]) - #assert prc.kind == nnkCall + template check(cond: untyped): untyped = + if not cond: + error("async proc call expected", prc) + check prc.kind == nnkCall + check prc[0].kind == nnkSym + check isAsyncPrc(prc[0].getImpl) let procImpl = getTypeImpl(prc[0]) - #assert procImpl.kind == nnkProcTy + check procImpl.kind == nnkProcTy let retTyp = procImpl.params[0] - #assert retTyp.kind == nnkBracketExpr - #let fut = repr(retTyp[0]) - #assert fut == "FutureUntracked", fut let baseTyp = retTyp[1] let raisesList = getRaisesList(prc[0]) let exTyp = if raisesList.len == 0: diff --git a/tests/async/tasync_error_tracking.nim b/tests/async/tasync_error_tracking.nim index 395f7aa9e38b..54e5e8cf1867 100644 --- a/tests/async/tasync_error_tracking.nim +++ b/tests/async/tasync_error_tracking.nim @@ -45,7 +45,9 @@ block: discard block: - # XXX raises: [] + # we cannot tell if fcb is an async proc + # or a closure that returns a user created newFuture() + # that can raise anything type FooBar = object fcb: proc(): Future[void] {.closure, gcsafe.} @@ -76,18 +78,16 @@ block: doAssert not compiles(bad()) block: + proc bar {.async.} = + err(false) + + proc foo {.async.} = + await bar() + template good = - proc bar {.async.} = - err(false) - proc foo {.async.} = - await bar() proc main {.async, raises: [MyError].} = await foo() template bad = - proc bar {.async.} = - err(false) - proc foo {.async.} = - await bar() proc main {.async, raises: [].} = await foo() doAssert compiles(good()) @@ -103,17 +103,43 @@ block: doAssert compiles(good()) doAssert not compiles(bad()) -# XXX this should not compile; -# add Future.isInternal field? -when false: - proc foo {.async, raises: [].} = - let f = newFuture[void]() - f.complete() - await f -when false: +block: + template good = + proc foo {.async, raises: [Exception].} = + await newFuture[void]() + template bad = + proc foo {.async, raises: [].} = + await newFuture[void]() + doAssert compiles(good()) + doAssert not compiles(bad()) + +block: proc fut: Future[void] = newFuture[void]() - proc main {.async, raises: [].} = - let f = fut() - f.complete() - await f + + template good = + proc main {.async, raises: [Exception].} = + await fut() + template bad = + proc main {.async, raises: [].} = + await fut() + doAssert compiles(good()) + doAssert not compiles(bad()) + +block: + proc bar() {.async.} = + err(false) + + # XXX We could check all returns are from async procs + # and if so use the inferred proc raises + proc foo(): Future[void] = + bar() + + template good = + proc main {.async, raises: [Exception].} = + await foo() + template bad = + proc main {.async, raises: [MyError].} = + await foo() + doAssert compiles(good()) + doAssert not compiles(bad())