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 09e310335675..d4eaf4d8b1dc 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 @@ -5,6 +5,7 @@ import '../flow_analysis/flow_analysis_operations.dart'; import '../types/shared_type.dart'; import 'nullability_suffix.dart'; +import 'type_constraint.dart'; /// Callback API used by the shared type analyzer to query and manipulate the /// client's representation of variables and types. @@ -698,6 +699,40 @@ abstract interface class TypeAnalyzerOperations< /// Returns [type] suffixed with the [suffix]. TypeStructure withNullabilitySuffixInternal( TypeStructure type, NullabilitySuffix suffix); + + TypeConstraintGenerator< + TypeStructure, + SharedNamedFunctionParameterStructure, + Variable, + TypeParameterStructure, + TypeDeclarationType, + TypeDeclaration, + Object> + createTypeConstraintGenerator( + {required TypeConstraintGenerationDataForTesting? + typeConstraintGenerationDataForTesting, + required List typeParametersToInfer, + required TypeAnalyzerOperations + typeAnalyzerOperations, + required bool inferenceUsingBoundsIsEnabled}); + + MergedTypeConstraint + mergeInConstraintsFromBound( + {required TypeParameterStructure typeParameterToInfer, + required List typeParametersToInfer, + required TypeStructure lower, + required Map< + TypeParameterStructure, + MergedTypeConstraint> + inferencePhaseConstraints, + required TypeConstraintGenerationDataForTesting? + dataForTesting, + required bool inferenceUsingBoundsIsEnabled}); } mixin TypeAnalyzerOperationsMixin< @@ -893,6 +928,96 @@ mixin TypeAnalyzerOperationsMixin< SharedTypeView type) { return new SharedTypeSchemaView(type.unwrapTypeView()); } + + @override + MergedTypeConstraint + mergeInConstraintsFromBound( + {required TypeParameterStructure typeParameterToInfer, + required List typeParametersToInfer, + required TypeStructure lower, + required Map< + TypeParameterStructure, + MergedTypeConstraint> + inferencePhaseConstraints, + required TypeConstraintGenerationDataForTesting? + dataForTesting, + required bool inferenceUsingBoundsIsEnabled}) { + // The type parameter's bound may refer to itself (or other type + // parameters), so we might have to create an additional constraint. + // Consider this example from + // https://github.com/dart-lang/language/issues/3009: + // + // class A> {} + // class B extends A {} + // class C extends B {} + // void f>(X x) {} + // void main() { + // f(C()); // should infer f(C()). + // } + // + // In order for `f(C())` to be inferred as `f(C())`, we need to + // generate the constraint `X <: B`. To do this, we first take the lower + // constraint we've accumulated so far (which, in this example, is `C`, + // due to the presence of the actual argument `C()`), and use subtype + // constraint generation to match it against the explicit bound (which + // is `A`; hence we perform `C <# A`). If this produces any + // constraints (i.e. `X <: B` in this example), then they are added to + // the set of constraints just before choosing the final type. + + TypeStructure typeParameterToInferBound = typeParameterToInfer.bound!; + + // TODO(cstefantsova): Pass [dataForTesting] when + // [InferenceDataForTesting] is merged with [TypeInferenceResultForTesting]. + TypeConstraintGenerator< + TypeStructure, + SharedNamedFunctionParameterStructure, + Variable, + TypeParameterStructure, + TypeDeclarationType, + TypeDeclaration, + Object> typeConstraintGatherer = + createTypeConstraintGenerator( + typeConstraintGenerationDataForTesting: null, + typeParametersToInfer: typeParametersToInfer, + typeAnalyzerOperations: this, + inferenceUsingBoundsIsEnabled: inferenceUsingBoundsIsEnabled); + typeConstraintGatherer.performSubtypeConstraintGenerationInternal( + lower, typeParameterToInferBound, + leftSchema: true, astNodeForTesting: null); + Map< + TypeParameterStructure, + MergedTypeConstraint< + TypeStructure, + TypeParameterStructure, + Variable, + TypeDeclarationType, + TypeDeclaration>> constraintsPerTypeVariable = + typeConstraintGatherer.computeConstraints(); + for (TypeParameterStructure typeParameter + in constraintsPerTypeVariable.keys) { + MergedTypeConstraint constraint = + constraintsPerTypeVariable[typeParameter]!; + constraint.origin = new TypeConstraintFromExtendsClause( + typeParameterName: typeParameterToInfer.displayName, + boundType: new SharedTypeView(typeParameterToInferBound), + extendsType: new SharedTypeView(typeParameterToInferBound)); + if (!constraint.isEmpty(this)) { + MergedTypeConstraint? constraintForParameter = + inferencePhaseConstraints[typeParameter]; + if (constraintForParameter == null) { + inferencePhaseConstraints[typeParameter] = constraint; + } else { + constraintForParameter.mergeInTypeSchemaUpper(constraint.upper, this); + constraintForParameter.mergeInTypeSchemaLower(constraint.lower, this); + } + } + } + return constraintsPerTypeVariable[typeParameterToInfer]!; + } } /// Abstract interface of a type constraint generator. @@ -1832,6 +1957,12 @@ abstract class TypeConstraintGenerator< return false; } + + /// Returns the set of type constraints that was gathered. + Map< + TypeParameterStructure, + MergedTypeConstraint> computeConstraints(); } mixin TypeConstraintGeneratorMixin< diff --git a/pkg/_fe_analyzer_shared/lib/src/type_inference/type_constraint.dart b/pkg/_fe_analyzer_shared/lib/src/type_inference/type_constraint.dart index 58beeed3827f..7a8a79dc3bca 100644 --- a/pkg/_fe_analyzer_shared/lib/src/type_inference/type_constraint.dart +++ b/pkg/_fe_analyzer_shared/lib/src/type_inference/type_constraint.dart @@ -365,3 +365,41 @@ class UnknownTypeConstraintOrigin< return []; } } + +/// Data structure maintaining intermediate type inference results, such as +/// type constraints, for testing purposes. Under normal execution, no +/// instance of this class should be created. +class TypeConstraintGenerationDataForTesting< + TypeStructure extends SharedTypeStructure, + TypeParameterStructure extends SharedTypeParameterStructure, + Variable extends Object, + AstNode extends Object> { + /// Map from nodes requiring type inference to the generated type constraints + /// for the node. + final Map< + AstNode, + List< + GeneratedTypeConstraint>> generatedTypeConstraints = {}; + + /// Merges [other] into the receiver, combining the constraints. + /// + /// The method reuses data structures from [other] whenever possible, to + /// avoid extra memory allocations. This process is destructive to [other] + /// because the changes made to the reused structures will be visible to + /// [other]. + void mergeIn( + TypeConstraintGenerationDataForTesting + other) { + for (AstNode node in other.generatedTypeConstraints.keys) { + List? constraints = + generatedTypeConstraints[node]; + if (constraints != null) { + constraints.addAll(other.generatedTypeConstraints[node]!); + } else { + generatedTypeConstraints[node] = other.generatedTypeConstraints[node]!; + } + } + } +} diff --git a/pkg/_fe_analyzer_shared/test/mini_ast.dart b/pkg/_fe_analyzer_shared/test/mini_ast.dart index 264acd146e6a..e9b5c314d4f3 100644 --- a/pkg/_fe_analyzer_shared/test/mini_ast.dart +++ b/pkg/_fe_analyzer_shared/test/mini_ast.dart @@ -28,11 +28,14 @@ import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer.dart' as shared; import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer.dart' hide MapPatternEntry, RecordPatternField; +import 'package:_fe_analyzer_shared/src/type_inference/type_constraint.dart'; import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer_operations.dart'; import 'package:_fe_analyzer_shared/src/type_inference/variable_bindings.dart'; import 'package:_fe_analyzer_shared/src/types/shared_type.dart'; import 'package:test/test.dart'; +import 'type_inference/type_constraint_gatherer_test.dart'; + import 'mini_ir.dart'; import 'mini_types.dart'; @@ -3225,6 +3228,21 @@ class MiniAstOperations Type withNullabilitySuffixInternal(Type type, NullabilitySuffix modifier) { return type.withNullability(modifier); } + + @override + TypeConstraintGenerator + createTypeConstraintGenerator( + {required TypeConstraintGenerationDataForTesting? + typeConstraintGenerationDataForTesting, + required List typeParametersToInfer, + required TypeAnalyzerOperations + typeAnalyzerOperations, + required bool inferenceUsingBoundsIsEnabled}) { + return TypeConstraintGatherer( + {for (var typeParameter in typeParametersToInfer) typeParameter.name}); + } } /// 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 a9ab92d25995..57d600a96d7d 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/type_inference/type_constraint.dart'; import 'package:_fe_analyzer_shared/src/types/shared_type.dart'; import 'package:checks/checks.dart'; import 'package:test/scaffolding.dart'; @@ -25,7 +26,7 @@ main() { group('performSubtypeConstraintGenerationForFunctionTypes:', () { test('Matching functions with no parameters', () { - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForFunctionTypes( Type('void Function()'), Type('void Function()'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -35,7 +36,7 @@ main() { group('Matching functions with positional parameters:', () { test('None optional', () { - var tcg = _TypeConstraintGatherer({'T', 'U'}); + var tcg = TypeConstraintGatherer({'T', 'U'}); check(tcg.performSubtypeConstraintGenerationForFunctionTypes( Type('void Function(int, String)'), Type('void Function(T, U)'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -44,7 +45,7 @@ main() { }); test('Some optional on LHS', () { - var tcg = _TypeConstraintGatherer({'T', 'U'}); + var tcg = TypeConstraintGatherer({'T', 'U'}); check(tcg.performSubtypeConstraintGenerationForFunctionTypes( Type('void Function(int, [String])'), Type('void Function(T, U)'), @@ -57,7 +58,7 @@ main() { group('Non-matching functions with positional parameters:', () { test('Non-matching due to return types', () { - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForFunctionTypes( Type('int Function(int)'), Type('String Function(int)'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -66,7 +67,7 @@ main() { }); test('Non-matching due to parameter types', () { - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForFunctionTypes( Type('void Function(int)'), Type('void Function(String)'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -75,7 +76,7 @@ main() { }); test('Non-matching due to optional parameters on RHS', () { - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForFunctionTypes( Type('void Function()'), Type('void Function([int])'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -84,7 +85,7 @@ main() { }); test('Non-matching due to more parameters being required on LHS', () { - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForFunctionTypes( Type('void Function(int)'), Type('void Function([int])'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -95,7 +96,7 @@ main() { group('Matching functions with named parameters:', () { test('None optional', () { - var tcg = _TypeConstraintGatherer({'T', 'U'}); + var tcg = TypeConstraintGatherer({'T', 'U'}); check(tcg.performSubtypeConstraintGenerationForFunctionTypes( Type('void Function({required int x, required String y})'), Type('void Function({required T x, required U y})'), @@ -106,7 +107,7 @@ main() { }); test('Some optional on LHS', () { - var tcg = _TypeConstraintGatherer({'T', 'U'}); + var tcg = TypeConstraintGatherer({'T', 'U'}); check(tcg.performSubtypeConstraintGenerationForFunctionTypes( Type('void Function({required int x, String y})'), Type('void Function({required T x, required U y})'), @@ -117,7 +118,7 @@ main() { }); test('Optional named parameter on LHS', () { - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForFunctionTypes( Type('void Function(int, {String x})'), Type('void Function(T)'), @@ -128,7 +129,7 @@ main() { }); test('Extra optional named parameter on LHS', () { - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForFunctionTypes( Type('void Function({String x, int y})'), Type('void Function({T y})'), @@ -141,7 +142,7 @@ main() { group('Non-matching functions with named parameters:', () { test('Non-matching due to return types', () { - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForFunctionTypes( Type('int Function({int x})'), Type('String Function({int x})'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -150,7 +151,7 @@ main() { }); test('Non-matching due to named parameter types', () { - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForFunctionTypes( Type('void Function({int x})'), Type('void Function({String x})'), @@ -161,7 +162,7 @@ main() { }); test('Non-matching due to required named parameter on LHS', () { - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForFunctionTypes( Type('void Function({required int x})'), Type('void Function()'), @@ -172,7 +173,7 @@ main() { }); test('Non-matching due to optional named parameter on RHS', () { - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForFunctionTypes( Type('void Function()'), Type('void Function({int x})'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -182,7 +183,7 @@ main() { test('Non-matching due to named parameter on RHS, with decoys on LHS', () { - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForFunctionTypes( Type('void Function({int x, int y})'), Type('void Function({int z})'), @@ -194,7 +195,7 @@ main() { }); test('Matching functions with named and positional parameters', () { - var tcg = _TypeConstraintGatherer({'T', 'U'}); + var tcg = TypeConstraintGatherer({'T', 'U'}); check(tcg.performSubtypeConstraintGenerationForFunctionTypes( Type('void Function(int, {String y})'), Type('void Function(T, {U y})'), @@ -208,7 +209,7 @@ main() { test( 'Non-matching due to LHS not accepting optional positional parameter', () { - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForFunctionTypes( Type('void Function(int, {String x})'), Type('void Function(int, [String])'), @@ -219,7 +220,7 @@ main() { }); test('Non-matching due to positional parameter length mismatch', () { - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForFunctionTypes( Type('void Function(int, {String x})'), Type('void Function(int, String)'), @@ -233,7 +234,7 @@ main() { group('performSubtypeConstraintGenerationForRecordTypes:', () { test('Matching empty records', () { - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForRecordTypes( Type('()'), Type('()'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -243,7 +244,7 @@ main() { group('Matching records:', () { test('Without named parameters', () { - var tcg = _TypeConstraintGatherer({'T', 'U'}); + var tcg = TypeConstraintGatherer({'T', 'U'}); check(tcg.performSubtypeConstraintGenerationForRecordTypes( Type('(int, String)'), Type('(T, U)'), leftSchema: true, astNodeForTesting: Node.placeholder())) @@ -252,7 +253,7 @@ main() { }); test('With named parameters', () { - var tcg = _TypeConstraintGatherer({'T', 'U'}); + var tcg = TypeConstraintGatherer({'T', 'U'}); check(tcg.performSubtypeConstraintGenerationForRecordTypes( Type('(int, {String foo})'), Type('(T, {U foo})'), leftSchema: true, astNodeForTesting: Node.placeholder())) @@ -263,7 +264,7 @@ main() { group('Non-matching records without named parameters:', () { test('Non-matching due to positional types', () { - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForRecordTypes( Type('(int,)'), Type('(String,)'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -272,7 +273,7 @@ main() { }); test('Non-matching due to parameter numbers', () { - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForFunctionTypes( Type('()'), Type('(int,)'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -281,7 +282,7 @@ main() { }); test('Non-matching due to more parameters on LHS', () { - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForFunctionTypes( Type('(int,)'), Type('()'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -292,7 +293,7 @@ main() { group('Matching records with named parameters:', () { test('No type parameter occurrences', () { - var tcg = _TypeConstraintGatherer({'T', 'U'}); + var tcg = TypeConstraintGatherer({'T', 'U'}); check(tcg.performSubtypeConstraintGenerationForRecordTypes( Type('({int x, String y})'), Type('({int x, String y})'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -301,7 +302,7 @@ main() { }); test('Type parameters in RHS', () { - var tcg = _TypeConstraintGatherer({'T', 'U'}); + var tcg = TypeConstraintGatherer({'T', 'U'}); check(tcg.performSubtypeConstraintGenerationForRecordTypes( Type('({int x, String y})'), Type('({T x, U y})'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -310,7 +311,7 @@ main() { }); test('Type parameters in LHS', () { - var tcg = _TypeConstraintGatherer({'T', 'U'}); + var tcg = TypeConstraintGatherer({'T', 'U'}); check(tcg.performSubtypeConstraintGenerationForRecordTypes( Type('({T x, U y})'), Type('({int x, String y})'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -321,7 +322,7 @@ main() { group('Matching records with named parameters:', () { test('No type parameter occurrences', () { - var tcg = _TypeConstraintGatherer({'T', 'U'}); + var tcg = TypeConstraintGatherer({'T', 'U'}); check(tcg.performSubtypeConstraintGenerationForRecordTypes( Type('({int x, String y})'), Type('({int x, String y})'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -330,7 +331,7 @@ main() { }); test('Type parameters in RHS', () { - var tcg = _TypeConstraintGatherer({'T', 'U'}); + var tcg = TypeConstraintGatherer({'T', 'U'}); check(tcg.performSubtypeConstraintGenerationForRecordTypes( Type('({int x, String y})'), Type('({T x, U y})'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -339,7 +340,7 @@ main() { }); test('Type parameters in LHS', () { - var tcg = _TypeConstraintGatherer({'T', 'U'}); + var tcg = TypeConstraintGatherer({'T', 'U'}); check(tcg.performSubtypeConstraintGenerationForRecordTypes( Type('({T x, U y})'), Type('({int x, String y})'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -350,7 +351,7 @@ main() { group('Non-matching records with named parameters:', () { test('Non-matching due to positional parameter numbers', () { - var tcg = _TypeConstraintGatherer({'T', 'U'}); + var tcg = TypeConstraintGatherer({'T', 'U'}); check(tcg.performSubtypeConstraintGenerationForRecordTypes( Type('(num, num, {T x, U y})'), Type('(num, {int x, String y})'), @@ -361,7 +362,7 @@ main() { }); test('Non-matching due to named parameter numbers', () { - var tcg = _TypeConstraintGatherer({'T', 'U'}); + var tcg = TypeConstraintGatherer({'T', 'U'}); check(tcg.performSubtypeConstraintGenerationForRecordTypes( Type('({T x, U y, num z})'), Type('({int x, String y})'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -370,7 +371,7 @@ main() { }); test('Non-matching due to named parameter names', () { - var tcg = _TypeConstraintGatherer({'T', 'U'}); + var tcg = TypeConstraintGatherer({'T', 'U'}); check(tcg.performSubtypeConstraintGenerationForRecordTypes( Type('(num, {T x, U y})'), Type('(num, {int x, String x2})'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -383,7 +384,7 @@ main() { group('performSubtypeConstraintGenerationForFutureOr:', () { test('FutureOr matches FutureOr with constraints based on arguments', () { // `FutureOr <# FutureOr` reduces to `T <# int` - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForRightFutureOr( Type('FutureOr'), Type('FutureOr'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -394,7 +395,7 @@ main() { test('FutureOr does not match FutureOr because arguments fail to match', () { // `FutureOr <# FutureOr` reduces to `int <# String` - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForRightFutureOr( Type('FutureOr'), Type('FutureOr'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -410,7 +411,7 @@ main() { // producing `Future <: T` // In cases where both branches produce a constraint, the "Future" branch // is favored. - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForRightFutureOr( Type('Future'), Type('FutureOr'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -426,7 +427,7 @@ main() { // producing `Future<_> <: T` // In cases where only one branch produces a constraint, that branch is // favored. - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForRightFutureOr( Type('Future<_>'), Type('FutureOr'), leftSchema: true, astNodeForTesting: Node.placeholder())) @@ -442,7 +443,7 @@ main() { // producing `T <: int` // In cases where both branches produce a constraint, the "Future" branch // is favored. - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForRightFutureOr( Type('T'), Type('FutureOr'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -451,7 +452,7 @@ main() { }); test('Testing FutureOr as the lower bound of the constraint', () { - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForLeftFutureOr( Type('FutureOr'), Type('dynamic'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -463,7 +464,7 @@ main() { // `FutureOr <# Q` if `Future <# Q` and `P0 <# Q`. This test case // verifies that if `Future <# Q` matches but `P0 <# Q` does not, then // the match fails. - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForLeftFutureOr( Type('FutureOr<(T,)>'), Type('Future<(int,)>'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -472,7 +473,7 @@ main() { }); test('Testing nested FutureOr as the lower bound of the constraint', () { - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForLeftFutureOr( Type('FutureOr>'), Type('FutureOr'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -483,7 +484,7 @@ main() { test('Future matches FutureOr with no constraints', () { // `Future <# FutureOr` matches (taking the "Future" branch of // the FutureOr) without generating any constraints. - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForRightFutureOr( Type('Future'), Type('FutureOr'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -495,7 +496,7 @@ main() { // `List <# FutureOr>` could only match by taking the // "non-Future" branch of the FutureOr, so the constraint `T <: int` is // produced. - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForRightFutureOr( Type('List'), Type('FutureOr>'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -505,7 +506,7 @@ main() { group('Nullable FutureOr on RHS:', () { test('Does not match, according to spec', () { - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForRightFutureOr( Type('FutureOr'), Type('FutureOr?'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -514,7 +515,7 @@ main() { }); test('Matches, according to CFE discrepancy', () { - var tcg = _TypeConstraintGatherer({'T'}, + var tcg = TypeConstraintGatherer({'T'}, enableDiscrepantObliviousnessOfNullabilitySuffixOfFutureOr: true); check(tcg.performSubtypeConstraintGenerationForRightFutureOr( Type('FutureOr'), Type('FutureOr?'), @@ -526,7 +527,7 @@ main() { group('Nullable FutureOr on LHS:', () { test('Does not match, according to spec', () { - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForRightFutureOr( Type('FutureOr?'), Type('FutureOr'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -535,7 +536,7 @@ main() { }); test('Matches, according to CFE discrepancy', () { - var tcg = _TypeConstraintGatherer({'T'}, + var tcg = TypeConstraintGatherer({'T'}, enableDiscrepantObliviousnessOfNullabilitySuffixOfFutureOr: true); check(tcg.performSubtypeConstraintGenerationForRightFutureOr( Type('FutureOr?'), Type('FutureOr'), @@ -549,7 +550,7 @@ main() { group('performSubtypeConstraintGenerationForLeftNullableType:', () { test('Nullable matches nullable with constraints based on base types', () { // `T? <# int?` reduces to `T <# int?` - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForLeftNullableType( Type('T?'), Type('Null'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -560,7 +561,7 @@ main() { test('Nullable does not match Nullable because base types fail to match', () { // `int? <# String?` reduces to `int <# String` - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForLeftNullableType( Type('int?'), Type('String?'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -570,7 +571,7 @@ main() { test('Nullable does not match non-nullable', () { // `(int, T)? <# (int, String)` does not match - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForLeftNullableType( Type('(int, T)?'), Type('(int, String)'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -579,7 +580,7 @@ main() { }); test('Both LHS and RHS nullable, matching', () { - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForRightNullableType( Type('T?'), Type('int?'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -588,7 +589,7 @@ main() { }); test('Both LHS and RHS nullable, not matching', () { - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForRightNullableType( Type('(T, int)?'), Type('(int, String)?'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -606,7 +607,7 @@ main() { // producing `Null <: T` // In cases where both branches produce a constraint, the "non-Null" // branch is favored. - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForRightNullableType( Type('Null'), Type('T?'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -622,7 +623,7 @@ main() { // producing `T <: int` // In cases where both branches produce a constraint, the "non-Null" // branch is favored. - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForRightNullableType( Type('T'), Type('int?'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -633,7 +634,7 @@ main() { test('Null matches Nullable with no constraints', () { // `Null <# int?` matches (taking the "Null" branch of // the Nullable) without generating any constraints. - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForRightNullableType( Type('Null'), Type('int?'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -642,7 +643,7 @@ main() { }); test('Dynamic matches Object?', () { - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForRightNullableType( Type('dynamic'), Type('Object?'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -651,7 +652,7 @@ main() { }); test('void matches Object?', () { - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForRightNullableType( Type('void'), Type('Object?'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -660,7 +661,7 @@ main() { }); test('LHS not nullable, matches with no constraints', () { - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForRightNullableType( Type('int'), Type('int?'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -672,7 +673,7 @@ main() { group('performSubtypeConstraintGenerationForTypeDeclarationTypes', () { group('Same base type on both sides:', () { test('Covariant, matching', () { - var tcg = _TypeConstraintGatherer({'T', 'U'}); + var tcg = TypeConstraintGatherer({'T', 'U'}); check(tcg.performSubtypeConstraintGenerationForTypeDeclarationTypes( Type('Map'), Type('Map'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -681,7 +682,7 @@ main() { }); test('Covariant, not matching', () { - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForTypeDeclarationTypes( Type('Map'), Type('Map'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -690,7 +691,7 @@ main() { }); test('Contravariant, matching', () { - var tcg = _TypeConstraintGatherer({'T', 'U'}); + var tcg = TypeConstraintGatherer({'T', 'U'}); tcg.typeAnalyzerOperations.addVariance( 'Contravariant', [Variance.contravariant, Variance.contravariant]); check(tcg.performSubtypeConstraintGenerationForTypeDeclarationTypes( @@ -701,7 +702,7 @@ main() { }); test('Contravariant, not matching', () { - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); tcg.typeAnalyzerOperations.addVariance( 'Contravariant', [Variance.contravariant, Variance.contravariant]); check(tcg.performSubtypeConstraintGenerationForTypeDeclarationTypes( @@ -714,7 +715,7 @@ main() { }); test('Invariant, matching', () { - var tcg = _TypeConstraintGatherer({'T', 'U'}); + var tcg = TypeConstraintGatherer({'T', 'U'}); tcg.typeAnalyzerOperations .addVariance('Invariant', [Variance.invariant, Variance.invariant]); check(tcg.performSubtypeConstraintGenerationForTypeDeclarationTypes( @@ -726,7 +727,7 @@ main() { }); test('Invariant, not matching', () { - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); tcg.typeAnalyzerOperations .addVariance('Invariant', [Variance.invariant, Variance.invariant]); check(tcg.performSubtypeConstraintGenerationForTypeDeclarationTypes( @@ -739,7 +740,7 @@ main() { test('Unrelated, matchable', () { // When the variance is "unrelated", type inference doesn't even try to // match up the type parameters; they are always considered to match. - var tcg = _TypeConstraintGatherer({'T', 'U'}); + var tcg = TypeConstraintGatherer({'T', 'U'}); tcg.typeAnalyzerOperations .addVariance('Unrelated', [Variance.unrelated, Variance.unrelated]); check(tcg.performSubtypeConstraintGenerationForTypeDeclarationTypes( @@ -752,7 +753,7 @@ main() { test('Unrelated, not matchable', () { // When the variance is "unrelated", type inference doesn't even try to // match up the type parameters; they are always considered to match. - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); tcg.typeAnalyzerOperations .addVariance('Unrelated', [Variance.unrelated, Variance.unrelated]); check(tcg.performSubtypeConstraintGenerationForTypeDeclarationTypes( @@ -765,7 +766,7 @@ main() { group('Related types on both sides:', () { test('No change in type args', () { - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForTypeDeclarationTypes( Type('List'), Type('Iterable'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -774,7 +775,7 @@ main() { }); test('Change in type args', () { - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForTypeDeclarationTypes( Type('MyListOfInt'), Type('List'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -787,7 +788,7 @@ main() { // performSubtypeConstraintGenerationForTypeDeclarationTypes considers // it not to match (this is handled by other parts of the subtyping // algorithm) - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForTypeDeclarationTypes( Type('List?'), Type('Iterable'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -800,7 +801,7 @@ main() { // performSubtypeConstraintGenerationForTypeDeclarationTypes considers // it not to match (this is handled by other parts of the subtyping // algorithm) - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationForTypeDeclarationTypes( Type('List'), Type('Iterable?'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -810,7 +811,7 @@ main() { }); test('Non-interface type on LHS', () { - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForTypeDeclarationTypes( Type('void Function()'), Type('int'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -819,7 +820,7 @@ main() { }); test('Non-interface type on RHS', () { - var tcg = _TypeConstraintGatherer({}); + var tcg = TypeConstraintGatherer({}); check(tcg.performSubtypeConstraintGenerationForTypeDeclarationTypes( Type('int'), Type('void Function()'), leftSchema: false, astNodeForTesting: Node.placeholder())) @@ -830,7 +831,7 @@ main() { group('matchTypeParameterBoundInternal', () { test('Non-promoted parameter on LHS', () { - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationInternal( TypeParameterType(TypeRegistry.addTypeParameter('X') ..explicitBound = Type('Future')), @@ -842,7 +843,7 @@ main() { }); test('Promoted parameter on LHS', () { - var tcg = _TypeConstraintGatherer({'T'}); + var tcg = TypeConstraintGatherer({'T'}); check(tcg.performSubtypeConstraintGenerationInternal( TypeParameterType( TypeRegistry.addTypeParameter('X') @@ -857,7 +858,7 @@ main() { }); } -class _TypeConstraintGatherer extends TypeConstraintGenerator with TypeConstraintGeneratorMixin[]; - _TypeConstraintGatherer(Set typeVariablesBeingConstrained, + TypeConstraintGatherer(Set typeVariablesBeingConstrained, {this.enableDiscrepantObliviousnessOfNullabilitySuffixOfFutureOr = false}) : super(inferenceUsingBoundsIsEnabled: false) { for (var typeVariableName in typeVariablesBeingConstrained) { @@ -949,4 +950,12 @@ class _TypeConstraintGatherer extends TypeConstraintGenerator> + computeConstraints() { + // TODO(cstefantsova): implement computeConstraints + throw UnimplementedError(); + } } diff --git a/pkg/analyzer/lib/src/dart/analysis/testing_data.dart b/pkg/analyzer/lib/src/dart/analysis/testing_data.dart index 706e67de9162..fb93ab933b31 100644 --- a/pkg/analyzer/lib/src/dart/analysis/testing_data.dart +++ b/pkg/analyzer/lib/src/dart/analysis/testing_data.dart @@ -39,10 +39,14 @@ class TestingData { void recordTypeConstraintGenerationDataForTesting( Uri uri, TypeConstraintGenerationDataForTesting result) { TypeConstraintGenerationDataForTesting? existing = + // ignore: analyzer_use_new_elements uriToTypeConstraintGenerationData[uri]; + // ignore: analyzer_use_new_elements if (existing != null) { + // ignore: analyzer_use_new_elements existing.mergeIn(result); } else { + // ignore: analyzer_use_new_elements uriToTypeConstraintGenerationData[uri] = result; } } diff --git a/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart b/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart index d97a01706c44..aa57418d9719 100644 --- a/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart +++ b/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart @@ -6,20 +6,12 @@ import 'dart:math' as math; import 'package:_fe_analyzer_shared/src/type_inference/shared_inference_log.dart'; import 'package:_fe_analyzer_shared/src/types/shared_type.dart'; -import 'package:analyzer/dart/ast/ast.dart' - show - Annotation, - AsExpression, - AstNode, - ConstructorName, - Expression, - InvocationExpression, - SimpleIdentifier; import 'package:analyzer/dart/ast/syntactic_entity.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/element2.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/error/listener.dart' show ErrorReporter; +import 'package:analyzer/src/dart/ast/ast.dart'; import 'package:analyzer/src/dart/ast/extensions.dart'; import 'package:analyzer/src/dart/element/element.dart'; import 'package:analyzer/src/dart/element/type.dart'; @@ -154,7 +146,7 @@ class GenericInferrer { /// type is a subtype of the [parameterType]. void constrainArgument( DartType argumentType, DartType parameterType, String parameterName, - {InterfaceElement? genericClass, required AstNode? nodeForTesting}) { + {InterfaceElement? genericClass, required AstNodeImpl? nodeForTesting}) { var origin = TypeConstraintFromArgument( argumentType: SharedTypeView(argumentType), parameterType: SharedTypeView(parameterType), @@ -175,7 +167,7 @@ class GenericInferrer { {InterfaceElement? genericClass, required List parameters, required List argumentTypes, - required AstNode? nodeForTesting}) { + required AstNodeImpl? nodeForTesting}) { for (int i = 0; i < argumentTypes.length; i++) { // Try to pass each argument to each parameter, recording any type // parameter bounds that were implied by this assignment. @@ -193,7 +185,7 @@ class GenericInferrer { /// [contextType]. void constrainGenericFunctionInContext( FunctionType fnType, DartType contextType, - {required AstNode? nodeForTesting}) { + {required AstNodeImpl? nodeForTesting}) { var origin = TypeConstraintFromFunctionContext( functionType: fnType, contextType: contextType); @@ -217,7 +209,7 @@ class GenericInferrer { /// Apply a return type constraint, which asserts that the [declaredType] /// is a subtype of the [contextType]. void constrainReturnType(DartType declaredType, DartType contextType, - {required AstNode? nodeForTesting}) { + {required AstNodeImpl? nodeForTesting}) { var origin = TypeConstraintFromReturnType( declaredType: declaredType, contextType: contextType); inferenceLogWriter?.enterConstraintGeneration( @@ -426,13 +418,10 @@ class GenericInferrer { /// If [isContravariant] is `true`, then we are solving for a contravariant /// type parameter which means we choose the upper bound rather than the /// lower bound for normally covariant type parameters. - DartType _chooseTypeFromConstraints( - Iterable constraints, - {bool toKnownType = false, - required bool isContravariant}) { - var (:lower, :upper) = - _computeLowerAndUpperBoundsOfConstraints(constraints); - + DartType _chooseTypeFromConstraint(MergedTypeConstraint constraint, + {bool toKnownType = false, required bool isContravariant}) { + DartType upper = constraint.upper.unwrapTypeSchemaView(); + DartType lower = constraint.lower.unwrapTypeSchemaView(); // Prefer the known bound, if any. // Otherwise take whatever bound has partial information, e.g. `Iterable` // @@ -476,7 +465,7 @@ class GenericInferrer { _typeFormals.length, UnknownInferredType.instance); var inferencePhaseConstraints = { for (var typeParameter in _constraints.keys) - typeParameter: [...?_constraints[typeParameter]] + typeParameter: _squashConstraints(_constraints[typeParameter]!) }; for (int i = 0; i < _typeFormals.length; i++) { // TODO(kallentu): : Clean up TypeParameterElementImpl casting once @@ -495,24 +484,24 @@ class GenericInferrer { ); } - var constraints = inferencePhaseConstraints[typeParam]!; + var constraint = inferencePhaseConstraints[typeParam]!; var previouslyInferredType = _typesInferredSoFar[typeParam]; if (previouslyInferredType != null) { inferredTypes[i] = previouslyInferredType; } else if (preliminary) { var inferredType = _inferTypeParameterFromContext( - constraints, extendsClause, + constraint, extendsClause, isContravariant: typeParam.variance.isContravariant, typeParameterToInfer: typeParam, inferencePhaseConstraints: inferencePhaseConstraints); + inferredTypes[i] = inferredType; if (typeParam.isLegacyCovariant && UnknownInferredType.isKnown(inferredType)) { _typesInferredSoFar[typeParam] = inferredType; } } else { - inferredTypes[i] = _inferTypeParameterFromAll( - constraints, extendsClause, + inferredTypes[i] = _inferTypeParameterFromAll(constraint, extendsClause, isContravariant: typeParam.variance.isContravariant, typeParameterToInfer: typeParam, inferencePhaseConstraints: inferencePhaseConstraints); @@ -522,32 +511,6 @@ class GenericInferrer { return inferredTypes; } - ({DartType lower, DartType upper}) _computeLowerAndUpperBoundsOfConstraints( - Iterable constraints) { - DartType lower = UnknownInferredType.instance; - DartType upper = UnknownInferredType.instance; - for (var constraint in constraints) { - // Given constraints: - // - // L1 <: T <: U1 - // L2 <: T <: U2 - // - // These can be combined to produce: - // - // LUB(L1, L2) <: T <: GLB(U1, U2). - // - // This can then be done for all constraints in sequence. - // - // This resulting constraint may be unsatisfiable; in that case inference - // will fail. - upper = _typeSystem.greatestLowerBound( - upper, constraint.upper.unwrapTypeSchemaView()); - lower = _typeSystem.leastUpperBound( - lower, constraint.lower.unwrapTypeSchemaView()); - } - return (lower: lower, upper: upper); - } - void _demoteTypes(List types) { for (var i = 0; i < types.length; i++) { types[i] = _typeSystem.demoteType(types[i]); @@ -593,52 +556,52 @@ class GenericInferrer { 'Consider passing explicit type argument(s) to the generic.\n\n'; } - DartType _inferTypeParameterFromAll(List constraints, - MergedTypeConstraint? extendsClause, + DartType _inferTypeParameterFromAll( + MergedTypeConstraint constraint, MergedTypeConstraint? extendsClause, {required bool isContravariant, required TypeParameterElementImpl2 typeParameterToInfer, - required Map> + required Map inferencePhaseConstraints}) { if (extendsClause != null) { - var (:lower, upper: _) = - _computeLowerAndUpperBoundsOfConstraints(constraints); - MergedTypeConstraint? boundConstraint; if (inferenceUsingBoundsIsEnabled) { - if (!identical(lower, UnknownInferredType.instance)) { - boundConstraint = _mergeInConstraintsFromBound( + if (!identical(constraint.lower.unwrapTypeSchemaView(), + UnknownInferredType.instance)) { + boundConstraint = _typeSystemOperations.mergeInConstraintsFromBound( typeParameterToInfer: typeParameterToInfer, - lower: lower, - inferencePhaseConstraints: inferencePhaseConstraints); + typeParametersToInfer: _typeFormals, + lower: constraint.lower.unwrapTypeSchemaView(), + inferencePhaseConstraints: inferencePhaseConstraints, + dataForTesting: dataForTesting, + inferenceUsingBoundsIsEnabled: inferenceUsingBoundsIsEnabled); } } - constraints = [ - ...constraints, + constraint = _squashConstraints([ + constraint, extendsClause, if (boundConstraint != null && !boundConstraint.isEmpty(_typeSystemOperations)) boundConstraint - ]; + ]); } - var choice = _chooseTypeFromConstraints(constraints, + var choice = _chooseTypeFromConstraint(constraint, toKnownType: true, isContravariant: isContravariant); return choice; } DartType _inferTypeParameterFromContext( - Iterable constraints, - MergedTypeConstraint? extendsClause, + MergedTypeConstraint constraint, MergedTypeConstraint? extendsClause, {required bool isContravariant, required TypeParameterElementImpl2 typeParameterToInfer, - required Map> + required Map inferencePhaseConstraints}) { // Both bits of the bound information should be available at the same time. assert(extendsClause == null || typeParameterToInfer.bound != null); - DartType t = _chooseTypeFromConstraints(constraints, - isContravariant: isContravariant); + DartType t = + _chooseTypeFromConstraint(constraint, isContravariant: isContravariant); if (UnknownInferredType.isUnknown(t)) { return t; } @@ -651,83 +614,33 @@ class GenericInferrer { // // If we consider the `T extends num` we conclude ``, which works. if (extendsClause != null) { - var (:lower, upper: _) = - _computeLowerAndUpperBoundsOfConstraints(constraints); - MergedTypeConstraint? boundConstraint; if (inferenceUsingBoundsIsEnabled) { - if (!identical(lower, UnknownInferredType.instance)) { - boundConstraint = _mergeInConstraintsFromBound( + if (!identical(constraint.lower.unwrapTypeSchemaView(), + UnknownInferredType.instance)) { + boundConstraint = _typeSystemOperations.mergeInConstraintsFromBound( typeParameterToInfer: typeParameterToInfer, - lower: lower, - inferencePhaseConstraints: inferencePhaseConstraints); + typeParametersToInfer: _typeFormals, + lower: constraint.lower.unwrapTypeSchemaView(), + inferencePhaseConstraints: inferencePhaseConstraints, + dataForTesting: dataForTesting, + inferenceUsingBoundsIsEnabled: inferenceUsingBoundsIsEnabled); } } - constraints = [ - ...constraints, + constraint = _squashConstraints([ + constraint, extendsClause, if (boundConstraint != null && !boundConstraint.isEmpty(_typeSystemOperations)) boundConstraint - ]; - return _chooseTypeFromConstraints(constraints, + ]); + return _chooseTypeFromConstraint(constraint, isContravariant: isContravariant); } return t; } - MergedTypeConstraint _mergeInConstraintsFromBound( - {required TypeParameterElementImpl2 typeParameterToInfer, - required DartType lower, - required Map> - inferencePhaseConstraints}) { - // The type parameter's bound may refer to itself (or other type - // parameters), so we might have to create an additional constraint. - // Consider this example from - // https://github.com/dart-lang/language/issues/3009: - // - // class A> {} - // class B extends A {} - // class C extends B {} - // void f>(X x) {} - // void main() { - // f(C()); // should infer f(C()). - // } - // - // In order for `f(C())` to be inferred as `f(C())`, we need to - // generate the constraint `X <: B`. To do this, we first take the lower - // constraint we've accumulated so far (which, in this example, is `C`, - // due to the presence of the actual argument `C()`), and use subtype - // constraint generation to match it against the explicit bound (which - // is `A`; hence we perform `C <# A`). If this produces any - // constraints (i.e. `X <: B` in this example), then they are added to - // the set of constraints just before choosing the final type. - - DartType typeParameterToInferBound = typeParameterToInfer.bound!; - TypeConstraintGatherer typeConstraintGatherer = TypeConstraintGatherer( - typeSystemOperations: _typeSystemOperations, - typeParameters: _typeFormals, - inferenceUsingBoundsIsEnabled: inferenceUsingBoundsIsEnabled, - dataForTesting: null); - typeConstraintGatherer.performSubtypeConstraintGenerationInternal( - lower, typeParameterToInferBound, - leftSchema: true, astNodeForTesting: null); - var constraintsPerTypeVariable = - typeConstraintGatherer.computeConstraints(); - for (var typeParameter in constraintsPerTypeVariable.keys) { - var constraint = constraintsPerTypeVariable[typeParameter]!; - constraint.origin = TypeConstraintFromExtendsClause( - typeParameterName: typeParameterToInfer.name3, - boundType: SharedTypeView(typeParameterToInferBound), - extendsType: SharedTypeView(typeParameterToInferBound)); - if (!constraint.isEmpty(_typeSystemOperations)) { - (inferencePhaseConstraints[typeParameter] ??= []).add(constraint); - } - } - return constraintsPerTypeVariable[typeParameterToInfer]!; - } - /// Reports an inference failure on [errorEntity] according to its type. void _reportInferenceFailure({ ErrorReporter? errorReporter, @@ -809,13 +722,44 @@ class GenericInferrer { } } + MergedTypeConstraint _squashConstraints( + Iterable constraints) { + DartType lower = UnknownInferredType.instance; + DartType upper = UnknownInferredType.instance; + TypeConstraintOrigin origin = UnknownTypeConstraintOrigin(); + + for (var constraint in constraints) { + // Given constraints: + // + // L1 <: T <: U1 + // L2 <: T <: U2 + // + // These can be combined to produce: + // + // LUB(L1, L2) <: T <: GLB(U1, U2). + // + // This can then be done for all constraints in sequence. + // + // This resulting constraint may be unsatisfiable; in that case inference + // will fail. + upper = _typeSystem.greatestLowerBound( + upper, constraint.upper.unwrapTypeSchemaView()); + lower = _typeSystem.leastUpperBound( + lower, constraint.lower.unwrapTypeSchemaView()); + } + return MergedTypeConstraint( + lower: SharedTypeSchemaView(lower), + upper: SharedTypeSchemaView(upper), + origin: origin); + } + /// Tries to make [t1] a subtype of [t2] and accumulate constraints as needed. /// /// The return value indicates whether the match was successful. If it was /// unsuccessful, any constraints that were accumulated during the match /// attempt have been rewound. bool _tryMatchSubtypeOf(DartType t1, DartType t2, TypeConstraintOrigin origin, - {required bool covariant, required AstNode? nodeForTesting}) { + {required bool covariant, required AstNodeImpl? nodeForTesting}) { var gatherer = TypeConstraintGatherer( typeParameters: _typeParameters, typeSystemOperations: _typeSystemOperations, diff --git a/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart b/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart index 2d4829eadab5..7dc445ddc97a 100644 --- a/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart +++ b/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart @@ -13,6 +13,7 @@ import 'package:_fe_analyzer_shared/src/type_inference/type_constraint.dart' show GeneratedTypeConstraint, MergedTypeConstraint, + TypeConstraintGenerationDataForTesting, TypeConstraintFromArgument, TypeConstraintFromExtendsClause, TypeConstraintFromFunctionContext, @@ -74,6 +75,10 @@ typedef TypeConstraintFromReturnType = shared.TypeConstraintFromReturnType< InterfaceTypeImpl, InterfaceElementImpl2>; +typedef TypeConstraintGenerationDataForTesting + = shared.TypeConstraintGenerationDataForTesting; + /// Instance of [shared.TypeConstraintOrigin] specific to the Analyzer. typedef TypeConstraintOrigin = shared.TypeConstraintOrigin< DartType, @@ -99,7 +104,7 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator< TypeParameterElementImpl2, InterfaceTypeImpl, InterfaceElementImpl2, - AstNode> + AstNodeImpl> with shared.TypeConstraintGeneratorMixin< DartType, @@ -108,7 +113,7 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator< TypeParameterElementImpl2, InterfaceTypeImpl, InterfaceElementImpl2, - AstNode> { + AstNodeImpl> { @override final Set typeParametersToConstrain = Set.identity(); @@ -142,7 +147,7 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator< @override void addLowerConstraintForParameter( TypeParameterElementImpl2 element, DartType lower, - {required AstNode? astNodeForTesting}) { + {required AstNodeImpl? astNodeForTesting}) { GeneratedTypeConstraint generatedTypeConstraint = GeneratedTypeConstraint.lower(element, SharedTypeSchemaView(lower)); _constraints.add(generatedTypeConstraint); @@ -155,7 +160,7 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator< @override void addUpperConstraintForParameter( TypeParameterElementImpl2 element, DartType upper, - {required AstNode? astNodeForTesting}) { + {required AstNodeImpl? astNodeForTesting}) { GeneratedTypeConstraint generatedTypeConstraint = GeneratedTypeConstraint.upper(element, SharedTypeSchemaView(upper)); _constraints.add(generatedTypeConstraint); @@ -165,7 +170,7 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator< } } - /// Returns the set of type constraints that was gathered. + @override Map computeConstraints() { var result = {}; for (var parameter in typeParametersToConstrain) { @@ -190,7 +195,7 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator< void eliminateTypeParametersInGeneratedConstraints( covariant List eliminator, shared.TypeConstraintGeneratorState eliminationStartState, - {required AstNode? astNodeForTesting}) { + {required AstNodeImpl? astNodeForTesting}) { var constraints = _constraints.sublist(eliminationStartState.count); _constraints.length = eliminationStartState.count; for (var constraint in constraints) { @@ -269,31 +274,3 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator< _constraints.length = state.count; } } - -/// Data structure maintaining intermediate type inference results, such as -/// type constraints, for testing purposes. Under normal execution, no -/// instance of this class should be created. -class TypeConstraintGenerationDataForTesting { - /// Map from nodes requiring type inference to the generated type constraints - /// for the node. - final Map> generatedTypeConstraints = - {}; - - /// Merges [other] into the receiver, combining the constraints. - /// - /// The method reuses data structures from [other] whenever possible, to - /// avoid extra memory allocations. This process is destructive to [other] - /// because the changes made to the reused structures will be visible to - /// [other]. - void mergeIn(TypeConstraintGenerationDataForTesting other) { - for (AstNode node in other.generatedTypeConstraints.keys) { - List? constraints = - generatedTypeConstraints[node]; - if (constraints != null) { - constraints.addAll(other.generatedTypeConstraints[node]!); - } else { - generatedTypeConstraints[node] = other.generatedTypeConstraints[node]!; - } - } - } -} diff --git a/pkg/analyzer/lib/src/dart/element/type_system.dart b/pkg/analyzer/lib/src/dart/element/type_system.dart index fa47fa879c61..0bd61895d5bd 100644 --- a/pkg/analyzer/lib/src/dart/element/type_system.dart +++ b/pkg/analyzer/lib/src/dart/element/type_system.dart @@ -13,6 +13,7 @@ import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/dart/element/type_provider.dart'; import 'package:analyzer/dart/element/type_system.dart'; import 'package:analyzer/error/listener.dart' show ErrorReporter; +import 'package:analyzer/src/dart/ast/ast.dart' show AstNodeImpl; import 'package:analyzer/src/dart/element/element.dart'; import 'package:analyzer/src/dart/element/extensions.dart'; import 'package:analyzer/src/dart/element/generic_inferrer.dart'; @@ -644,7 +645,7 @@ class TypeSystemImpl implements TypeSystem { required bool strictInference, required bool strictCasts, required TypeConstraintGenerationDataForTesting? dataForTesting, - required AstNode? nodeForTesting, + required AstNodeImpl? nodeForTesting, }) { if (contextType.typeFormals.isNotEmpty || fnType.typeFormals.isEmpty) { return const []; @@ -1687,7 +1688,7 @@ class TypeSystemImpl implements TypeSystem { required bool strictCasts, required TypeSystemOperations typeSystemOperations, required TypeConstraintGenerationDataForTesting? dataForTesting, - required AstNode? nodeForTesting, + required AstNodeImpl? nodeForTesting, }) { // Create a GenericInferrer that will allow certain type parameters to be // inferred. It will optimistically assume these type parameters can be diff --git a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart index f1e34f7f0253..06a31d09c6b2 100644 --- a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart +++ b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart @@ -337,7 +337,7 @@ class ExtensionMemberResolver { List? _inferTypeArguments( ExtensionOverride node, DartType receiverType, {required TypeConstraintGenerationDataForTesting? dataForTesting, - required AstNode? nodeForTesting}) { + required AstNodeImpl? nodeForTesting}) { var element = node.element2; var typeParameters = element.typeParameters2; var typeArguments = node.typeArguments; 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 b11638848720..8a09f40e2d9a 100644 --- a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart +++ b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart @@ -22,6 +22,7 @@ import 'package:analyzer/src/dart/ast/ast.dart'; import 'package:analyzer/src/dart/ast/extensions.dart'; import 'package:analyzer/src/dart/element/element.dart'; import 'package:analyzer/src/dart/element/type.dart'; +import 'package:analyzer/src/dart/element/type_constraint_gatherer.dart'; import 'package:analyzer/src/dart/element/type_schema.dart'; import 'package:analyzer/src/dart/element/type_system.dart' show TypeSystemImpl; import 'package:analyzer/src/generated/inference_log.dart'; @@ -166,7 +167,8 @@ class FlowAnalysisHelper { /// the top level declaration. This is used to compute assigned variables /// information within the body or initializer. If `null`, the entire [node] /// will be visited. - void bodyOrInitializer_enter(AstNode node, FormalParameterList? parameters, + void bodyOrInitializer_enter( + AstNodeImpl node, FormalParameterList? parameters, {void Function(AstVisitor visitor)? visit}) { inferenceLogWriter?.enterBodyOrInitializer(node); assert(flow == null); @@ -349,7 +351,8 @@ class FlowAnalysisHelper { /// Computes the [AssignedVariables] map for the given [node]. static AssignedVariables - computeAssignedVariables(AstNode node, FormalParameterList? parameters, + computeAssignedVariables( + AstNodeImpl node, FormalParameterList? parameters, {bool retainDataForTesting = false, void Function(AstVisitor visitor)? visit}) { AssignedVariables assignedVariables = @@ -507,6 +510,28 @@ class TypeSystemOperations } } + @override + TypeConstraintGenerator< + DartType, + FormalParameterElementOrMember, + PromotableElementImpl2, + TypeParameterElementImpl2, + InterfaceTypeImpl, + InterfaceElementImpl2, + AstNodeImpl> + createTypeConstraintGenerator( + {required covariant TypeConstraintGenerationDataForTesting? + typeConstraintGenerationDataForTesting, + required List typeParametersToInfer, + required covariant TypeSystemOperations typeAnalyzerOperations, + required bool inferenceUsingBoundsIsEnabled}) { + return TypeConstraintGatherer( + typeParameters: typeParametersToInfer, + inferenceUsingBoundsIsEnabled: inferenceUsingBoundsIsEnabled, + typeSystemOperations: typeAnalyzerOperations, + dataForTesting: typeConstraintGenerationDataForTesting); + } + @override SharedTypeView extensionTypeErasure(SharedTypeView type) { return SharedTypeView(type.unwrapTypeView().extensionTypeErasure); diff --git a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart index 25d8a7cf98d2..3497b07be786 100644 --- a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart +++ b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart @@ -120,8 +120,8 @@ class InvocationInferenceHelper { /// Given an uninstantiated generic function type, referenced by the /// [identifier] in the tear-off [expression], try to infer the instantiated /// generic function type from the surrounding context. - DartType inferTearOff(Expression expression, SimpleIdentifierImpl identifier, - DartType tearOffType, + DartType inferTearOff(ExpressionImpl expression, + SimpleIdentifierImpl identifier, DartType tearOffType, {required DartType contextType}) { if (contextType is FunctionType && tearOffType is FunctionType) { var typeArguments = _typeSystem.inferFunctionTypeInstantiation( diff --git a/pkg/analyzer/lib/src/dart/resolver/named_type_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/named_type_resolver.dart index 1a5b52fe2c2b..45feb8b39cac 100644 --- a/pkg/analyzer/lib/src/dart/resolver/named_type_resolver.dart +++ b/pkg/analyzer/lib/src/dart/resolver/named_type_resolver.dart @@ -171,7 +171,7 @@ class NamedTypeResolver with ScopeHelpers { /// [enclosingClass]. InterfaceType _inferRedirectedConstructor(InterfaceElement element, {required TypeConstraintGenerationDataForTesting? dataForTesting, - required AstNode? nodeForTesting}) { + required AstNodeImpl? nodeForTesting}) { if (element == enclosingClass) { return element.thisType; } else { @@ -200,7 +200,7 @@ class NamedTypeResolver with ScopeHelpers { } } - DartType _instantiateElement(NamedType node, Element element, + DartType _instantiateElement(NamedTypeImpl node, Element element, {required TypeConstraintGenerationDataForTesting? dataForTesting}) { var nullability = _getNullability(node); diff --git a/pkg/analyzer/lib/src/dart/resolver/typed_literal_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/typed_literal_resolver.dart index a7a1f5450eab..71cc13e7802f 100644 --- a/pkg/analyzer/lib/src/dart/resolver/typed_literal_resolver.dart +++ b/pkg/analyzer/lib/src/dart/resolver/typed_literal_resolver.dart @@ -425,7 +425,7 @@ class TypedLiteralResolver { } } - GenericInferrer _inferListTypeDownwards(ListLiteral node, + GenericInferrer _inferListTypeDownwards(ListLiteralImpl node, {required DartType contextType}) { var element = _typeProvider.listElement2; var typeParameters = element.typeParameters2; @@ -493,7 +493,7 @@ class TypedLiteralResolver { } GenericInferrer _inferMapTypeDownwards( - SetOrMapLiteral node, DartType contextType) { + SetOrMapLiteralImpl node, DartType contextType) { var element = _typeProvider.mapElement2; inferenceLogWriter?.enterGenericInference( // TODO(paulberry): make this cast unnecessary by changing @@ -601,7 +601,7 @@ class TypedLiteralResolver { } GenericInferrer _inferSetTypeDownwards( - SetOrMapLiteral node, DartType contextType) { + SetOrMapLiteralImpl node, DartType contextType) { var element = _typeProvider.setElement2; inferenceLogWriter?.enterGenericInference( // TODO(paulberry): make this cast unnecessary by changing @@ -737,7 +737,7 @@ class TypedLiteralResolver { DartType _toMapType( GenericInferrer? inferrer, _LiteralResolution literalResolution, - SetOrMapLiteral node, + SetOrMapLiteralImpl node, List<_InferredCollectionElementTypeInformation> inferredTypes) { inferenceLogWriter?.assertGenericInferenceState( inProgress: inferrer != null); @@ -787,7 +787,7 @@ class TypedLiteralResolver { DartType _toSetType( GenericInferrer? inferrer, _LiteralResolution literalResolution, - SetOrMapLiteral node, + SetOrMapLiteralImpl node, List<_InferredCollectionElementTypeInformation> inferredTypes) { inferenceLogWriter?.assertGenericInferenceState( inProgress: inferrer != null); diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart index d167b5d0ff89..55321335b676 100644 --- a/pkg/analyzer/lib/src/generated/resolver.dart +++ b/pkg/analyzer/lib/src/generated/resolver.dart @@ -2763,7 +2763,7 @@ class ResolverVisitor extends ThrowingAstVisitor } @override - void visitFormalParameterList(FormalParameterList node) { + void visitFormalParameterList(covariant FormalParameterListImpl node) { // Formal parameter lists can contain default values, which in turn contain // expressions, so we need flow analysis to be available to process those // expressions. @@ -4059,7 +4059,7 @@ class ResolverVisitor extends ThrowingAstVisitor required AstNode errorNode, required DartType declaredType, required DartType contextType, - required AstNode? nodeForTesting, + required AstNodeImpl? nodeForTesting, }) { inferenceLogWriter?.enterGenericInference(typeParameters, declaredType); var inferrer = GenericInferrer( diff --git a/pkg/analyzer/lib/src/summary2/ast_resolver.dart b/pkg/analyzer/lib/src/summary2/ast_resolver.dart index dcdf5feb81f1..c66daf5d6117 100644 --- a/pkg/analyzer/lib/src/summary2/ast_resolver.dart +++ b/pkg/analyzer/lib/src/summary2/ast_resolver.dart @@ -107,7 +107,7 @@ class AstResolver { node = getNode(); node.accept(_scopeResolverVisitor); _prepareEnclosingDeclarations(); - _flowAnalysis.bodyOrInitializer_enter(node.parent!, null); + _flowAnalysis.bodyOrInitializer_enter(node.parent as AstNodeImpl, null); _resolverVisitor.analyzeExpression(node, SharedTypeSchemaView(contextType)); _resolverVisitor.popRewrite(); _resolverVisitor.checkIdle(); diff --git a/pkg/front_end/lib/src/type_inference/type_constraint_gatherer.dart b/pkg/front_end/lib/src/type_inference/type_constraint_gatherer.dart index 26b4b0f2012f..a809b1c10765 100644 --- a/pkg/front_end/lib/src/type_inference/type_constraint_gatherer.dart +++ b/pkg/front_end/lib/src/type_inference/type_constraint_gatherer.dart @@ -186,7 +186,7 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator< return _environment.getTypeArgumentsAsInstanceOf(type, typeDeclaration); } - /// Returns the set of type constraints that was gathered. + @override Map computeConstraints() { Map result = {}; for (StructuralParameter parameter in typeParametersToConstrain) { 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 5149be20b446..872f68d83778 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 @@ -7,6 +7,8 @@ import 'package:_fe_analyzer_shared/src/type_inference/assigned_variables.dart'; import 'package:_fe_analyzer_shared/src/type_inference/nullability_suffix.dart'; import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer_operations.dart' hide Variance; +import 'package:_fe_analyzer_shared/src/type_inference/type_constraint.dart' + as shared; import 'package:_fe_analyzer_shared/src/types/shared_type.dart'; import 'package:kernel/ast.dart'; import 'package:kernel/class_hierarchy.dart' @@ -28,11 +30,11 @@ import '../source/source_constructor_builder.dart'; import '../source/source_library_builder.dart' show FieldNonPromotabilityInfo, SourceLibraryBuilder; import 'factor_type.dart'; +import 'type_constraint_gatherer.dart'; import 'type_inferrer.dart'; import 'type_schema.dart'; import 'type_schema_elimination.dart' as type_schema_elimination; -import 'type_schema_environment.dart' - show GeneratedTypeConstraint, TypeSchemaEnvironment; +import 'type_schema_environment.dart' show TypeSchemaEnvironment; /// Visitor to check whether a given type mentions any of a class's type /// parameters in a non-covariant fashion. @@ -392,7 +394,10 @@ class TypeInferenceEngineImpl extends TypeInferenceEngine { } } -class InferenceDataForTesting { +// TODO(cstefantsova): Merge with [TypeInferenceResultForTesting]. +class InferenceDataForTesting + extends shared.TypeConstraintGenerationDataForTesting { final FlowAnalysisResult flowAnalysisResult = new FlowAnalysisResult(); final TypeInferenceResultForTesting typeInferenceResult = @@ -1010,12 +1015,31 @@ class OperationsCfe bool isNullableInternal(DartType type) { return type.nullability == Nullability.nullable; } + + @override + TypeConstraintGenerator + createTypeConstraintGenerator( + {required covariant TypeInferenceResultForTesting? + typeConstraintGenerationDataForTesting, + required List typeParametersToInfer, + required covariant OperationsCfe typeAnalyzerOperations, + required bool inferenceUsingBoundsIsEnabled}) { + // TODO(cstefantsova): Pass [typeConstraintGenerationDataForTesting] when + // [InferenceDataForTesting] is merged with [TypeInferenceResultForTesting]. + return new TypeConstraintGatherer( + typeAnalyzerOperations.typeEnvironment as TypeSchemaEnvironment, + typeParametersToInfer, + typeOperations: typeAnalyzerOperations, + inferenceResultForTesting: null, + inferenceUsingBoundsIsEnabled: inferenceUsingBoundsIsEnabled); + } } /// Type inference results used for testing. -class TypeInferenceResultForTesting { +class TypeInferenceResultForTesting + extends shared.TypeConstraintGenerationDataForTesting { final Map> inferredTypeArguments = {}; - final Map> generatedTypeConstraints = - {}; final Map inferredVariableTypes = {}; } 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 94d5bf11b1e0..ad06832528ac 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 @@ -187,6 +187,7 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment List? previouslyInferredTypes, {bool preliminary = false, required OperationsCfe operations, + required InferenceDataForTesting? dataForTesting, required bool inferenceUsingBoundsIsEnabled}) { List inferredTypes = previouslyInferredTypes?.toList(growable: false) ?? @@ -197,53 +198,10 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment DartType typeParamBound = typeParam.bound; DartType? extendsConstraint; - Map? constraintsFromBound; if (!hasOmittedBound(typeParam)) { extendsConstraint = new FunctionTypeInstantiator.fromIterables( typeParametersToInfer, inferredTypes) .substitute(typeParamBound); - - MergedTypeConstraint? mergedTypeConstraint = constraints[typeParam]; - if (inferenceUsingBoundsIsEnabled) { - // The type parameter's bound may refer to itself (or other type - // parameters), so we might have to create an additional constraint. - // Consider this example from - // https://github.com/dart-lang/language/issues/3009: - // - // class A> {} - // class B extends A {} - // class C extends B {} - // void f>(X x) {} - // void main() { - // f(C()); // should infer f(C()). - // } - // - // In order for `f(C())` to be inferred as `f(C())`, we need to - // generate the constraint `X <: B`. To do this, we first take the - // lower constraint we've accumulated so far (which, in this example, - // is `C`, due to the presence of the actual argument `C()`), and use - // subtype constraint generation to match it against the explicit - // bound (which is `A`; hence we perform `C <# A`). If this - // produces any constraints (i.e. `X <: B` in this example), then they - // are added to the set of constraints just before choosing the final - // type. - - if (mergedTypeConstraint != null && - mergedTypeConstraint.lower - is! SharedUnknownTypeSchemaView) { - TypeConstraintGatherer extendsConstraintGatherer = - new TypeConstraintGatherer(this, typeParametersToInfer, - typeOperations: operations, - inferenceUsingBoundsIsEnabled: - inferenceUsingBoundsIsEnabled, - inferenceResultForTesting: null); - extendsConstraintGatherer.tryConstrainLower(typeParamBound, - mergedTypeConstraint.lower.unwrapTypeSchemaView(), - treeNodeForTesting: null); - constraintsFromBound = - extendsConstraintGatherer.computeConstraints(); - } - } } MergedTypeConstraint constraint = constraints[typeParam]!; @@ -252,10 +210,11 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment previouslyInferredTypes?[i], constraint, extendsConstraint, isLegacyCovariant: typeParam.isLegacyCovariant, operations: operations, - constraintsFromBound: constraintsFromBound, constraints: constraints, typeParameterToInfer: typeParam, - typeParametersToInfer: typeParametersToInfer); + typeParametersToInfer: typeParametersToInfer, + dataForTesting: dataForTesting, + inferenceUsingBoundsIsEnabled: inferenceUsingBoundsIsEnabled); } else { inferredTypes[i] = _inferTypeParameterFromAll( previouslyInferredTypes?[i], constraint, extendsConstraint, @@ -263,10 +222,11 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment typeParam.variance == shared.Variance.contravariant, isLegacyCovariant: typeParam.isLegacyCovariant, operations: operations, - constraintsFromBound: constraintsFromBound, constraints: constraints, typeParameterToInfer: typeParam, - typeParametersToInfer: typeParametersToInfer); + typeParametersToInfer: typeParametersToInfer, + dataForTesting: dataForTesting, + inferenceUsingBoundsIsEnabled: inferenceUsingBoundsIsEnabled); } } @@ -490,6 +450,7 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment previouslyInferredTypes, preliminary: preliminary, operations: gatherer.typeOperations, + dataForTesting: dataForTesting, inferenceUsingBoundsIsEnabled: inferenceUsingBoundsIsEnabled); for (int i = 0; i < inferredTypes.length; i++) { @@ -503,11 +464,11 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment {bool isContravariant = false, bool isLegacyCovariant = true, required OperationsCfe operations, - required Map? - constraintsFromBound, required Map constraints, required StructuralParameter typeParameterToInfer, - required List typeParametersToInfer}) { + required List typeParametersToInfer, + required InferenceDataForTesting? dataForTesting, + required bool inferenceUsingBoundsIsEnabled}) { // See if we already fixed this type in a previous inference step. // If so, then we aren't allowed to change it unless [isLegacyCovariant] is // false. @@ -517,13 +478,21 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment return typeFromPreviousInference; } - if (constraintsFromBound != null) { - _mergeInConstraintsFromBound( - constraints: constraints, - constraintsFromBound: constraintsFromBound, - typeParametersToInfer: typeParametersToInfer, - operations: operations); - constraint = constraints[typeParameterToInfer]!; + if (inferenceUsingBoundsIsEnabled && + constraint.lower is! SharedUnknownTypeSchemaView && + !hasOmittedBound(typeParameterToInfer)) { + // Coverage-ignore-block(suite): Not run. + MergedTypeConstraint constraintFromBound = + operations.mergeInConstraintsFromBound( + typeParameterToInfer: typeParameterToInfer, + typeParametersToInfer: typeParametersToInfer, + lower: constraint.lower.unwrapTypeSchemaView(), + inferencePhaseConstraints: constraints, + dataForTesting: dataForTesting, + inferenceUsingBoundsIsEnabled: inferenceUsingBoundsIsEnabled); + + constraint.mergeInTypeSchemaUpper(constraintFromBound.upper, operations); + constraint.mergeInTypeSchemaLower(constraintFromBound.lower, operations); } if (extendsConstraint != null) { @@ -538,33 +507,15 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment isContravariant: isContravariant); } - void _mergeInConstraintsFromBound( - {required Map - constraintsFromBound, - required List typeParametersToInfer, - required Map constraints, - required OperationsCfe operations}) { - for (StructuralParameter typeParamToUpdate in typeParametersToInfer) { - MergedTypeConstraint? constraintFromBoundForTypeParam = - constraintsFromBound[typeParamToUpdate]; - if (constraintFromBoundForTypeParam != null) { - constraints[typeParamToUpdate]?.mergeInTypeSchemaUpper( - constraintFromBoundForTypeParam.upper, operations); - constraints[typeParamToUpdate]?.mergeInTypeSchemaLower( - constraintFromBoundForTypeParam.lower, operations); - } - } - } - DartType _inferTypeParameterFromContext(DartType? typeFromPreviousInference, MergedTypeConstraint constraint, DartType? extendsConstraint, {bool isLegacyCovariant = true, required OperationsCfe operations, - required Map? - constraintsFromBound, required Map constraints, required List typeParametersToInfer, - required StructuralParameter typeParameterToInfer}) { + required StructuralParameter typeParameterToInfer, + required InferenceDataForTesting? dataForTesting, + required bool inferenceUsingBoundsIsEnabled}) { // See if we already fixed this type in a previous inference step. // If so, then we aren't allowed to change it unless [isLegacyCovariant] is // false. @@ -588,14 +539,21 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment // // If we consider the `T extends num` we conclude ``, which works. - if (constraintsFromBound != null) { + if (inferenceUsingBoundsIsEnabled && + constraint.lower is! SharedUnknownTypeSchemaView && + !hasOmittedBound(typeParameterToInfer)) { // Coverage-ignore-block(suite): Not run. - _mergeInConstraintsFromBound( - constraintsFromBound: constraintsFromBound, - typeParametersToInfer: typeParametersToInfer, - constraints: constraints, - operations: operations); - constraint = constraints[typeParameterToInfer]!; + MergedTypeConstraint constraintFromBound = + operations.mergeInConstraintsFromBound( + typeParameterToInfer: typeParameterToInfer, + typeParametersToInfer: typeParametersToInfer, + lower: constraint.lower.unwrapTypeSchemaView(), + inferencePhaseConstraints: constraints, + dataForTesting: dataForTesting, + inferenceUsingBoundsIsEnabled: inferenceUsingBoundsIsEnabled); + + constraint.mergeInTypeSchemaUpper(constraintFromBound.upper, operations); + constraint.mergeInTypeSchemaLower(constraintFromBound.lower, operations); } if (extendsConstraint != null) { diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt index 4f6203e040f3..863a5b0c5283 100644 --- a/pkg/front_end/test/spell_checking_list_common.txt +++ b/pkg/front_end/test/spell_checking_list_common.txt @@ -105,6 +105,7 @@ all allocated allocating allocation +allocations allow allowed allowing 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 6af484dee556..b63208cd34d9 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 @@ -230,6 +230,7 @@ abstract class TypeSchemaEnvironmentTestBase { inferredTypeNodes, preliminary: downwardsInferPhase, inferenceUsingBoundsIsEnabled: true, + dataForTesting: null, operations: _operations); expect(inferredTypeNodes.single, expectedTypeNode);