mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
Use the new spec for Future flattening for type inference
Change Fasta type inference and Kernel type checking to use the new definition for Future flattening, which is really unwrapping (peeling off one layer of Future or FutureOr). Use this for inferring types of `await` expressions and return types from `async` functions. Ensure that we are using the same notion of flattening for inference and checking. (Maybe it was a red flag that we weren't.) This fixes await_test so that it produces a runtime error rather than a compile time error - see #31541. A similar change will need to be made to the analyzer - see #31887. Change-Id: I7d936e9788969a48fdc216628eaa793389fb5e30 Reviewed-on: https://dart-review.googlesource.com/34504 Commit-Queue: Kevin Millikin <kmillikin@google.com> Reviewed-by: Kevin Millikin <kmillikin@google.com>
This commit is contained in:
parent
72c3c8ea61
commit
45b02f8216
|
@ -143,13 +143,14 @@ f(Object x) {
|
|||
}
|
||||
|
||||
test_await_flattened() async {
|
||||
// The analyzer type system over-flattens - see dartbug.com/31887
|
||||
await assertErrorsInCode('''
|
||||
import 'dart:async';
|
||||
Future<Future<int>> ffi() => null;
|
||||
f() async {
|
||||
Future<int> b = await ffi(); // Warning: int not assignable to Future<int>
|
||||
}
|
||||
''', [StaticTypeWarningCode.INVALID_ASSIGNMENT]);
|
||||
''', useCFE ? [] : [StaticTypeWarningCode.INVALID_ASSIGNMENT]);
|
||||
}
|
||||
|
||||
test_await_simple() async {
|
||||
|
|
|
@ -7057,10 +7057,18 @@ import 'dart:async';
|
|||
var v = (Future<Future<Future<int>>> f) async => await f;
|
||||
''');
|
||||
if (isStrongMode) {
|
||||
checkElementText(library, r'''
|
||||
if (isSharedFrontEnd) {
|
||||
checkElementText(library, r'''
|
||||
import 'dart:async';
|
||||
(Future<Future<Future<int>>>) → Future<Future<int>> v;
|
||||
''');
|
||||
} else {
|
||||
// The analyzer type system over-flattens - see dartbug.com/31887
|
||||
checkElementText(library, r'''
|
||||
import 'dart:async';
|
||||
(Future<Future<Future<int>>>) → Future<int> v;
|
||||
''');
|
||||
}
|
||||
} else {
|
||||
checkElementText(library, r'''
|
||||
import 'dart:async';
|
||||
|
|
|
@ -174,7 +174,7 @@ class ShadowAwaitExpression extends AwaitExpression
|
|||
}
|
||||
var inferredType =
|
||||
inferrer.inferExpression(operand, typeContext, typeNeeded);
|
||||
inferredType = inferrer.typeSchemaEnvironment.flattenFutures(inferredType);
|
||||
inferredType = inferrer.typeSchemaEnvironment.unfutureType(inferredType);
|
||||
inferrer.listener.awaitExpressionExit(this, inferredType);
|
||||
return inferredType;
|
||||
}
|
||||
|
|
|
@ -122,7 +122,7 @@ class ClosureContext {
|
|||
}
|
||||
} else if (isAsync) {
|
||||
returnContext = inferrer.wrapFutureOrType(
|
||||
inferrer.typeSchemaEnvironment.flattenFutures(returnContext));
|
||||
inferrer.typeSchemaEnvironment.unfutureType(returnContext));
|
||||
}
|
||||
return new ClosureContext._(isAsync, isGenerator, returnContext,
|
||||
needToInferReturnType, needImplicitDowncasts);
|
||||
|
@ -178,14 +178,7 @@ class ClosureContext {
|
|||
}
|
||||
var unwrappedType = type;
|
||||
if (isAsync && isReturn) {
|
||||
if (type is InterfaceType) {
|
||||
if (identical(type.classNode, inferrer.coreTypes.futureClass)) {
|
||||
unwrappedType = type.typeArguments[0];
|
||||
} else if (identical(
|
||||
type.classNode, inferrer.coreTypes.futureOrClass)) {
|
||||
unwrappedType = type.typeArguments[0];
|
||||
}
|
||||
}
|
||||
unwrappedType = inferrer.typeSchemaEnvironment.unfutureType(type);
|
||||
} else if (isYieldStar) {
|
||||
unwrappedType = inferrer.getDerivedTypeArgumentOf(
|
||||
type,
|
||||
|
@ -1426,12 +1419,8 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
|||
|
||||
DartType wrapFutureType(DartType type) {
|
||||
var typeWithoutFutureOr = type ?? const DynamicType();
|
||||
if (type is InterfaceType &&
|
||||
identical(type.classNode, coreTypes.futureOrClass)) {
|
||||
typeWithoutFutureOr = type.typeArguments[0];
|
||||
}
|
||||
return new InterfaceType(coreTypes.futureClass,
|
||||
<DartType>[typeSchemaEnvironment.flattenFutures(typeWithoutFutureOr)]);
|
||||
return new InterfaceType(
|
||||
coreTypes.futureClass, <DartType>[typeWithoutFutureOr]);
|
||||
}
|
||||
|
||||
DartType wrapType(DartType type, Class class_) {
|
||||
|
|
|
@ -84,28 +84,6 @@ class TypeSchemaEnvironment extends TypeEnvironment {
|
|||
constraint.upper = getGreatestLowerBound(constraint.upper, upper);
|
||||
}
|
||||
|
||||
/// Implements the function "flatten" defined in the spec, where T is [type]:
|
||||
///
|
||||
/// If T = Future<S> then flatten(T) = flatten(S).
|
||||
///
|
||||
/// Otherwise if T <: Future then let S be a type such that T << Future<S>
|
||||
/// and for all R, if T << Future<R> then S << R. Then flatten(T) = S.
|
||||
///
|
||||
/// In any other circumstance, flatten(T) = T.
|
||||
DartType flattenFutures(DartType type) {
|
||||
if (type is InterfaceType) {
|
||||
if (identical(type.classNode, coreTypes.futureClass)) {
|
||||
return flattenFutures(type.typeArguments[0]);
|
||||
}
|
||||
InterfaceType futureBase =
|
||||
hierarchy.getTypeAsInstanceOf(type, coreTypes.futureClass);
|
||||
if (futureBase != null) {
|
||||
return futureBase.typeArguments[0];
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/// Computes the greatest lower bound of [type1] and [type2].
|
||||
DartType getGreatestLowerBound(DartType type1, DartType type2) {
|
||||
// The greatest lower bound relation is reflexive. Note that we don't test
|
||||
|
|
47
pkg/front_end/testcases/inference/async_await.dart
Normal file
47
pkg/front_end/testcases/inference/async_await.dart
Normal file
|
@ -0,0 +1,47 @@
|
|||
// Copyright (c) 2018, 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.
|
||||
|
||||
/*@testedFeatures=inference*/
|
||||
library test;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
abstract class MyFuture implements Future<int> {}
|
||||
|
||||
void test() async {
|
||||
int x0;
|
||||
Future<int> x1;
|
||||
Future<Future<int>> x2;
|
||||
Future<FutureOr<int>> x3;
|
||||
Future<MyFuture> x4;
|
||||
FutureOr<int> x5;
|
||||
FutureOr<Future<int>> x6;
|
||||
FutureOr<FutureOr<int>> x7;
|
||||
FutureOr<MyFuture> x8;
|
||||
MyFuture x9;
|
||||
|
||||
/*@returnType=Future<int>*/ test0() async => x0;
|
||||
/*@returnType=Future<int>*/ test1() async => x1;
|
||||
/*@returnType=Future<Future<int>>*/ test2() async => x2;
|
||||
/*@returnType=Future<FutureOr<int>>*/ test3() async => x3;
|
||||
/*@returnType=Future<MyFuture>*/ test4() async => x4;
|
||||
/*@returnType=Future<int>*/ test5() async => x5;
|
||||
/*@returnType=Future<Future<int>>*/ test6() async => x6;
|
||||
/*@returnType=Future<FutureOr<int>>*/ test7() async => x7;
|
||||
/*@returnType=Future<MyFuture>*/ test8() async => x8;
|
||||
/*@returnType=Future<int>*/ test9() async => x9;
|
||||
|
||||
var /*@type=int*/ y0 = await x0;
|
||||
var /*@type=int*/ y1 = await x1;
|
||||
var /*@type=Future<int>*/ y2 = await x2;
|
||||
var /*@type=FutureOr<int>*/ y3 = await x3;
|
||||
var /*@type=MyFuture*/ y4 = await x4;
|
||||
var /*@type=int*/ y5 = await x5;
|
||||
var /*@type=Future<int>*/ y6 = await x6;
|
||||
var /*@type=FutureOr<int>*/ y7 = await x7;
|
||||
var /*@type=MyFuture*/ y8 = await x8;
|
||||
var /*@type=int*/ y9 = await x9;
|
||||
}
|
||||
|
||||
main() {}
|
|
@ -0,0 +1,53 @@
|
|||
library test;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "dart:async" as asy;
|
||||
|
||||
abstract class MyFuture extends core::Object implements asy::Future<core::int> {
|
||||
default constructor •() → void
|
||||
: super core::Object::•()
|
||||
;
|
||||
}
|
||||
static method test() → void async {
|
||||
core::int x0;
|
||||
asy::Future<core::int> x1;
|
||||
asy::Future<asy::Future<core::int>> x2;
|
||||
asy::Future<asy::FutureOr<core::int>> x3;
|
||||
asy::Future<self::MyFuture> x4;
|
||||
asy::FutureOr<core::int> x5;
|
||||
asy::FutureOr<asy::Future<core::int>> x6;
|
||||
asy::FutureOr<asy::FutureOr<core::int>> x7;
|
||||
asy::FutureOr<self::MyFuture> x8;
|
||||
self::MyFuture x9;
|
||||
function test0() → dynamic async
|
||||
return x0;
|
||||
function test1() → dynamic async
|
||||
return x1;
|
||||
function test2() → dynamic async
|
||||
return x2;
|
||||
function test3() → dynamic async
|
||||
return x3;
|
||||
function test4() → dynamic async
|
||||
return x4;
|
||||
function test5() → dynamic async
|
||||
return x5;
|
||||
function test6() → dynamic async
|
||||
return x6;
|
||||
function test7() → dynamic async
|
||||
return x7;
|
||||
function test8() → dynamic async
|
||||
return x8;
|
||||
function test9() → dynamic async
|
||||
return x9;
|
||||
dynamic y0 = await x0;
|
||||
dynamic y1 = await x1;
|
||||
dynamic y2 = await x2;
|
||||
dynamic y3 = await x3;
|
||||
dynamic y4 = await x4;
|
||||
dynamic y5 = await x5;
|
||||
dynamic y6 = await x6;
|
||||
dynamic y7 = await x7;
|
||||
dynamic y8 = await x8;
|
||||
dynamic y9 = await x9;
|
||||
}
|
||||
static method main() → dynamic {}
|
|
@ -0,0 +1,13 @@
|
|||
library test;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "dart:async" as asy;
|
||||
|
||||
abstract class MyFuture extends core::Object implements asy::Future<core::int> {
|
||||
default constructor •() → void
|
||||
;
|
||||
}
|
||||
static method test() → void
|
||||
;
|
||||
static method main() → dynamic
|
||||
;
|
|
@ -0,0 +1,54 @@
|
|||
library test;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "dart:async" as asy;
|
||||
|
||||
abstract class MyFuture extends core::Object implements asy::Future<core::int> {
|
||||
default constructor •() → void
|
||||
: super core::Object::•()
|
||||
;
|
||||
abstract forwarding-stub method timeout(core::Duration timeLimit, {generic-covariant-impl () → asy::FutureOr<core::int> onTimeout}) → asy::Future<core::int>;
|
||||
}
|
||||
static method test() → void async {
|
||||
core::int x0;
|
||||
asy::Future<core::int> x1;
|
||||
asy::Future<asy::Future<core::int>> x2;
|
||||
asy::Future<asy::FutureOr<core::int>> x3;
|
||||
asy::Future<self::MyFuture> x4;
|
||||
asy::FutureOr<core::int> x5;
|
||||
asy::FutureOr<asy::Future<core::int>> x6;
|
||||
asy::FutureOr<asy::FutureOr<core::int>> x7;
|
||||
asy::FutureOr<self::MyFuture> x8;
|
||||
self::MyFuture x9;
|
||||
function test0() → asy::Future<core::int> async
|
||||
return x0;
|
||||
function test1() → asy::Future<core::int> async
|
||||
return x1;
|
||||
function test2() → asy::Future<asy::Future<core::int>> async
|
||||
return x2;
|
||||
function test3() → asy::Future<asy::FutureOr<core::int>> async
|
||||
return x3;
|
||||
function test4() → asy::Future<self::MyFuture> async
|
||||
return x4;
|
||||
function test5() → asy::Future<core::int> async
|
||||
return x5;
|
||||
function test6() → asy::Future<asy::Future<core::int>> async
|
||||
return x6;
|
||||
function test7() → asy::Future<asy::FutureOr<core::int>> async
|
||||
return x7;
|
||||
function test8() → asy::Future<self::MyFuture> async
|
||||
return x8;
|
||||
function test9() → asy::Future<core::int> async
|
||||
return x9;
|
||||
core::int y0 = await x0;
|
||||
core::int y1 = await x1;
|
||||
asy::Future<core::int> y2 = await x2;
|
||||
asy::FutureOr<core::int> y3 = await x3;
|
||||
self::MyFuture y4 = await x4;
|
||||
core::int y5 = await x5;
|
||||
asy::Future<core::int> y6 = await x6;
|
||||
asy::FutureOr<core::int> y7 = await x7;
|
||||
self::MyFuture y8 = await x8;
|
||||
core::int y9 = await x9;
|
||||
}
|
||||
static method main() → dynamic {}
|
|
@ -7,6 +7,6 @@ static method id<T extends core::Object>(self::id::T x) → self::id::T
|
|||
return x;
|
||||
static method test() → dynamic async {
|
||||
asy::Future<core::String> f;
|
||||
core::String s = await self::id<asy::FutureOr<core::String>>(f) as{TypeError} core::String;
|
||||
core::String s = await self::id<asy::FutureOr<core::String>>(f);
|
||||
}
|
||||
static method main() → dynamic {}
|
||||
|
|
|
@ -84,17 +84,9 @@ inference/downwards_inference_annotations_type_variable_local: Fail # Issue #284
|
|||
inference/downwards_inference_on_function_of_t_using_the_t: Fail # Issue #29798
|
||||
inference/downwards_inference_on_list_literals_infer_downwards: RuntimeError
|
||||
inference/downwards_inference_yield_yield_star: TypeCheckError
|
||||
inference/future_then_2: TypeCheckError
|
||||
inference/future_then_4: TypeCheckError
|
||||
inference/future_then_5: TypeCheckError
|
||||
inference/future_then_conditional_2: TypeCheckError
|
||||
inference/future_then_conditional_4: TypeCheckError
|
||||
inference/future_then_conditional_5: TypeCheckError
|
||||
inference/future_then_explicit_future: Fail # Issue #30040
|
||||
inference/future_then_upwards: RuntimeError
|
||||
inference/future_then_upwards_2: RuntimeError
|
||||
inference/future_union_downwards_2: TypeCheckError
|
||||
inference/future_union_downwards_4: TypeCheckError
|
||||
inference/generic_functions_return_typedef: Fail # Issue #29798
|
||||
inference/generic_methods_correctly_recognize_generic_upper_bound: TypeCheckError
|
||||
inference/generic_methods_do_not_infer_invalid_override_of_generic_method: TypeCheckError
|
||||
|
|
|
@ -61,11 +61,27 @@ class TypeEnvironment extends SubtypeTester {
|
|||
return new InterfaceType(coreTypes.futureClass, <DartType>[type]);
|
||||
}
|
||||
|
||||
/// Removes any number of `Future<>` types wrapping a type.
|
||||
/// Removes a level of `Future<>` types wrapping a type.
|
||||
///
|
||||
/// This implements the function `flatten` from the spec, which unwraps a
|
||||
/// layer of Future or FutureOr from a type.
|
||||
DartType unfutureType(DartType type) {
|
||||
return type is InterfaceType && type.classNode == coreTypes.futureClass
|
||||
? unfutureType(type.typeArguments[0])
|
||||
: type;
|
||||
if (type is InterfaceType) {
|
||||
if (type.classNode == coreTypes.futureOrClass ||
|
||||
type.classNode == coreTypes.futureClass) {
|
||||
return type.typeArguments[0];
|
||||
}
|
||||
// It is a compile-time error to implement, extend, or mixin FutureOr so
|
||||
// we aren't concerned with it. If a class implements multiple
|
||||
// instantiations of Future, getTypeAsInstanceOf is responsible for
|
||||
// picking the least one in the sense required by the spec.
|
||||
InterfaceType future =
|
||||
hierarchy.getTypeAsInstanceOf(type, coreTypes.futureClass);
|
||||
if (future != null) {
|
||||
return future.typeArguments[0];
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/// Called if the computation of a static type failed due to a type error.
|
||||
|
|
|
@ -34,7 +34,7 @@ part of dart.async;
|
|||
/// means that `FutureOr<Object>` is equivalent to `Object`.
|
||||
///
|
||||
/// As a corollary, `FutureOr<Object>` is equivalent to
|
||||
/// `FutureOr<FutureOr<Object>>`, `FutureOr<Future<Object>> is equivalent to
|
||||
/// `FutureOr<FutureOr<Object>>`, `FutureOr<Future<Object>>` is equivalent to
|
||||
/// `Future<Object>`.
|
||||
abstract class FutureOr<T> {
|
||||
// Private generative constructor, so that it is not subclassable, mixable, or
|
||||
|
|
|
@ -143,7 +143,7 @@ async_or_generator_return_type_stacktrace_test/02: MissingCompileTimeError
|
|||
async_or_generator_return_type_stacktrace_test/03: MissingCompileTimeError
|
||||
async_return_types_test/tooManyTypeParameters: MissingCompileTimeError
|
||||
async_return_types_test/wrongReturnType: Crash # Maltyped input from Fasta, issue 31414
|
||||
await_test: CompileTimeError # Issue 31541
|
||||
await_test: Crash # Issue 31541
|
||||
bad_named_parameters2_test/01: MissingCompileTimeError
|
||||
bad_named_parameters_test/01: MissingCompileTimeError
|
||||
bad_named_parameters_test/02: MissingCompileTimeError
|
||||
|
|
|
@ -242,7 +242,7 @@ async_star_test/03: CompileTimeError # Issue 31402 (Invocation arguments)
|
|||
async_star_test/04: CompileTimeError # Issue 31402 (Invocation arguments)
|
||||
async_star_test/05: CompileTimeError # Issue 31402 (Invocation arguments)
|
||||
async_star_test/none: CompileTimeError # Issue 31402 (Invocation arguments)
|
||||
await_test: CompileTimeError # Issue 31541
|
||||
await_test: RuntimeError # Issue 31541
|
||||
bad_named_parameters2_test/01: MissingCompileTimeError
|
||||
bad_named_parameters_test/01: MissingCompileTimeError
|
||||
bad_named_parameters_test/02: MissingCompileTimeError
|
||||
|
@ -1068,7 +1068,7 @@ async_star_test/03: CompileTimeError # Issue 31402 (Invocation arguments)
|
|||
async_star_test/04: CompileTimeError # Issue 31402 (Invocation arguments)
|
||||
async_star_test/05: CompileTimeError # Issue 31402 (Invocation arguments)
|
||||
async_star_test/none: CompileTimeError # Issue 31402 (Invocation arguments)
|
||||
await_test: CompileTimeError # Issue 31541
|
||||
await_test: RuntimeError # Issue 31541
|
||||
bad_named_parameters2_test/01: MissingCompileTimeError
|
||||
bad_named_parameters_test/01: MissingCompileTimeError
|
||||
bad_named_parameters_test/02: MissingCompileTimeError
|
||||
|
|
Loading…
Reference in a new issue