status-im / nim-chronos

Chronos - An efficient library for asynchronous programming
https://status-im.github.io/nim-chronos/docs/chronos
Apache License 2.0
353 stars 51 forks source link

Support implicit returns #349

Closed arnetheduck closed 1 year ago

arnetheduck commented 1 year ago

Based on https://github.com/nim-lang/Nim/pull/20933

Bugs that we might hit:

arnetheduck commented 1 year ago

Generated code example:

proc testMacro2(): Future[seq[Opt[Result[OpenObject, cstring]]]] {.
    stackTrace: off, gcsafe, raises: [].} =
  iterator testMacro2_536873665(chronosInternalRetFuture: Future[
      seq[Opt[Result[OpenObject, cstring]]]]): FutureBase {.closure, gcsafe,
      raises: [CatchableError].} =
    {.push, warning[ResultShadowed]: off.}
    var result: seq[Opt[Result[OpenObject, cstring]]]
    {.pop.}
    template chronosAssignImplicitResult(
        x`gensym235: seq[Opt[Result[OpenObject, cstring]]]) {.used.} =
      complete(chronosInternalRetFuture, x`gensym235)

    template chronosAssignImplicitResult(x`gensym235: untyped) {.used.} =
      x`gensym235
      complete(chronosInternalRetFuture, result)

    chronosAssignImplicitResult(block:
      complete(chronosInternalRetFuture, result)
      return nil)

  var resultFuture = newFuture[seq[Opt[Result[OpenObject, cstring]]]](
      "testMacro2")
  resultFuture.closure = testMacro2_536873665
  futureContinue(resultFuture)
  return resultFuture

Note in particular how in the case of an expression, the fake result variable isn't used at all - instead, we complete the future directly with the expression, avoiding an assignment.

arnetheduck commented 1 year ago

moving to draft due to bugs and more fixes needed

arnetheduck commented 1 year ago

when-based version that works around Nim bug:

proc asyncImplicitReturn(n: int): Future[int] {.stackTrace: off, gcsafe,
    raises: [].} =
  iterator asyncImplicitReturn_754974800(chronosInternalRetFuture: Future[int]): FutureBase {.
      closure, gcsafe, raises: [CatchableError].} =
    {.push, warning[resultshadowed]: off.}
    var result: int
    {.pop.}
    when typeof do:
      block:
        if n < 10:
          n * 5
        else:
          await asyncRetValue(n) is
        void:
      block:
        if n < 10:
          n * 5
        else:
          await asyncRetValue(n)
      complete(chronosInternalRetFuture, result)
    else:
      complete(chronosInternalRetFuture):
        block:
          if n < 10:
            n * 5
          else:
            await asyncRetValue(n)

  let resultFuture = newFuture[int]("asyncImplicitReturn")
  resultFuture.closure = asyncImplicitReturn_754974800
  futureContinue(resultFuture)
  return resultFuture