Skip to content

Specification: Incorrect type check rule for async return. #929

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
eernstg opened this issue Apr 16, 2020 · 5 comments
Closed

Specification: Incorrect type check rule for async return. #929

eernstg opened this issue Apr 16, 2020 · 5 comments
Labels
bug There is a mistake in the language specification or in an active document specification

Comments

@eernstg
Copy link
Member

eernstg commented Apr 16, 2020

The rules in the language specification about return statements in an async function include the following:

It is a compile-time error if $s$ is \code{\RETURN{} $e$;},
\flatten{S} is not \VOID,
and \code{Future<\flatten{S}>} is not assignable to $T$.

The context for this rule is that we are considering return e; where the static type of e is $S$ (written as S when already in math mode), in an async function $f$ with declared return type $T$. A typical example would be the following:

Future<num> f() async {
  return Future<int>.value(42);
}

This is allowed, because $S$ as well as \code{Future<\flatten{S}>} is Future<int>, and $T$ is Future<num>. Similarly, return 42 is allowed because \code{Future<\flatten{S}>} is again Future<int>.

However, the following should be allowed, but is currently specified to be a compile-time error:

Future<Future<int>> g() async {
  return Future<int>.value(42);
}

Here, \code{Future<\flatten{S}>} is Future<int> and $T$ is Future<Future<int>>, and the former is not assignable to the latter.

So we must change this rule.

Note that the vm and dart2js already run the following code with no issues:

import 'dart:async';

Future<Future<int>> g() async {
  return Future<int>.value(42);
}

void main() async {
  print(await await g()); // Prints '42'.
}

The analyzer currently rejects this program, but it is a non-breaking change to allow it. Also, the analyzer reports the following:

Analyzing n026.dart...
  error • A value of type 'int' can't be returned from function 'g' because it has a return type of 'Future<int>'. • n026.dart:4:10 • return_of_invalid_type
1 error found.

which is wrong with respect to the type of the returned expression as well as the declared return type, so there would be a need to take a look at the relevant code for this check in any case.

@eernstg eernstg added bug There is a mistake in the language specification or in an active document specification labels Apr 16, 2020
@ili
Copy link

ili commented Apr 17, 2020

5 cents, if you allow: it would be better to deny using async without await, because this causes missunderstood about how does it works.

@eernstg
Copy link
Member Author

eernstg commented Apr 17, 2020

Hello @ili! There's nothing that prevents writing an async function that does not contain any asynchrony:

Future<int> foo() async => 1;

So what would it mean to deny using async without await in that situation?

[Edit] I think I know what you mean now: When an await is needed in order to perform a return in an async function, it must be written explicitly. That would indeed be simpler, but it is also seriously breaking. You could say, conversely, that if we hadn't had these implicit await operations, it would be a very typical convenience feature that we might add to the language. ;-)

In this particular case the main issue is that the specification needs to be fixed for correctness. It does connect to #914 as well, because it prepares the spec for not causing a breaking change by accident when we change the assignability rules along with the introduction of null-safety.

@leafpetersen
Copy link
Member

So we must change this rule.

I'm not sure about must - there's nothing wrong with the rule. I'm happy to consider changing it though. Is this just a sub-issue for your revisit of the return rules?

@eernstg
Copy link
Member Author

eernstg commented Apr 18, 2020

I don't understand why you do not think that the following is a bug in the specification:

The following should be allowed, but is currently specified to be a compile-time error:

Future<Future<int>> g() async {
  return Future<int>.value(42);
}

Here, \code{Future<\flatten{S}>} is Future<int> and $T$ is Future<Future<int>>, > and the former is not assignable to the latter.

As I see it, it is obviously a spec bug when it is a compile-time error to return an expression of type U in an async function with return type Future<U>.

@eernstg
Copy link
Member Author

eernstg commented May 28, 2020

Closing: #941 and #948 landed corrections for this in the null-safety specification.

@eernstg eernstg closed this as completed May 28, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug There is a mistake in the language specification or in an active document specification
Projects
None yet
Development

No branches or pull requests

3 participants