mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 12:58:05 +00:00
Issue 50672. Update flatten()
Bug: https://github.com/dart-lang/sdk/issues/50672 Change-Id: I2747b9c071aa0074f2b26cea33b8d0f11ac61e33 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/274734 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
parent
fd9e7cbbdb
commit
c90821350e
|
@ -33,6 +33,7 @@ import 'package:analyzer/src/dart/element/type_schema.dart';
|
|||
import 'package:analyzer/src/dart/element/type_schema_elimination.dart';
|
||||
import 'package:analyzer/src/dart/element/well_bounded.dart';
|
||||
import 'package:analyzer/src/utilities/extensions/collection.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
/// Fresh type parameters created to unify two lists of type parameters.
|
||||
class RelatedTypeParameters {
|
||||
|
@ -237,36 +238,53 @@ class TypeSystemImpl implements TypeSystem {
|
|||
}
|
||||
|
||||
@override
|
||||
DartType flatten(DartType type) {
|
||||
if (identical(type, UnknownInferredType.instance)) {
|
||||
return type;
|
||||
DartType flatten(DartType T) {
|
||||
if (identical(T, UnknownInferredType.instance)) {
|
||||
return T;
|
||||
}
|
||||
|
||||
// if T is S? then flatten(T) = flatten(S)?
|
||||
// if T is S* then flatten(T) = flatten(S)*
|
||||
NullabilitySuffix nullabilitySuffix = type.nullabilitySuffix;
|
||||
final nullabilitySuffix = T.nullabilitySuffix;
|
||||
if (nullabilitySuffix != NullabilitySuffix.none) {
|
||||
var S = (type as TypeImpl).withNullability(NullabilitySuffix.none);
|
||||
final S = (T as TypeImpl).withNullability(NullabilitySuffix.none);
|
||||
return (flatten(S) as TypeImpl).withNullability(nullabilitySuffix);
|
||||
}
|
||||
|
||||
// otherwise if T is FutureOr<S> then flatten(T) = S
|
||||
// otherwise if T is Future<S> then flatten(T) = S (shortcut)
|
||||
if (type is InterfaceType) {
|
||||
if (type.isDartAsyncFutureOr || type.isDartAsyncFuture) {
|
||||
return type.typeArguments[0];
|
||||
// If T is X & S for some type variable X and type S then:
|
||||
if (T is TypeParameterTypeImpl) {
|
||||
final S = T.promotedBound;
|
||||
if (S != null) {
|
||||
// * if S has future type U then flatten(T) = flatten(U)
|
||||
final futureType = this.futureType(S);
|
||||
if (futureType != null) {
|
||||
return flatten(futureType);
|
||||
}
|
||||
// * otherwise, flatten(T) = flatten(X)
|
||||
return flatten(
|
||||
TypeParameterTypeImpl(
|
||||
element: T.element,
|
||||
nullabilitySuffix: nullabilitySuffix,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
var futureType = type.asInstanceOf(typeProvider.futureElement);
|
||||
if (futureType != null) {
|
||||
return futureType.typeArguments[0];
|
||||
// If T has future type Future<S> or FutureOr<S> then flatten(T) = S
|
||||
// If T has future type Future<S>? or FutureOr<S>? then flatten(T) = S?
|
||||
final futureType = this.futureType(T);
|
||||
if (futureType is InterfaceType) {
|
||||
if (futureType.isDartAsyncFuture || futureType.isDartAsyncFutureOr) {
|
||||
final S = futureType.typeArguments[0] as TypeImpl;
|
||||
if (futureType.nullabilitySuffix == NullabilitySuffix.question) {
|
||||
return S.withNullability(NullabilitySuffix.question);
|
||||
}
|
||||
return S;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise flatten(T) = T
|
||||
return type;
|
||||
return T;
|
||||
}
|
||||
|
||||
DartType futureOrBase(DartType type) {
|
||||
|
@ -282,6 +300,23 @@ class TypeSystemImpl implements TypeSystem {
|
|||
return type;
|
||||
}
|
||||
|
||||
/// We say that S is the future type of a type T in the following cases,
|
||||
/// using the first applicable case:
|
||||
@visibleForTesting
|
||||
DartType? futureType(DartType T) {
|
||||
// T implements S, and there is a U such that S is Future<U>
|
||||
if (T.nullabilitySuffix != NullabilitySuffix.question) {
|
||||
final result = T.asInstanceOf(typeProvider.futureElement);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// T is S bounded, and there is a U such that S is FutureOr<U>,
|
||||
// Future<U>?, or FutureOr<U>?.
|
||||
return _futureTypeOfBounded(T);
|
||||
}
|
||||
|
||||
/// Compute "future value type" of [T].
|
||||
///
|
||||
/// https://github.com/dart-lang/language/
|
||||
|
@ -1688,6 +1723,50 @@ class TypeSystemImpl implements TypeSystem {
|
|||
}).toFixedList();
|
||||
}
|
||||
|
||||
/// `S` is the future type of a type `T` in the following cases, using the
|
||||
/// first applicable case:
|
||||
/// * see [futureType].
|
||||
/// * `T` is `S` bounded, and there is a `U` such that `S` is `FutureOr<U>`,
|
||||
/// `Future<U>?`, or `FutureOr<U>?`.
|
||||
///
|
||||
/// 17.15.3: For a given type `T0`, we introduce the notion of a `T0` bounded
|
||||
/// type: `T0` itself is `T0` bounded; if `B` is `T0` bounded and `X` is a
|
||||
/// type variable with bound `B` then `X` is `T0` bounded; finally, if `B`
|
||||
/// is `T0` bounded and `X` is a type variable then `X&B` is `T0` bounded.
|
||||
DartType? _futureTypeOfBounded(DartType T) {
|
||||
if (T is InterfaceType) {
|
||||
if (T.nullabilitySuffix != NullabilitySuffix.question) {
|
||||
if (T.isDartAsyncFutureOr) {
|
||||
return T;
|
||||
}
|
||||
} else {
|
||||
if (T.isDartAsyncFutureOr || T.isDartAsyncFuture) {
|
||||
return T;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (T is TypeParameterTypeImpl) {
|
||||
final bound = T.element.bound;
|
||||
if (bound != null) {
|
||||
final result = _futureTypeOfBounded(bound);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
final promotedBound = T.promotedBound;
|
||||
if (promotedBound != null) {
|
||||
final result = _futureTypeOfBounded(promotedBound);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
DartType _refineBinaryExpressionTypeLegacy(DartType leftType,
|
||||
TokenType operator, DartType rightType, DartType currentType) {
|
||||
if (leftType is TypeParameterType && leftType.bound.isDartCoreNum) {
|
||||
|
|
|
@ -12,6 +12,7 @@ import '../../../generated/type_system_base.dart';
|
|||
main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(FlattenTypeTest);
|
||||
defineReflectiveTests(FutureTypeTest);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -21,51 +22,42 @@ class FlattenTypeTest extends AbstractTypeSystemTest {
|
|||
_check(dynamicNone, 'dynamic');
|
||||
}
|
||||
|
||||
test_interfaceType_none() {
|
||||
test_interfaceType() {
|
||||
_check(intNone, 'int');
|
||||
_check(intQuestion, 'int?');
|
||||
_check(intStar, 'int*');
|
||||
}
|
||||
|
||||
test_interfaceType_none_hasFutureType() {
|
||||
_check(futureNone(intNone), 'int');
|
||||
_check(futureNone(intQuestion), 'int?');
|
||||
_check(futureNone(intStar), 'int*');
|
||||
|
||||
// otherwise if T is FutureOr<S> then flatten(T) = S
|
||||
_check(futureQuestion(intNone), 'int?');
|
||||
_check(futureQuestion(intQuestion), 'int?');
|
||||
|
||||
_check(futureOrNone(intNone), 'int');
|
||||
_check(futureOrNone(intQuestion), 'int?');
|
||||
_check(futureOrNone(intStar), 'int*');
|
||||
|
||||
var A = class_(name: 'A', interfaces: [
|
||||
futureNone(intNone),
|
||||
]);
|
||||
_check(interfaceTypeNone(A), 'int');
|
||||
_check(futureOrQuestion(intNone), 'int?');
|
||||
_check(futureOrQuestion(intQuestion), 'int?');
|
||||
|
||||
_check(futureOrNone(futureNone(intNone)), 'Future<int>');
|
||||
_check(futureOrNone(futureNone(intQuestion)), 'Future<int?>');
|
||||
_check(futureOrNone(futureNone(intStar)), 'Future<int*>');
|
||||
|
||||
_check(futureOrQuestion(futureNone(intNone)), 'Future<int>?');
|
||||
_check(futureOrQuestion(futureNone(intQuestion)), 'Future<int?>?');
|
||||
}
|
||||
|
||||
test_interfaceType_question() {
|
||||
_check(futureQuestion(intNone), 'int?');
|
||||
_check(futureQuestion(intQuestion), 'int?');
|
||||
_check(futureQuestion(intStar), 'int?');
|
||||
|
||||
_check(futureQuestion(listNone(intNone)), 'List<int>?');
|
||||
_check(futureQuestion(listQuestion(intNone)), 'List<int>?');
|
||||
_check(futureQuestion(listStar(intNone)), 'List<int>?');
|
||||
|
||||
_check(futureOrQuestion(intNone), 'int?');
|
||||
_check(futureOrQuestion(intQuestion), 'int?');
|
||||
_check(futureOrQuestion(intStar), 'int?');
|
||||
}
|
||||
|
||||
test_interfaceType_star() {
|
||||
_check(futureStar(intNone), 'int*');
|
||||
_check(futureStar(intQuestion), 'int*');
|
||||
_check(futureStar(intStar), 'int*');
|
||||
|
||||
_check(futureStar(listNone(intNone)), 'List<int>*');
|
||||
_check(futureStar(listQuestion(intNone)), 'List<int>*');
|
||||
_check(futureStar(listStar(intNone)), 'List<int>*');
|
||||
|
||||
_check(futureOrStar(intNone), 'int*');
|
||||
_check(futureOrStar(intQuestion), 'int*');
|
||||
_check(futureOrStar(intStar), 'int*');
|
||||
}
|
||||
|
||||
test_typeParameterType_none() {
|
||||
// T extends Future<int>
|
||||
_check(
|
||||
typeParameterTypeNone(
|
||||
typeParameter('T', bound: futureNone(intNone)),
|
||||
|
@ -73,6 +65,15 @@ class FlattenTypeTest extends AbstractTypeSystemTest {
|
|||
'int',
|
||||
);
|
||||
|
||||
// T extends FutureOr<int>
|
||||
_check(
|
||||
typeParameterTypeNone(
|
||||
typeParameter('T', bound: futureOrNone(intNone)),
|
||||
),
|
||||
'int',
|
||||
);
|
||||
|
||||
// T & Future<int>
|
||||
_check(
|
||||
typeParameterTypeNone(
|
||||
typeParameter('T'),
|
||||
|
@ -80,18 +81,182 @@ class FlattenTypeTest extends AbstractTypeSystemTest {
|
|||
),
|
||||
'int',
|
||||
);
|
||||
|
||||
// T & FutureOr<int>
|
||||
_check(
|
||||
typeParameterTypeNone(
|
||||
typeParameter('T'),
|
||||
promotedBound: futureOrNone(intNone),
|
||||
),
|
||||
'int',
|
||||
);
|
||||
|
||||
// T extends int
|
||||
_check(
|
||||
typeParameterTypeNone(
|
||||
typeParameter('T', bound: intNone),
|
||||
),
|
||||
'T',
|
||||
);
|
||||
|
||||
// T & int
|
||||
_check(
|
||||
typeParameterTypeNone(
|
||||
typeParameter('T'),
|
||||
promotedBound: intNone,
|
||||
),
|
||||
'T',
|
||||
);
|
||||
}
|
||||
|
||||
test_typeParameterType_question() {
|
||||
// T extends Future<int>
|
||||
_check(
|
||||
typeParameterTypeQuestion(
|
||||
typeParameter('T', bound: futureNone(intNone)),
|
||||
),
|
||||
'int?',
|
||||
);
|
||||
|
||||
// T extends FutureOr<int>
|
||||
_check(
|
||||
typeParameterTypeQuestion(
|
||||
typeParameter('T', bound: futureOrNone(intNone)),
|
||||
),
|
||||
'int?',
|
||||
);
|
||||
}
|
||||
|
||||
test_unknownInferredType() {
|
||||
var type = UnknownInferredType.instance;
|
||||
final type = UnknownInferredType.instance;
|
||||
expect(typeSystem.flatten(type), same(type));
|
||||
}
|
||||
|
||||
void _check(DartType T, String expected) {
|
||||
var result = typeSystem.flatten(T);
|
||||
final result = typeSystem.flatten(T);
|
||||
expect(
|
||||
result.getDisplayString(withNullability: true),
|
||||
expected,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class FutureTypeTest extends AbstractTypeSystemTest {
|
||||
test_dynamic() {
|
||||
_check(dynamicNone, null);
|
||||
}
|
||||
|
||||
test_functionType() {
|
||||
_check(functionTypeNone(returnType: voidNone), null);
|
||||
}
|
||||
|
||||
test_implements_Future() {
|
||||
final A = class_(name: 'A', interfaces: [
|
||||
futureNone(intNone),
|
||||
]);
|
||||
|
||||
_check(interfaceTypeNone(A), 'Future<int>');
|
||||
_check(interfaceTypeQuestion(A), null);
|
||||
}
|
||||
|
||||
test_interfaceType() {
|
||||
_check(objectNone, null);
|
||||
_check(objectQuestion, null);
|
||||
|
||||
_check(intNone, null);
|
||||
_check(intQuestion, null);
|
||||
|
||||
_check(listNone(intNone), null);
|
||||
_check(listNone(intQuestion), null);
|
||||
|
||||
_check(listQuestion(intNone), null);
|
||||
_check(listQuestion(intQuestion), null);
|
||||
|
||||
_check(futureNone(intNone), 'Future<int>');
|
||||
_check(futureNone(intQuestion), 'Future<int?>');
|
||||
|
||||
_check(futureQuestion(intNone), 'Future<int>?');
|
||||
_check(futureQuestion(intQuestion), 'Future<int?>?');
|
||||
|
||||
_check(futureOrNone(intNone), 'FutureOr<int>');
|
||||
_check(futureOrNone(intQuestion), 'FutureOr<int?>');
|
||||
|
||||
_check(futureOrQuestion(intNone), 'FutureOr<int>?');
|
||||
_check(futureOrQuestion(intQuestion), 'FutureOr<int?>?');
|
||||
|
||||
_check(futureNone(futureNone(intNone)), 'Future<Future<int>>');
|
||||
_check(futureNone(futureOrNone(intNone)), 'Future<FutureOr<int>>');
|
||||
_check(futureOrNone(futureNone(intNone)), 'FutureOr<Future<int>>');
|
||||
_check(futureOrNone(futureOrNone(intNone)), 'FutureOr<FutureOr<int>>');
|
||||
}
|
||||
|
||||
test_typeParameterType_none() {
|
||||
// T extends Future<int>
|
||||
_check(
|
||||
typeParameterTypeNone(
|
||||
typeParameter('T', bound: futureNone(intNone)),
|
||||
),
|
||||
'Future<int>',
|
||||
);
|
||||
|
||||
// T extends FutureOr<int>
|
||||
_check(
|
||||
typeParameterTypeNone(
|
||||
typeParameter('T', bound: futureOrNone(intNone)),
|
||||
),
|
||||
'FutureOr<int>',
|
||||
);
|
||||
|
||||
// T & Future<int>
|
||||
_check(
|
||||
typeParameterTypeNone(
|
||||
typeParameter('T'),
|
||||
promotedBound: futureNone(intNone),
|
||||
),
|
||||
'Future<int>',
|
||||
);
|
||||
|
||||
// T & FutureOr<int>
|
||||
_check(
|
||||
typeParameterTypeNone(
|
||||
typeParameter('T'),
|
||||
promotedBound: futureOrNone(intNone),
|
||||
),
|
||||
'FutureOr<int>',
|
||||
);
|
||||
|
||||
// T extends int
|
||||
_check(
|
||||
typeParameterTypeNone(
|
||||
typeParameter('T', bound: intNone),
|
||||
),
|
||||
null,
|
||||
);
|
||||
|
||||
// T & int
|
||||
_check(
|
||||
typeParameterTypeNone(
|
||||
typeParameter('T'),
|
||||
promotedBound: intNone,
|
||||
),
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
test_unknownInferredType() {
|
||||
_check(UnknownInferredType.instance, null);
|
||||
}
|
||||
|
||||
void _check(DartType T, String? expected) {
|
||||
final result = typeSystem.futureType(T);
|
||||
if (result == null) {
|
||||
expect(expected, isNull);
|
||||
} else {
|
||||
expect(
|
||||
result.getDisplayString(withNullability: true),
|
||||
expected,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -179,7 +179,7 @@ void main() async {
|
|||
|
||||
// Promoted type variable; the value of `X` does not matter.
|
||||
Future<void> g6<X>(X x) async {
|
||||
if (X is FutureOr<A>) {
|
||||
if (x is FutureOr<A>) {
|
||||
var x2 = await x; // The future is awaited.
|
||||
x2.expectStaticType<Exactly<A>>();
|
||||
Expect.type<C1>(x2);
|
||||
|
@ -203,10 +203,10 @@ void main() async {
|
|||
}
|
||||
}
|
||||
|
||||
await g7<Object?, Object?>(Future<int>.value(1));
|
||||
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) {
|
||||
Future<void> h1<X extends Future<A>?>(X x) async {
|
||||
var x2 = await x; // Remains null.
|
||||
x2.expectStaticType<Exactly<A?>>();
|
||||
Expect.identical(null, x2);
|
||||
|
@ -215,7 +215,7 @@ void main() async {
|
|||
await h1<Null>(null);
|
||||
|
||||
// S? bounded type: called with `X == Future<A>`.
|
||||
Future<void> h2<X extends Future<A>?>(X x) {
|
||||
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);
|
||||
|
@ -226,7 +226,7 @@ void main() async {
|
|||
await h2<Future<A>>(C2());
|
||||
|
||||
// S? bounded type: called with `X == Null`.
|
||||
Future<void> h3<X extends FutureOr<A>?>(X x) {
|
||||
Future<void> h3<X extends FutureOr<A>?>(X x) async {
|
||||
var x2 = await x; // Remains null.
|
||||
x2.expectStaticType<Exactly<A?>>();
|
||||
Expect.identical(null, x2);
|
||||
|
@ -235,7 +235,7 @@ void main() async {
|
|||
await h3<Null>(null);
|
||||
|
||||
// S? bounded type: called with `X == FutureOr<A>`.
|
||||
Future<void> h4<X extends FutureOr<A>?>(X x) {
|
||||
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);
|
||||
|
|
Loading…
Reference in a new issue