Skip to content

Unexpected behavior of FutureOr<void> when returning either nothing or a Future #37320

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
skoga opened this issue Jun 20, 2019 · 3 comments
Closed
Assignees

Comments

@skoga
Copy link

skoga commented Jun 20, 2019

This is possibly related to issue #32443 and #35237.

If I have the following:

void main() async {
  final initialFuture = Future<int>.delayed(Duration(seconds: 1), () { return 1; });
  final secondFuture = initialFuture.then<void>((val) {
    if (val != 1) {
      return;
    }
    return Future<void>(() {
      print('void void void!');
    });
  });
  await secondFuture;
}

The compiler/analyzer will complain that the 'return' statement is invalid, even though it's a FutureOr.

If I reorder the code to just let it fall through:

if (val != 1) {
} else {
    return Future<void>(() {
      print('void void void!');
    });
}

Then the compiler/analyzer will not complain, and things work as intended.

Alternatively, I could explicitly return null; and that will work as well.

It seems to me that, ideally, the first case 'should just work'. The reordering of the code working properly surprises me given the inability to just return.

@leafpetersen
Copy link
Member

This is currently working as specified:

It is a compile-time error if $s$ is \code{\RETURN{};},
unless $T$ is \VOID, \DYNAMIC, or \code{Null}.

We could allow FutureOr<void> in that list as well.

For this specific case, inference would also need to be changed to infer FutureOr<void> for the return type, rather than the more specific Future<void> type.

cc @lrhn @eernstg

@eernstg
Copy link
Member

eernstg commented Jun 28, 2019

The rule for an asynchronous non-generator function is based on flatten(T) where T is the return type. So the return type can be Future<void>, and FutureOr<void> is allowed to join for free. ;-)

But the type FutureOr<void> is dubious in itself, because there is no safe way to distinguish the void case from the Future<void> case (cf. #35237 (comment)).

In the case that we have here, a synchronous non-generator function, we can't use flatten because we don't want to allow return; in such a function when the return type is Future<void>.

To me, this is a hint in the direction that it isn't sufficiently justified to add FutureOr<void> to the list, such that FutureOr<void> f() { return; } is allowed.

Also note that the original example can be modified slightly to gain access to the special treatment of FutureOr<void> for asynchronous functions, which will allow it to compile with no errors:

void main() async {
  final initialFuture =
      Future<int>.delayed(Duration(seconds: 1), () { return 1; });
  final secondFuture = initialFuture.then<void>((val) async {
    if (val != 1) return;
    print('void void void!');
  });
  await secondFuture;
}

So maybe it's better to keep the rules as they are, and recommend using an async function in this kind of situation.

@a-siva
Copy link
Contributor

a-siva commented Jul 3, 2019

Working per specification, closing issue.

@a-siva a-siva closed this as completed Jul 3, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants