[analyzer][cfe] Share inference-using-bounds routines

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

Change-Id: I12282c2492ed9f1220b47fcf8b0d73c93bbfc432
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/401660
Reviewed-by: Erik Ernst <eernst@google.com>
Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
This commit is contained in:
Chloe Stefantsova
2025-01-09 03:13:06 -08:00
committed by Commit Queue
parent a6b99afdea
commit f49c620ab3
20 changed files with 484 additions and 353 deletions
@@ -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<TypeStructure>,
Variable,
TypeParameterStructure,
TypeDeclarationType,
TypeDeclaration,
Object>
createTypeConstraintGenerator(
{required TypeConstraintGenerationDataForTesting<TypeStructure,
TypeParameterStructure, Variable, Object>?
typeConstraintGenerationDataForTesting,
required List<TypeParameterStructure> typeParametersToInfer,
required TypeAnalyzerOperations<TypeStructure, Variable,
TypeParameterStructure, TypeDeclarationType, TypeDeclaration>
typeAnalyzerOperations,
required bool inferenceUsingBoundsIsEnabled});
MergedTypeConstraint<TypeStructure, TypeParameterStructure, Variable,
TypeDeclarationType, TypeDeclaration>
mergeInConstraintsFromBound(
{required TypeParameterStructure typeParameterToInfer,
required List<TypeParameterStructure> typeParametersToInfer,
required TypeStructure lower,
required Map<
TypeParameterStructure,
MergedTypeConstraint<TypeStructure, TypeParameterStructure,
Variable, TypeDeclarationType, TypeDeclaration>>
inferencePhaseConstraints,
required TypeConstraintGenerationDataForTesting<TypeStructure,
TypeParameterStructure, Variable, Object>?
dataForTesting,
required bool inferenceUsingBoundsIsEnabled});
}
mixin TypeAnalyzerOperationsMixin<
@@ -893,6 +928,96 @@ mixin TypeAnalyzerOperationsMixin<
SharedTypeView<TypeStructure> type) {
return new SharedTypeSchemaView(type.unwrapTypeView());
}
@override
MergedTypeConstraint<TypeStructure, TypeParameterStructure, Variable,
TypeDeclarationType, TypeDeclaration>
mergeInConstraintsFromBound(
{required TypeParameterStructure typeParameterToInfer,
required List<TypeParameterStructure> typeParametersToInfer,
required TypeStructure lower,
required Map<
TypeParameterStructure,
MergedTypeConstraint<TypeStructure, TypeParameterStructure,
Variable, TypeDeclarationType, TypeDeclaration>>
inferencePhaseConstraints,
required TypeConstraintGenerationDataForTesting<TypeStructure,
TypeParameterStructure, Variable, Object>?
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<X extends A<X>> {}
// class B extends A<B> {}
// class C extends B {}
// void f<X extends A<X>>(X x) {}
// void main() {
// f(C()); // should infer f<B>(C()).
// }
//
// In order for `f(C())` to be inferred as `f<B>(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<X>`; hence we perform `C <# A<X>`). 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<TypeStructure>,
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<TypeStructure, TypeParameterStructure, Variable,
TypeDeclarationType, TypeDeclaration> 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<TypeStructure, TypeParameterStructure, Variable,
TypeDeclarationType, TypeDeclaration>> computeConstraints();
}
mixin TypeConstraintGeneratorMixin<
@@ -365,3 +365,41 @@ class UnknownTypeConstraintOrigin<
return <String>[];
}
}
/// 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<TypeStructure>,
TypeParameterStructure extends SharedTypeParameterStructure<TypeStructure>,
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<TypeStructure, TypeParameterStructure,
Variable>>> 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<TypeStructure,
TypeParameterStructure, Variable, AstNode>
other) {
for (AstNode node in other.generatedTypeConstraints.keys) {
List<GeneratedTypeConstraint>? constraints =
generatedTypeConstraints[node];
if (constraints != null) {
constraints.addAll(other.generatedTypeConstraints[node]!);
} else {
generatedTypeConstraints[node] = other.generatedTypeConstraints[node]!;
}
}
}
}
@@ -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<Type, NamedFunctionParameter, Var, TypeParameter,
Type, String, Node>
createTypeConstraintGenerator(
{required TypeConstraintGenerationDataForTesting?
typeConstraintGenerationDataForTesting,
required List<TypeParameter> typeParametersToInfer,
required TypeAnalyzerOperations<Type, Var, TypeParameter, Type,
String>
typeAnalyzerOperations,
required bool inferenceUsingBoundsIsEnabled}) {
return TypeConstraintGatherer(
{for (var typeParameter in typeParametersToInfer) typeParameter.name});
}
}
/// Representation of an expression or statement in the pseudo-Dart language
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer_operations.dart';
import 'package:_fe_analyzer_shared/src/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<T> <# FutureOr<int>` reduces to `T <# int`
var tcg = _TypeConstraintGatherer({'T'});
var tcg = TypeConstraintGatherer({'T'});
check(tcg.performSubtypeConstraintGenerationForRightFutureOr(
Type('FutureOr<T>'), Type('FutureOr<int>'),
leftSchema: false, astNodeForTesting: Node.placeholder()))
@@ -394,7 +395,7 @@ main() {
test('FutureOr does not match FutureOr because arguments fail to match',
() {
// `FutureOr<int> <# FutureOr<String>` reduces to `int <# String`
var tcg = _TypeConstraintGatherer({});
var tcg = TypeConstraintGatherer({});
check(tcg.performSubtypeConstraintGenerationForRightFutureOr(
Type('FutureOr<int>'), Type('FutureOr<String>'),
leftSchema: false, astNodeForTesting: Node.placeholder()))
@@ -410,7 +411,7 @@ main() {
// producing `Future<int> <: 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<int>'), Type('FutureOr<T>'),
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<T>'),
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<int>'),
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<T>'), Type('dynamic'),
leftSchema: false, astNodeForTesting: Node.placeholder()))
@@ -463,7 +464,7 @@ main() {
// `FutureOr<P0> <# Q` if `Future<P0> <# Q` and `P0 <# Q`. This test case
// verifies that if `Future<P0> <# 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<FutureOr<T>>'), Type('FutureOr<dynamic>'),
leftSchema: false, astNodeForTesting: Node.placeholder()))
@@ -483,7 +484,7 @@ main() {
test('Future matches FutureOr with no constraints', () {
// `Future<int> <# FutureOr<int>` matches (taking the "Future" branch of
// the FutureOr) without generating any constraints.
var tcg = _TypeConstraintGatherer({});
var tcg = TypeConstraintGatherer({});
check(tcg.performSubtypeConstraintGenerationForRightFutureOr(
Type('Future<int>'), Type('FutureOr<int>'),
leftSchema: false, astNodeForTesting: Node.placeholder()))
@@ -495,7 +496,7 @@ main() {
// `List<T> <# FutureOr<List<int>>` 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<T>'), Type('FutureOr<List<int>>'),
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<T>'), Type('FutureOr<int>?'),
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<T>'), Type('FutureOr<int>?'),
@@ -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<T>?'), Type('FutureOr<int>'),
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<T>?'), Type('FutureOr<int>'),
@@ -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<T, U>'), Type('Map<int, String>'),
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<T, int>'), Type('Map<int, String>'),
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<T>'), Type('Iterable<int>'),
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<T>'),
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<T>?'), Type('Iterable<int>'),
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<T>'), Type('Iterable<int>?'),
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<String>')),
@@ -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<Type,
class TypeConstraintGatherer extends TypeConstraintGenerator<Type,
NamedFunctionParameter, Var, TypeParameter, Type, String, Node>
with
TypeConstraintGeneratorMixin<Type, NamedFunctionParameter, Var,
@@ -873,7 +874,7 @@ class _TypeConstraintGatherer extends TypeConstraintGenerator<Type,
final _constraints = <String>[];
_TypeConstraintGatherer(Set<String> typeVariablesBeingConstrained,
TypeConstraintGatherer(Set<String> typeVariablesBeingConstrained,
{this.enableDiscrepantObliviousnessOfNullabilitySuffixOfFutureOr = false})
: super(inferenceUsingBoundsIsEnabled: false) {
for (var typeVariableName in typeVariablesBeingConstrained) {
@@ -949,4 +950,12 @@ class _TypeConstraintGatherer extends TypeConstraintGenerator<Type,
// TODO(paulberry): implement instantiateFunctionTypesAndProvideEliminator
throw UnimplementedError();
}
@override
Map<TypeParameter,
MergedTypeConstraint<Type, TypeParameter, Var, Type, String>>
computeConstraints() {
// TODO(cstefantsova): implement computeConstraints
throw UnimplementedError();
}
}
@@ -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;
}
}
@@ -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<ParameterElement> parameters,
required List<DartType> 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<MergedTypeConstraint> 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<MergedTypeConstraint> 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<DartType> 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<MergedTypeConstraint> constraints,
MergedTypeConstraint? extendsClause,
DartType _inferTypeParameterFromAll(
MergedTypeConstraint constraint, MergedTypeConstraint? extendsClause,
{required bool isContravariant,
required TypeParameterElementImpl2 typeParameterToInfer,
required Map<TypeParameterElementImpl2, List<MergedTypeConstraint>>
required Map<TypeParameterElementImpl2, MergedTypeConstraint>
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<MergedTypeConstraint> constraints,
MergedTypeConstraint? extendsClause,
MergedTypeConstraint constraint, MergedTypeConstraint? extendsClause,
{required bool isContravariant,
required TypeParameterElementImpl2 typeParameterToInfer,
required Map<TypeParameterElementImpl2, List<MergedTypeConstraint>>
required Map<TypeParameterElementImpl2, MergedTypeConstraint>
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 `<num>`, 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<TypeParameterElementImpl2, List<MergedTypeConstraint>>
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<X extends A<X>> {}
// class B extends A<B> {}
// class C extends B {}
// void f<X extends A<X>>(X x) {}
// void main() {
// f(C()); // should infer f<B>(C()).
// }
//
// In order for `f(C())` to be inferred as `f<B>(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<X>`; hence we perform `C <# A<X>`). 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<MergedTypeConstraint> 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,
@@ -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<DartType,
TypeParameterElementImpl2, PromotableElementImpl2, AstNodeImpl>;
/// 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<TypeParameterElementImpl2> 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<TypeParameterElementImpl2, MergedTypeConstraint> computeConstraints() {
var result = <TypeParameterElementImpl2, MergedTypeConstraint>{};
for (var parameter in typeParametersToConstrain) {
@@ -190,7 +195,7 @@ class TypeConstraintGatherer extends shared.TypeConstraintGenerator<
void eliminateTypeParametersInGeneratedConstraints(
covariant List<TypeParameterElementImpl2> 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<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<GeneratedTypeConstraint>? constraints =
generatedTypeConstraints[node];
if (constraints != null) {
constraints.addAll(other.generatedTypeConstraints[node]!);
} else {
generatedTypeConstraints[node] = other.generatedTypeConstraints[node]!;
}
}
}
}
@@ -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 <DartType>[];
@@ -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
@@ -337,7 +337,7 @@ class ExtensionMemberResolver {
List<DartType>? _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;
@@ -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<Object?> visitor)? visit}) {
inferenceLogWriter?.enterBodyOrInitializer(node);
assert(flow == null);
@@ -349,7 +351,8 @@ class FlowAnalysisHelper {
/// Computes the [AssignedVariables] map for the given [node].
static AssignedVariables<AstNodeImpl, PromotableElementImpl2>
computeAssignedVariables(AstNode node, FormalParameterList? parameters,
computeAssignedVariables(
AstNodeImpl node, FormalParameterList? parameters,
{bool retainDataForTesting = false,
void Function(AstVisitor<Object?> visitor)? visit}) {
AssignedVariables<AstNodeImpl, PromotableElementImpl2> assignedVariables =
@@ -507,6 +510,28 @@ class TypeSystemOperations
}
}
@override
TypeConstraintGenerator<
DartType,
FormalParameterElementOrMember,
PromotableElementImpl2,
TypeParameterElementImpl2,
InterfaceTypeImpl,
InterfaceElementImpl2,
AstNodeImpl>
createTypeConstraintGenerator(
{required covariant TypeConstraintGenerationDataForTesting?
typeConstraintGenerationDataForTesting,
required List<TypeParameterElementImpl2> typeParametersToInfer,
required covariant TypeSystemOperations typeAnalyzerOperations,
required bool inferenceUsingBoundsIsEnabled}) {
return TypeConstraintGatherer(
typeParameters: typeParametersToInfer,
inferenceUsingBoundsIsEnabled: inferenceUsingBoundsIsEnabled,
typeSystemOperations: typeAnalyzerOperations,
dataForTesting: typeConstraintGenerationDataForTesting);
}
@override
SharedTypeView<DartType> extensionTypeErasure(SharedTypeView<DartType> type) {
return SharedTypeView(type.unwrapTypeView().extensionTypeErasure);
@@ -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(
@@ -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);
@@ -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);
+2 -2
View File
@@ -2763,7 +2763,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
}
@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<void>
required AstNode errorNode,
required DartType declaredType,
required DartType contextType,
required AstNode? nodeForTesting,
required AstNodeImpl? nodeForTesting,
}) {
inferenceLogWriter?.enterGenericInference(typeParameters, declaredType);
var inferrer = GenericInferrer(
@@ -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();
@@ -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<StructuralParameter, MergedTypeConstraint> computeConstraints() {
Map<StructuralParameter, MergedTypeConstraint> result = {};
for (StructuralParameter parameter in typeParametersToConstrain) {
@@ -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<DartType,
StructuralParameter, VariableDeclaration, TreeNode> {
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<DartType, NamedType, VariableDeclaration,
StructuralParameter, TypeDeclarationType, TypeDeclaration, TreeNode>
createTypeConstraintGenerator(
{required covariant TypeInferenceResultForTesting?
typeConstraintGenerationDataForTesting,
required List<StructuralParameter> 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<DartType,
StructuralParameter, VariableDeclaration, TreeNode> {
final Map<TreeNode, List<DartType>> inferredTypeArguments = {};
final Map<TreeNode, List<GeneratedTypeConstraint>> generatedTypeConstraints =
{};
final Map<TreeNode, DartType> inferredVariableTypes = {};
}
@@ -187,6 +187,7 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment
List<DartType>? previouslyInferredTypes,
{bool preliminary = false,
required OperationsCfe operations,
required InferenceDataForTesting? dataForTesting,
required bool inferenceUsingBoundsIsEnabled}) {
List<DartType> inferredTypes =
previouslyInferredTypes?.toList(growable: false) ??
@@ -197,53 +198,10 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment
DartType typeParamBound = typeParam.bound;
DartType? extendsConstraint;
Map<StructuralParameter, MergedTypeConstraint>? 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<X extends A<X>> {}
// class B extends A<B> {}
// class C extends B {}
// void f<X extends A<X>>(X x) {}
// void main() {
// f(C()); // should infer f<B>(C()).
// }
//
// In order for `f(C())` to be inferred as `f<B>(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<X>`; hence we perform `C <# A<X>`). 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<DartType>) {
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<StructuralParameter, MergedTypeConstraint>?
constraintsFromBound,
required Map<StructuralParameter, MergedTypeConstraint> constraints,
required StructuralParameter typeParameterToInfer,
required List<StructuralParameter> typeParametersToInfer}) {
required List<StructuralParameter> 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<DartType> &&
!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<StructuralParameter, MergedTypeConstraint>
constraintsFromBound,
required List<StructuralParameter> typeParametersToInfer,
required Map<StructuralParameter, MergedTypeConstraint> 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<StructuralParameter, MergedTypeConstraint>?
constraintsFromBound,
required Map<StructuralParameter, MergedTypeConstraint> constraints,
required List<StructuralParameter> 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 `<num>`, which works.
if (constraintsFromBound != null) {
if (inferenceUsingBoundsIsEnabled &&
constraint.lower is! SharedUnknownTypeSchemaView<DartType> &&
!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) {
@@ -105,6 +105,7 @@ all
allocated
allocating
allocation
allocations
allow
allowed
allowing
@@ -230,6 +230,7 @@ abstract class TypeSchemaEnvironmentTestBase {
inferredTypeNodes,
preliminary: downwardsInferPhase,
inferenceUsingBoundsIsEnabled: true,
dataForTesting: null,
operations: _operations);
expect(inferredTypeNodes.single, expectedTypeNode);