mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 10:48:25 +00:00
[cfe] Implement sufficiency checks for the rest of type shapes
Part of https://github.com/dart-lang/sdk/issues/54998 Change-Id: Ib33391d56208e66dba0396da712ca05d21faece0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/358453 Commit-Queue: Chloe Stefantsova <cstefantsova@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
parent
2d7af14120
commit
5b0bf46bd2
|
@ -1723,6 +1723,275 @@ class TypeSchemaEnvironmentTest extends TypeSchemaEnvironmentTestBase {
|
|||
checkTargetType: "G<num, Object?>",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.interfaceShape);
|
||||
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "(int,)",
|
||||
checkTargetType: "(dynamic,)",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.recordShape);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "(int,)",
|
||||
checkTargetType: "(num,)",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.recordShape);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "(int,)",
|
||||
checkTargetType: "(String,)",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "(int, {String foo})",
|
||||
checkTargetType: "(dynamic, {dynamic foo})",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.recordShape);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "(int, {String foo})",
|
||||
checkTargetType: "(dynamic, {String foo})",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.recordShape);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "(int, {String foo})",
|
||||
checkTargetType: "(dynamic, {bool foo})",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "(int, {String foo})",
|
||||
checkTargetType: "(dynamic, {String bar})",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "(int) -> void",
|
||||
checkTargetType: "(Never) -> void",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.functionShape);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "(num) -> String",
|
||||
checkTargetType: "(int) -> Object",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.functionShape);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "(int) -> bool",
|
||||
checkTargetType: "(String) -> dynamic",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "(dynamic, {dynamic foo}) -> void",
|
||||
checkTargetType: "(int, {String foo}) -> void",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.functionShape);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "(dynamic, {String foo}) -> Object?",
|
||||
checkTargetType: "(bool, {String foo}) -> dynamic",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.functionShape);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "(dynamic, {bool foo}) -> Never?",
|
||||
checkTargetType: "(int, {String foo}) -> Null",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "(dynamic, {String foo}) -> Null",
|
||||
checkTargetType: "(int, {String bar}) -> Null",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "FutureOr<String>",
|
||||
checkTargetType: "FutureOr<Object?>",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.futureOrShape);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "FutureOr<String>",
|
||||
checkTargetType: "FutureOr<Object>",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.futureOrShape);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "FutureOr<int>",
|
||||
checkTargetType: "FutureOr<num>",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.futureOrShape);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "FutureOr<bool>",
|
||||
checkTargetType: "FutureOr<String>",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "int",
|
||||
checkTargetType: "(String,)",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "int",
|
||||
checkTargetType: "(num) -> void",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "bool",
|
||||
checkTargetType: "FutureOr<void>",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "(int,)",
|
||||
checkTargetType: "String",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "(int,)",
|
||||
checkTargetType: "(num) -> void",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "(bool,)",
|
||||
checkTargetType: "FutureOr<void>",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "(int) -> void",
|
||||
checkTargetType: "String",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "(int) -> void",
|
||||
checkTargetType: "(num,)",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "(bool) -> FutureOr<Object?>",
|
||||
checkTargetType: "FutureOr<void>",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "A<int>",
|
||||
checkTargetType: "C<int>?",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "A<int>",
|
||||
checkTargetType: "C<String>?",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "A<int>",
|
||||
checkTargetType: "C<dynamic>?",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.interfaceShape);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "A<int>?",
|
||||
checkTargetType: "C<dynamic>",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "B",
|
||||
checkTargetType: "C<int>?",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "B?",
|
||||
checkTargetType: "C<Object?>",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "B",
|
||||
checkTargetType: "C<Object?>?",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.interfaceShape);
|
||||
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "D<int>",
|
||||
checkTargetType: "E<int>?",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.interfaceShape);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "D<int>?",
|
||||
checkTargetType: "E<int>",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "D<int>",
|
||||
checkTargetType: "E<num>?",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.interfaceShape);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "D<int>?",
|
||||
checkTargetType: "E<num>",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "D<int>",
|
||||
checkTargetType: "E<dynamic>?",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.interfaceShape);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "D<int>?",
|
||||
checkTargetType: "E<dynamic>",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "D<num>",
|
||||
checkTargetType: "E<int>?",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "D<num>?",
|
||||
checkTargetType: "E<int>",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "F<int>",
|
||||
checkTargetType: "G<int, String>?",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "F<int>?",
|
||||
checkTargetType: "G<int, String>",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "F<int>",
|
||||
checkTargetType: "G<num, String>?",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "F<int>?",
|
||||
checkTargetType: "G<num, String>",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "F<int>",
|
||||
checkTargetType: "G<dynamic, Object?>?",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.interfaceShape);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "F<int>?",
|
||||
checkTargetType: "G<dynamic, Object?>",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "F<int>",
|
||||
checkTargetType: "G<int, Object?>?",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.interfaceShape);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "F<int>?",
|
||||
checkTargetType: "G<int, Object?>",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "F<int>",
|
||||
checkTargetType: "G<num, Object?>?",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.interfaceShape);
|
||||
checkTypeShapeCheckSufficiency(
|
||||
expressionStaticType: "F<int>?",
|
||||
checkTargetType: "G<num, Object?>",
|
||||
typeParameters: "",
|
||||
sufficiency: TypeShapeCheckSufficiency.insufficient);
|
||||
}
|
||||
|
||||
void checkUpperBound(
|
||||
|
|
|
@ -340,15 +340,18 @@ abstract class TypeEnvironment extends Types {
|
|||
{required DartType expressionStaticType,
|
||||
required DartType checkTargetType,
|
||||
required SubtypeCheckMode subtypeCheckMode}) {
|
||||
if (checkTargetType is! InterfaceType ||
|
||||
expressionStaticType is! InterfaceType) {
|
||||
// TODO(cstefantsova): Support record, function, and futureOr types.
|
||||
if (!IsSubtypeOf.basedSolelyOnNullabilities(
|
||||
expressionStaticType, checkTargetType)
|
||||
.inMode(subtypeCheckMode)) {
|
||||
return TypeShapeCheckSufficiency.insufficient;
|
||||
} else {
|
||||
} else if (checkTargetType is InterfaceType &&
|
||||
expressionStaticType is InterfaceType) {
|
||||
// Analyze if an interface shape check is sufficient.
|
||||
|
||||
// If `T` in `e is/as T` doesn't have type arguments, there's nothing more to
|
||||
// check besides the shape.
|
||||
// TODO(cstefantsova): Investigate if [expressionStaticType] can be of any
|
||||
// kind for the following sufficiency check to work.
|
||||
if (checkTargetType.typeArguments.isEmpty) {
|
||||
return TypeShapeCheckSufficiency.interfaceShape;
|
||||
}
|
||||
|
@ -366,12 +369,17 @@ abstract class TypeEnvironment extends Types {
|
|||
for (int typeParameterIndex = 0;
|
||||
typeParameterIndex < checkTargetTypeOwnTypeParameters.length;
|
||||
typeParameterIndex++) {
|
||||
// TODO(cstefantsova): Investigate if super-bounded types can appear as
|
||||
// [checkTargetType]s. In that case a subtype check should be done
|
||||
// instead of the mutual subtype check.
|
||||
if (!_isRawTypeArgumentEquivalent(checkTargetType, typeParameterIndex,
|
||||
subtypeCheckMode: subtypeCheckMode)) {
|
||||
targetTypeArgumentsAreDefaultTypes = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TODO(cstefantsova): Investigate if [expressionStaticType] can be of any
|
||||
// kind for the following sufficiency check to work.
|
||||
if (targetTypeArgumentsAreDefaultTypes) {
|
||||
return TypeShapeCheckSufficiency.interfaceShape;
|
||||
}
|
||||
|
@ -526,6 +534,79 @@ abstract class TypeEnvironment extends Types {
|
|||
return TypeShapeCheckSufficiency.insufficient;
|
||||
}
|
||||
}
|
||||
} else if (checkTargetType is RecordType &&
|
||||
expressionStaticType is RecordType) {
|
||||
bool isTopRecordTypeForTheShape = true;
|
||||
for (DartType positional in checkTargetType.positional) {
|
||||
if (!isTop(positional)) {
|
||||
isTopRecordTypeForTheShape = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (NamedType named in checkTargetType.named) {
|
||||
if (!isTop(named.type)) {
|
||||
isTopRecordTypeForTheShape = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isTopRecordTypeForTheShape) {
|
||||
// TODO(cstefantsova): Investigate if [expressionStaticType] can be of
|
||||
// any kind for the following sufficiency check to work.
|
||||
return TypeShapeCheckSufficiency.recordShape;
|
||||
}
|
||||
|
||||
if (isSubtypeOf(
|
||||
expressionStaticType, checkTargetType, subtypeCheckMode)) {
|
||||
return TypeShapeCheckSufficiency.recordShape;
|
||||
} else {
|
||||
return TypeShapeCheckSufficiency.insufficient;
|
||||
}
|
||||
} else if (checkTargetType is FunctionType &&
|
||||
expressionStaticType is FunctionType) {
|
||||
if (checkTargetType.typeParameters.isEmpty &&
|
||||
expressionStaticType.typeParameters.isEmpty) {
|
||||
bool isTopFunctionTypeForTheShape = true;
|
||||
for (DartType positional in checkTargetType.positionalParameters) {
|
||||
if (!isBottom(positional)) {
|
||||
isTopFunctionTypeForTheShape = false;
|
||||
}
|
||||
}
|
||||
for (NamedType named in checkTargetType.namedParameters) {
|
||||
if (!isBottom(named.type)) {
|
||||
isTopFunctionTypeForTheShape = false;
|
||||
}
|
||||
}
|
||||
if (!isTop(checkTargetType.returnType)) {
|
||||
isTopFunctionTypeForTheShape = false;
|
||||
}
|
||||
|
||||
if (isTopFunctionTypeForTheShape) {
|
||||
// TODO(cstefantsova): Investigate if [expressionStaticType] can be of
|
||||
// any kind for the following sufficiency check to work.
|
||||
return TypeShapeCheckSufficiency.functionShape;
|
||||
}
|
||||
}
|
||||
|
||||
if (isSubtypeOf(
|
||||
expressionStaticType, checkTargetType, subtypeCheckMode)) {
|
||||
return TypeShapeCheckSufficiency.functionShape;
|
||||
} else {
|
||||
return TypeShapeCheckSufficiency.insufficient;
|
||||
}
|
||||
} else if (checkTargetType is FutureOrType &&
|
||||
expressionStaticType is FutureOrType) {
|
||||
if (isTop(checkTargetType.typeArgument)) {
|
||||
// TODO(cstefantsova): Investigate if [expressionStaticType] can be of
|
||||
// any kind for the following sufficiency check to work.
|
||||
return TypeShapeCheckSufficiency.futureOrShape;
|
||||
} else if (isSubtypeOf(expressionStaticType.typeArgument,
|
||||
checkTargetType.typeArgument, subtypeCheckMode)) {
|
||||
return TypeShapeCheckSufficiency.futureOrShape;
|
||||
} else {
|
||||
return TypeShapeCheckSufficiency.insufficient;
|
||||
}
|
||||
} else {
|
||||
return TypeShapeCheckSufficiency.insufficient;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -766,6 +847,15 @@ class IsSubtypeOf {
|
|||
}
|
||||
return "IsSubtypeOf.<unknown value '${_value}'>";
|
||||
}
|
||||
|
||||
bool inMode(SubtypeCheckMode subtypeCheckMode) {
|
||||
switch (subtypeCheckMode) {
|
||||
case SubtypeCheckMode.withNullabilities:
|
||||
return isSubtypeWhenUsingNullabilities();
|
||||
case SubtypeCheckMode.ignoringNullabilities:
|
||||
return isSubtypeWhenIgnoringNullabilities();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum SubtypeCheckMode {
|
||||
|
|
Loading…
Reference in a new issue