[model] Define common helper isKnownType and use in inference

This CL is another step towards the unification between
TypeSchemaEnvironment.solveTypeConstraint in the CFE and
GenericInferrer._chooseTypeFromConstraint in the Analyzer.

Part of https://github.com/dart-lang/sdk/issues/54902

Change-Id: I2cfafef6a87b9bde8d1c94f6618e7f633debec0f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/424040
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
This commit is contained in:
Chloe Stefantsova
2025-04-28 02:19:45 -07:00
committed by Commit Queue
parent b23efe44e8
commit a2b2751d7d
8 changed files with 197 additions and 25 deletions
@@ -691,6 +691,21 @@ abstract interface class TypeAnalyzerOperations<Variable extends Object,
/// Converts a type into a corresponding type schema.
SharedTypeSchemaView typeToSchema(SharedTypeView type);
/// Determines whether a type schema contains the unknown type.
///
/// Examples of known types:
///
/// * `int`,
/// * `List<String>`,
/// * `bool Function(double)`.
///
/// Examples of types that are not known:
///
/// * `_`,
/// * `List<_>`,
/// * `_ Function(_)`.
bool isKnownType(SharedTypeSchemaView typeSchema);
}
mixin TypeAnalyzerOperationsMixin<Variable extends Object,
@@ -3236,6 +3236,63 @@ class MiniAstOperations
PropertyNonPromotabilityReason? whyPropertyIsNotPromotable(
covariant _PropertyElement property) =>
property.whyNotPromotable;
@override
bool isKnownType(SharedTypeSchemaView typeSchema) {
var unwrapped = typeSchema.unwrapTypeSchemaView<Type>();
switch (unwrapped) {
case FutureOrType(:var typeArgument):
return isKnownType(SharedTypeSchemaView(typeArgument));
case PrimaryType(:var args):
for (var arg in args) {
if (!isKnownType(SharedTypeSchemaView(arg))) {
return false;
}
}
return true;
case FunctionType(
:var returnType,
:var typeParametersShared,
:var positionalParameters,
:var namedParameters
):
if (!isKnownType(SharedTypeSchemaView(returnType))) {
return false;
}
for (var typeParameter in typeParametersShared) {
if (!isKnownType(SharedTypeSchemaView(typeParameter.bound))) {
return false;
}
}
for (var positionalParameter in positionalParameters) {
if (!isKnownType(SharedTypeSchemaView(positionalParameter))) {
return false;
}
}
for (var namedParameter in namedParameters) {
if (!isKnownType(SharedTypeSchemaView(namedParameter.type))) {
return false;
}
}
return true;
case RecordType(:var positionalTypes, :var namedTypes):
for (var positionalType in positionalTypes) {
if (!isKnownType(SharedTypeSchemaView(positionalType))) {
return false;
}
}
for (var namedType in namedTypes) {
if (!isKnownType(SharedTypeSchemaView(namedType.type))) {
return false;
}
}
return true;
case UnknownType():
return false;
default:
return true;
}
}
}
/// Representation of an expression or statement in the pseudo-Dart language
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer_operations.dart';
import 'package:_fe_analyzer_shared/src/types/shared_type.dart';
import 'package:checks/checks.dart';
import 'package:test/scaffolding.dart';
@@ -855,4 +856,80 @@ main() {
check(tcg.constraints).unorderedEquals(['num <: T']);
});
});
group('isKnownType', () {
test('Simple types', () {
var tcg = TypeConstraintGatherer({'T'});
check(tcg.typeAnalyzerOperations
.isKnownType(SharedTypeSchemaView(Type('String'))))
.isTrue();
check(tcg.typeAnalyzerOperations
.isKnownType(SharedTypeSchemaView(Type('dynamic'))))
.isTrue();
check(tcg.typeAnalyzerOperations
.isKnownType(SharedTypeSchemaView(Type('Object'))))
.isTrue();
check(tcg.typeAnalyzerOperations
.isKnownType(SharedTypeSchemaView(Type('void'))))
.isTrue();
check(tcg.typeAnalyzerOperations
.isKnownType(SharedTypeSchemaView(Type('T'))))
.isTrue();
check(tcg.typeAnalyzerOperations
.isKnownType(SharedTypeSchemaView(Type('_'))))
.isFalse();
});
test('Compound types', () {
var tcg = TypeConstraintGatherer({'T'});
check(tcg.typeAnalyzerOperations
.isKnownType(SharedTypeSchemaView(Type('List<String>'))))
.isTrue();
check(tcg.typeAnalyzerOperations
.isKnownType(SharedTypeSchemaView(Type('List<_>'))))
.isFalse();
check(tcg.typeAnalyzerOperations
.isKnownType(SharedTypeSchemaView(Type('List<List<int>>'))))
.isTrue();
check(tcg.typeAnalyzerOperations
.isKnownType(SharedTypeSchemaView(Type('List<List<_>>'))))
.isFalse();
check(tcg.typeAnalyzerOperations
.isKnownType(SharedTypeSchemaView(Type('dynamic Function()'))))
.isTrue();
check(tcg.typeAnalyzerOperations
.isKnownType(SharedTypeSchemaView(Type('_ Function()'))))
.isFalse();
check(tcg.typeAnalyzerOperations
.isKnownType(SharedTypeSchemaView(Type('int Function(int)'))))
.isTrue();
check(tcg.typeAnalyzerOperations
.isKnownType(SharedTypeSchemaView(Type('int Function(_)'))))
.isFalse();
check(tcg.typeAnalyzerOperations.isKnownType(
SharedTypeSchemaView(Type('int Function({String named})'))))
.isTrue();
check(tcg.typeAnalyzerOperations.isKnownType(
SharedTypeSchemaView(Type('int Function({_ named})'))))
.isFalse();
check(tcg.typeAnalyzerOperations
.isKnownType(SharedTypeSchemaView(Type('(int, String, Object)'))))
.isTrue();
check(tcg.typeAnalyzerOperations
.isKnownType(SharedTypeSchemaView(Type('(int, String, _)'))))
.isFalse();
check(tcg.typeAnalyzerOperations.isKnownType(
SharedTypeSchemaView(Type('(int, String, {dynamic named})'))))
.isTrue();
check(tcg.typeAnalyzerOperations.isKnownType(
SharedTypeSchemaView(Type('(int, String, {_ named})'))))
.isFalse();
check(tcg.typeAnalyzerOperations.isKnownType(SharedTypeSchemaView(
Type('(int, String, {List<dynamic> Function(int) named})'))))
.isTrue();
check(tcg.typeAnalyzerOperations.isKnownType(SharedTypeSchemaView(
Type('(int, String, {List<_> Function(int) named})'))))
.isFalse();
});
});
}
@@ -371,7 +371,7 @@ class GenericInferrer {
);
}
if (UnknownInferredType.isKnown(inferred)) {
if (_typeSystemOperations.isKnownType(SharedTypeSchemaView(inferred))) {
knownTypes[parameter] = inferred;
} else if (_strictInference) {
// [typeParam] could not be inferred. A result will still be returned
@@ -519,10 +519,10 @@ class GenericInferrer {
// For both of those, prefer the lower bound (arbitrary heuristic) or upper
// bound if [isContravariant] is `true`
if (isContravariant) {
if (UnknownInferredType.isKnown(upper)) {
if (_typeSystemOperations.isKnownType(SharedTypeSchemaView(upper))) {
return upper;
}
if (UnknownInferredType.isKnown(lower)) {
if (_typeSystemOperations.isKnownType(SharedTypeSchemaView(lower))) {
return lower;
}
if (!identical(UnknownInferredType.instance, upper)) {
@@ -533,10 +533,10 @@ class GenericInferrer {
}
return upper;
} else {
if (UnknownInferredType.isKnown(lower)) {
if (_typeSystemOperations.isKnownType(SharedTypeSchemaView(lower))) {
return lower;
}
if (UnknownInferredType.isKnown(upper)) {
if (_typeSystemOperations.isKnownType(SharedTypeSchemaView(upper))) {
return upper;
}
if (!identical(UnknownInferredType.instance, lower)) {
@@ -596,7 +596,9 @@ class GenericInferrer {
inferredTypes[i] = inferredType;
if (typeParam.isLegacyCovariant &&
UnknownInferredType.isKnown(inferredType)) {
_typeSystemOperations.isKnownType(
SharedTypeSchemaView(inferredType),
)) {
_typesInferredSoFar[typeParam] = inferredType;
}
} else {
@@ -730,7 +732,7 @@ class GenericInferrer {
constraint,
isContravariant: isContravariant,
);
if (UnknownInferredType.isUnknown(t)) {
if (!_typeSystemOperations.isKnownType(SharedTypeSchemaView(t))) {
return t;
}
@@ -667,6 +667,13 @@ class TypeSystemOperations
type.element3 is! ExtensionTypeElement;
}
@override
bool isKnownType(SharedTypeSchemaView typeSchema) {
return UnknownInferredType.isKnown(
typeSchema.unwrapTypeSchemaView<TypeImpl>(),
);
}
@override
bool isNonNullableInternal(TypeImpl type) {
return typeSystem.isNonNullable(type);
@@ -1043,6 +1043,11 @@ class OperationsCfe
inferenceResultForTesting: null,
inferenceUsingBoundsIsEnabled: inferenceUsingBoundsIsEnabled);
}
@override
bool isKnownType(SharedTypeSchemaView typeSchema) {
return isKnown(typeSchema.unwrapTypeSchemaView());
}
}
/// Type inference results used for testing.
@@ -20,7 +20,7 @@ import 'standard_bounds.dart' show TypeSchemaStandardBounds;
import 'type_constraint_gatherer.dart' show TypeConstraintGatherer;
import 'type_demotion.dart';
import 'type_inference_engine.dart';
import 'type_schema.dart' show UnknownType, isKnown;
import 'type_schema.dart' show UnknownType;
import 'type_schema_elimination.dart' show greatestClosure, leastClosure;
typedef GeneratedTypeConstraint
@@ -236,7 +236,8 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment
new TypeParameterType.withDefaultNullability(
helperTypeParameters[i]);
} else {
assert(isKnown(inferredTypes[i]));
assert(operations
.isKnownType(new SharedTypeSchemaView(inferredTypes[i])));
inferredSubstitution[helperTypeParameters[i]] = inferredTypes[i];
}
}
@@ -344,13 +345,15 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment
/// type parameter which means we choose the upper bound rather than the
/// lower bound for normally covariant type parameters.
DartType solveTypeConstraint(MergedTypeConstraint constraint,
{bool grounded = false, bool isContravariant = false}) {
{bool grounded = false,
bool isContravariant = false,
required covariant OperationsCfe operations}) {
if (!isContravariant) {
// Prefer the known bound, if any.
if (isKnown(constraint.lower.unwrapTypeSchemaView())) {
if (operations.isKnownType(constraint.lower)) {
return constraint.lower.unwrapTypeSchemaView();
}
if (isKnown(constraint.upper.unwrapTypeSchemaView())) {
if (operations.isKnownType(constraint.upper)) {
return constraint.upper.unwrapTypeSchemaView();
}
@@ -361,27 +364,28 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment
? leastClosure(constraint.lower.unwrapTypeSchemaView(),
coreTypes: coreTypes)
: constraint.lower.unwrapTypeSchemaView();
} else if (constraint.upper is! UnknownType) {
} else if (constraint.upper is! SharedUnknownTypeSchemaView) {
return grounded
? greatestClosure(constraint.upper.unwrapTypeSchemaView(),
topType: coreTypes.objectNullableRawType)
: constraint.upper.unwrapTypeSchemaView();
} else {
return const UnknownType();
assert(constraint.lower is SharedUnknownTypeSchemaView);
return constraint.lower.unwrapTypeSchemaView();
}
} else {
// Prefer the known bound, if any.
if (isKnown(constraint.upper.unwrapTypeSchemaView())) {
if (operations.isKnownType(constraint.upper)) {
// Coverage-ignore-block(suite): Not run.
return constraint.upper.unwrapTypeSchemaView();
}
if (isKnown(constraint.lower.unwrapTypeSchemaView())) {
if (operations.isKnownType(constraint.lower)) {
return constraint.lower.unwrapTypeSchemaView();
}
// Otherwise take whatever bound has partial information,
// e.g. `Iterable<?>`
if (constraint.upper is! UnknownType) {
if (constraint.upper is! SharedUnknownTypeSchemaView) {
// Coverage-ignore-block(suite): Not run.
return grounded
? greatestClosure(constraint.upper.unwrapTypeSchemaView(),
@@ -395,7 +399,8 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment
// Coverage-ignore(suite): Not run.
constraint.lower.unwrapTypeSchemaView();
} else {
return const UnknownType();
assert(constraint.upper is SharedUnknownTypeSchemaView);
return constraint.upper.unwrapTypeSchemaView();
}
}
}
@@ -464,7 +469,8 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment
// false.
if (typeFromPreviousInference != null &&
isLegacyCovariant &&
isKnown(typeFromPreviousInference)) {
operations
.isKnownType(new SharedTypeSchemaView(typeFromPreviousInference))) {
return typeFromPreviousInference;
}
@@ -492,7 +498,9 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment
}
return solveTypeConstraint(constraint,
grounded: true, isContravariant: isContravariant);
grounded: true,
isContravariant: isContravariant,
operations: operations);
}
DartType _inferTypeParameterFromContext(DartType? typeFromPreviousInference,
@@ -509,12 +517,13 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment
// false.
if (isLegacyCovariant &&
typeFromPreviousInference != null &&
isKnown(typeFromPreviousInference)) {
operations
.isKnownType(new SharedTypeSchemaView(typeFromPreviousInference))) {
return typeFromPreviousInference;
}
DartType t = solveTypeConstraint(constraint);
if (!isKnown(t)) {
DartType t = solveTypeConstraint(constraint, operations: operations);
if (!operations.isKnownType(new SharedTypeSchemaView(t))) {
return t;
}
@@ -548,7 +557,7 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment
constraint = constraint.clone();
constraint.mergeInTypeSchemaUpper(
new SharedTypeSchemaView(extendsConstraint), operations);
return solveTypeConstraint(constraint);
return solveTypeConstraint(constraint, operations: operations);
}
return t;
@@ -68,7 +68,7 @@ abstract class TypeSchemaEnvironmentTestBase {
{required bool grounded}) {
expect(
typeSchemaEnvironment.solveTypeConstraint(parseConstraint(constraint),
grounded: grounded),
grounded: grounded, operations: _operations),
parseType(expected));
}