diff --git a/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart b/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart index 4a5b6ffc499..21454694192 100644 --- a/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart +++ b/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart @@ -691,6 +691,21 @@ abstract interface class TypeAnalyzerOperations`, + /// * `bool Function(double)`. + /// + /// Examples of types that are not known: + /// + /// * `_`, + /// * `List<_>`, + /// * `_ Function(_)`. + bool isKnownType(SharedTypeSchemaView typeSchema); } mixin TypeAnalyzerOperationsMixin property.whyNotPromotable; + + @override + bool isKnownType(SharedTypeSchemaView typeSchema) { + var unwrapped = typeSchema.unwrapTypeSchemaView(); + 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 diff --git a/pkg/_fe_analyzer_shared/test/type_inference/type_constraint_gatherer_test.dart b/pkg/_fe_analyzer_shared/test/type_inference/type_constraint_gatherer_test.dart index 983c521795f..adc99ee76a3 100644 --- a/pkg/_fe_analyzer_shared/test/type_inference/type_constraint_gatherer_test.dart +++ b/pkg/_fe_analyzer_shared/test/type_inference/type_constraint_gatherer_test.dart @@ -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')))) + .isTrue(); + check(tcg.typeAnalyzerOperations + .isKnownType(SharedTypeSchemaView(Type('List<_>')))) + .isFalse(); + check(tcg.typeAnalyzerOperations + .isKnownType(SharedTypeSchemaView(Type('List>')))) + .isTrue(); + check(tcg.typeAnalyzerOperations + .isKnownType(SharedTypeSchemaView(Type('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 Function(int) named})')))) + .isTrue(); + check(tcg.typeAnalyzerOperations.isKnownType(SharedTypeSchemaView( + Type('(int, String, {List<_> Function(int) named})')))) + .isFalse(); + }); + }); } diff --git a/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart b/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart index fa0ff6dc207..3c4bced15d5 100644 --- a/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart +++ b/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart @@ -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; } diff --git a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart index 17299911e64..64c4756794c 100644 --- a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart +++ b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart @@ -667,6 +667,13 @@ class TypeSystemOperations type.element3 is! ExtensionTypeElement; } + @override + bool isKnownType(SharedTypeSchemaView typeSchema) { + return UnknownInferredType.isKnown( + typeSchema.unwrapTypeSchemaView(), + ); + } + @override bool isNonNullableInternal(TypeImpl type) { return typeSystem.isNonNullable(type); diff --git a/pkg/front_end/lib/src/type_inference/type_inference_engine.dart b/pkg/front_end/lib/src/type_inference/type_inference_engine.dart index f3e79e4cdf5..6cf7dccaecd 100644 --- a/pkg/front_end/lib/src/type_inference/type_inference_engine.dart +++ b/pkg/front_end/lib/src/type_inference/type_inference_engine.dart @@ -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. diff --git a/pkg/front_end/lib/src/type_inference/type_schema_environment.dart b/pkg/front_end/lib/src/type_inference/type_schema_environment.dart index 3776d713e2d..bb8a1aa30ff 100644 --- a/pkg/front_end/lib/src/type_inference/type_schema_environment.dart +++ b/pkg/front_end/lib/src/type_inference/type_schema_environment.dart @@ -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; diff --git a/pkg/front_end/test/type_inference/type_schema_environment_test_base.dart b/pkg/front_end/test/type_inference/type_schema_environment_test_base.dart index 5b0b01d9d32..fe60ef5e530 100644 --- a/pkg/front_end/test/type_inference/type_schema_environment_test_base.dart +++ b/pkg/front_end/test/type_inference/type_schema_environment_test_base.dart @@ -68,7 +68,7 @@ abstract class TypeSchemaEnvironmentTestBase { {required bool grounded}) { expect( typeSchemaEnvironment.solveTypeConstraint(parseConstraint(constraint), - grounded: grounded), + grounded: grounded, operations: _operations), parseType(expected)); }