-
Notifications
You must be signed in to change notification settings - Fork 214
Bug in *flatten* function. #2310
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
Comments
Trying to find a closed function definition which provides the nearest possible supertype of the "union of the flatten of each part of the union type" is an approach. It still has weird behavior for That's because Consider a different approach, where the await is based on the static type. Awaiting an expression
That is, on union types, treat each part individually (which can obviously be optimized to only ever have two branches, one where you await the value, which must be a future, and one where you wrap the value in a future and await that future, because that's the only two possible final outcomes.) We treat That would mean a flatten function defined as:
That's actually the current flatten function! Also, code which has enabled the It would change current behavior in a number of potential cases, but those all involve |
The code example does demonstrate that the behavior of the implementation differs from the specified behavior. Here's the spec language (from the null safety update of the spec) as well as the bindings of symbols in the text (the "actual values"):
Evaluation of an await expression If the run-time type of Next, the stream associated with the innermost enclosing asynchronous for To me, this looks like the implemented semantics of But it looks like a case that we have looked at before, so I'm surprised that there is an issue with this today, I thought it had been fixed a while back. The
In this situation, where the currently implemented behavior is unsound, it seems unlikely that we'd want to change the specification to simply specify what the implementations actually do. We seem to agree on this:
However, I don't agree that the spec is unsound, so one obvious approach could be to keep the spec unchanged, and change the implementations accordingly. |
I had totally forgotten that we have introduced such type checks in Apparently the implementation teams didn't know about them either, which might have reminded me when I checked the code. It's an extra type-check for |
I was a bit worried about the performance implications: Is it possible that the specified semantics is significantly more costly than the currently implemented behavior, because the specified behavior will allocate and await a fresh future? Does it matter? — |
I think we can optimize away the extra future allocation. We just need to schedule the |
Cool! It sounds like we can make the change to the implemented behavior, and it shouldn't hurt. |
I'd rephrase the:
It's bad style to say that we use a specific constructor. In this case the So, I'd say:
(Preferably, I'd actually rewrite completely to just say:
|
I think we've addressed this at the spec level now with #2333. The implementation change is handled in dart-lang/sdk#49396. |
Uh oh!
There was an error while loading. Please reload this page.
EDIT: Not a bug in the flatten function, but in the implementations of
await
.The flatten function in the language specification is intended to match the behavior of
await
at the type level.That's why flatten(
Future<int>
) isint
.I believe there is a bug in the specification:
The issue is the third item:
This fails to match
await
for, e.g., some values of the typeFutureOr<Future<Object>>
.The static type of
r
is flatten(FutureOr<Future<Object>>
), which isFuture<Object>
.The value of
await f
is the result of awaitingFuture<Object>.value(o)
, which iso
with runtime typeObject
.The assignment isn't sound, so the spec for flatten fails to capture the runtime behavior of
await
.A quick check in Dartpad shows that our behavior is indeed unsound. As usual, any such failure to be sound can probably be leveraged to convince the compiler of any other unsound thing, through a series of valid deductions starting from the false assumption. The VM gives an error on accessing
then
, and crashes when precompiled.Addition: The last item is actually also wrong.
fails for the type
Object
containing anyFuture<X?>.value(null)
.It used to be that any proper supertype of Future/FutureOr types was a top type. Because of that, saying that
await topTypeValue
had the same type as the expression was safe.With null safety,
Object
(and the equivalentFutureOr<Object>
, etc) is a non-top super type ofFuture
.We need to make it
(because any non-
Never
class can have a subclass implementingFuture<Object?>
, and therefore can be awaited to the valuenull
. Well, not function types, but I don't know if the exception is worth making. Not even sure theNever
exception is worth it.)Solution.
The spec and implementation of flatten should be changed to be sound.
The underlying rule is that the flatten of a union type should be the union of the flatten of the individual types of the union.
(Because
await
on a value of a union type is anawait
of a value of one of those types.)Flattening a non-union type amounts to either reducing an interface type implementing
Future<S>
toS
, or keeping a non-Future
type as-is.The problem is that we don't have a representation of general union types, so we can't just define
It's not given that there is a Dart type representing that union. (I hope there always is one, but I haven't found a closed formula for finding it just yet.)
So, TODO: Find formula.
The text was updated successfully, but these errors were encountered: