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:
Paul Berry 2018-01-15 07:09:36 +00:00 committed by commit-bot@chromium.org
parent 72c3c8ea61
commit 45b02f8216
15 changed files with 208 additions and 57 deletions

View file

@ -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 {

View file

@ -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';

View file

@ -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;
}

View file

@ -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_) {

View file

@ -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

View 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() {}

View file

@ -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 {}

View file

@ -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
;

View file

@ -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 {}

View file

@ -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 {}

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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