mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:19:48 +00:00
47abd96c5b
This is a reland of commit 6f4bed30cc
Original change's description:
> [vm] Fix CompileType::CanBeFuture()
>
> In certain cases compiler can omit a type check in 'await e' if it can
> prove that expression 'e' cannot be evaluated to Future.
>
> Previously, if actual type of 'e' is unknown, CompileType::CanBeFuture()
> tested if static type of 'e' is a subtype of Future.
>
> This is not correct, as static type could have a subtype which is also
> a subtype of Future, but static type itself is not a subtype of Future:
>
> class A {}
> class B implements A, Future<A> {}
>
> A e = confuse(B()); // 'e' is B, but has a static type A.
> await e; // A is not a subtype of Future, but 'e' is Future
> // and should be awaited.
>
TEST=language/async/await_flatten_test
Fixes https://github.com/dart-lang/sdk/issues/52585
Change-Id: I37add9021261c95dee5930e031187868e821c161
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/309481
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
250 lines
6.8 KiB
Dart
250 lines
6.8 KiB
Dart
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
// Requirements=nnbd-strong
|
|
|
|
import 'dart:async';
|
|
import 'package:expect/expect.dart';
|
|
import '../static_type_helper.dart';
|
|
|
|
// Testing the behavior of flatten (cf. SDK issue #49396).
|
|
|
|
class A {}
|
|
|
|
abstract class B<X> extends A implements Future<X> {
|
|
Future<X> get fut;
|
|
asStream() => fut.asStream();
|
|
catchError(error, {test}) => fut.catchError(error, test: test);
|
|
then<R>(onValue, {onError}) => fut.then(onValue, onError: onError);
|
|
timeout(timeLimit, {onTimeout}) =>
|
|
fut.timeout(timeLimit, onTimeout: onTimeout);
|
|
whenComplete(action) => fut.whenComplete(action);
|
|
}
|
|
|
|
class C extends B<Object?> {
|
|
final Future<Object?> fut = Future.value(CompletelyUnrelated());
|
|
}
|
|
|
|
class CompletelyUnrelated {}
|
|
|
|
class C1 extends A {}
|
|
|
|
class C2 extends B<C1> {
|
|
final Future<C1> fut = Future.value(C1());
|
|
}
|
|
|
|
void main() async {
|
|
final Object o = Future<Object?>.value();
|
|
var o2 = await o; // Remains a future.
|
|
o2.expectStaticType<Exactly<Object>>();
|
|
Expect.type<Future<Object?>>(o2);
|
|
Expect.identical(o, o2);
|
|
|
|
final FutureOr<Object> x = Future<Object?>.value();
|
|
var x2 = await x; // Remains a future.
|
|
x2.expectStaticType<Exactly<Object>>();
|
|
Expect.type<Future<Object?>>(x2);
|
|
Expect.identical(x, x2);
|
|
|
|
final FutureOr<Future<int>> y = Future<int>.value(1);
|
|
var y2 = await y; // Remains a `Future<int>`.
|
|
y2.expectStaticType<Exactly<Future<int>>>();
|
|
Expect.type<Future<int>>(y2);
|
|
Expect.identical(y, y2);
|
|
|
|
A a = C();
|
|
var a2 = await a; // Remains a `C` and a `Future<Object?>`.
|
|
a2.expectStaticType<Exactly<A>>();
|
|
Expect.type<C>(a2);
|
|
Expect.identical(a, a2);
|
|
|
|
// Type variable; called with `X == Object`.
|
|
Future<void> f1<X extends Object>(X x) async {
|
|
var x2 = await x; // Remains a `Future<Object?>`.
|
|
x2.expectStaticType<Exactly<X>>();
|
|
Expect.type<Future<Object?>>(x2);
|
|
Expect.identical(x, x2);
|
|
}
|
|
|
|
await f1<Object>(Future<Object?>.value(null));
|
|
|
|
// Type variable; called with `X == A`.
|
|
Future<void> f2<X extends A>(X x) async {
|
|
var x2 = await x; // The future is awaited.
|
|
x2.expectStaticType<Exactly<X>>();
|
|
Expect.type<C1>(x2);
|
|
Expect.notType<C2>(x2);
|
|
Expect.notIdentical(x, x2);
|
|
}
|
|
|
|
await f2<A>(C2());
|
|
|
|
// Type variable; called with `X == C2`.
|
|
Future<void> f3<X extends A>(X x) async {
|
|
var x2 = await x; // Remains a `C2` and a `Future<C1>`.
|
|
x2.expectStaticType<Exactly<X>>();
|
|
Expect.type<C2>(x2);
|
|
Expect.identical(x, x2);
|
|
}
|
|
|
|
await f3<C2>(C2());
|
|
|
|
// Type variable; value of `X` makes no difference.
|
|
Future<void> f4<X extends Future<A>>(X x) async {
|
|
var x2 = await x; // The future is awaited.
|
|
x2.expectStaticType<Exactly<A>>();
|
|
Expect.type<C1>(x2);
|
|
Expect.notType<C2>(x2);
|
|
Expect.notIdentical(x, x2);
|
|
}
|
|
|
|
await f4<Future<A>>(C2());
|
|
await f4<C2>(C2());
|
|
|
|
// Type variable; value of `X` makes no difference.
|
|
Future<void> f5<X extends FutureOr<A>>(X x) async {
|
|
var x2 = await x; // The future is awaited.
|
|
x2.expectStaticType<Exactly<A>>();
|
|
Expect.type<C1>(x2);
|
|
Expect.notType<C2>(x2);
|
|
Expect.notIdentical(x, x2);
|
|
}
|
|
|
|
await f5<A>(C2());
|
|
await f5<Future<A>>(C2());
|
|
await f5<Future<C1>>(C2());
|
|
await f5<C2>(C2());
|
|
|
|
// Promoted type variable; called with `X == Object`.
|
|
Future<void> g1<X>(X x) async {
|
|
if (x is Object) {
|
|
var x2 = await x; // Remains a `Future<Object?>`.
|
|
x2.expectStaticType<Exactly<X>>();
|
|
Expect.type<Future<Object?>>(x2);
|
|
Expect.identical(x, x2);
|
|
}
|
|
}
|
|
|
|
await g1<Object>(Future<Object?>.value(null));
|
|
|
|
// Promoted type variable; called with `X == Object?`.
|
|
Future<void> g2<X>(X x) async {
|
|
if (x is Object) {
|
|
var x2 = await x; // The future is awaited.
|
|
x2.expectStaticType<Exactly<X>>();
|
|
Expect.isNull(x2);
|
|
}
|
|
}
|
|
|
|
await g2<Object?>(Future<Object?>.value(null));
|
|
|
|
// Promoted type variable; called with `X == A`.
|
|
Future<void> g3<X>(X x) async {
|
|
if (x is A) {
|
|
var x2 = await x; // The future is awaited.
|
|
x2.expectStaticType<Exactly<X>>();
|
|
Expect.type<C1>(x2);
|
|
Expect.notType<C2>(x2);
|
|
Expect.notIdentical(x, x2);
|
|
}
|
|
}
|
|
|
|
await g3<A>(C2());
|
|
|
|
// Promoted type variable; called with `X == C2`.
|
|
Future<void> g4<X>(X x) async {
|
|
if (x is A) {
|
|
var x2 = await x; // Remains a `C2` and a `Future<C1>`.
|
|
x2.expectStaticType<Exactly<X>>();
|
|
Expect.type<C2>(x2);
|
|
Expect.identical(x, x2);
|
|
}
|
|
}
|
|
|
|
await g4<C2>(C2());
|
|
|
|
// Promoted type variable; the value of `X` does not matter.
|
|
Future<void> g5<X>(X x) async {
|
|
if (x is Future<A>) {
|
|
var x2 = await x; // The future is awaited.
|
|
x2.expectStaticType<Exactly<A>>();
|
|
Expect.type<C1>(x2);
|
|
Expect.notType<C2>(x2);
|
|
Expect.notIdentical(x, x2);
|
|
}
|
|
}
|
|
|
|
await g5<A>(C2());
|
|
await g5<C2>(C2());
|
|
await g5<Future<A>>(C2());
|
|
|
|
// Promoted type variable; the value of `X` does not matter.
|
|
Future<void> g6<X>(X x) async {
|
|
if (x is FutureOr<A>) {
|
|
var x2 = await x; // The future is awaited.
|
|
x2.expectStaticType<Exactly<A>>();
|
|
Expect.type<C1>(x2);
|
|
Expect.notType<C2>(x2);
|
|
Expect.notIdentical(x, x2);
|
|
}
|
|
}
|
|
|
|
await g6<A>(C2());
|
|
await g6<C2>(C2());
|
|
await g6<Future<A>>(C2());
|
|
await g6<Future<C1>>(C2());
|
|
|
|
// Promoted type variable; values of type variables makes no difference.
|
|
Future<void> g7<X, Y extends Future<int>>(X x) async {
|
|
if (x is Future<int> && x is Y) {
|
|
// The promoted type of `x` is `X & Y`.
|
|
var x2 = await x; // The future is awaited.
|
|
x2.expectStaticType<Exactly<int>>();
|
|
Expect.equals(1, x2);
|
|
}
|
|
}
|
|
|
|
await g7<Object?, Future<int>>(Future<int>.value(1));
|
|
|
|
// S? bounded type: called with `X == Null`.
|
|
Future<void> h1<X extends Future<A>?>(X x) async {
|
|
var x2 = await x; // Remains null.
|
|
x2.expectStaticType<Exactly<A?>>();
|
|
Expect.identical(null, x2);
|
|
}
|
|
|
|
await h1<Null>(null);
|
|
|
|
// S? bounded type: called with `X == Future<A>`.
|
|
Future<void> h2<X extends Future<A>?>(X x) async {
|
|
var x2 = await x; // The future is awaited.
|
|
x2.expectStaticType<Exactly<A?>>();
|
|
Expect.type<C1>(x2);
|
|
Expect.notType<C2>(x2);
|
|
Expect.notIdentical(x, x2);
|
|
}
|
|
|
|
await h2<Future<A>>(C2());
|
|
|
|
// S? bounded type: called with `X == Null`.
|
|
Future<void> h3<X extends FutureOr<A>?>(X x) async {
|
|
var x2 = await x; // Remains null.
|
|
x2.expectStaticType<Exactly<A?>>();
|
|
Expect.identical(null, x2);
|
|
}
|
|
|
|
await h3<Null>(null);
|
|
|
|
// S? bounded type: called with `X == FutureOr<A>`.
|
|
Future<void> h4<X extends FutureOr<A>?>(X x) async {
|
|
var x2 = await x; // The future is awaited.
|
|
x2.expectStaticType<Exactly<A?>>();
|
|
Expect.type<C1>(x2);
|
|
Expect.notType<C2>(x2);
|
|
Expect.notIdentical(x, x2);
|
|
}
|
|
|
|
await h4<FutureOr<A>>(C2());
|
|
}
|