Skip to content

Commit f49c620

Browse files
chloestefantsovaCommit Queue
authored andcommitted
[analyzer][cfe] Share inference-using-bounds routines
Part of #54902 Change-Id: I12282c2492ed9f1220b47fcf8b0d73c93bbfc432 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/401660 Reviewed-by: Erik Ernst <[email protected]> Commit-Queue: Chloe Stefantsova <[email protected]>
1 parent a6b99af commit f49c620

20 files changed

+484
-353
lines changed

pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import '../flow_analysis/flow_analysis_operations.dart';
66
import '../types/shared_type.dart';
77
import 'nullability_suffix.dart';
8+
import 'type_constraint.dart';
89

910
/// Callback API used by the shared type analyzer to query and manipulate the
1011
/// client's representation of variables and types.
@@ -698,6 +699,40 @@ abstract interface class TypeAnalyzerOperations<
698699
/// Returns [type] suffixed with the [suffix].
699700
TypeStructure withNullabilitySuffixInternal(
700701
TypeStructure type, NullabilitySuffix suffix);
702+
703+
TypeConstraintGenerator<
704+
TypeStructure,
705+
SharedNamedFunctionParameterStructure<TypeStructure>,
706+
Variable,
707+
TypeParameterStructure,
708+
TypeDeclarationType,
709+
TypeDeclaration,
710+
Object>
711+
createTypeConstraintGenerator(
712+
{required TypeConstraintGenerationDataForTesting<TypeStructure,
713+
TypeParameterStructure, Variable, Object>?
714+
typeConstraintGenerationDataForTesting,
715+
required List<TypeParameterStructure> typeParametersToInfer,
716+
required TypeAnalyzerOperations<TypeStructure, Variable,
717+
TypeParameterStructure, TypeDeclarationType, TypeDeclaration>
718+
typeAnalyzerOperations,
719+
required bool inferenceUsingBoundsIsEnabled});
720+
721+
MergedTypeConstraint<TypeStructure, TypeParameterStructure, Variable,
722+
TypeDeclarationType, TypeDeclaration>
723+
mergeInConstraintsFromBound(
724+
{required TypeParameterStructure typeParameterToInfer,
725+
required List<TypeParameterStructure> typeParametersToInfer,
726+
required TypeStructure lower,
727+
required Map<
728+
TypeParameterStructure,
729+
MergedTypeConstraint<TypeStructure, TypeParameterStructure,
730+
Variable, TypeDeclarationType, TypeDeclaration>>
731+
inferencePhaseConstraints,
732+
required TypeConstraintGenerationDataForTesting<TypeStructure,
733+
TypeParameterStructure, Variable, Object>?
734+
dataForTesting,
735+
required bool inferenceUsingBoundsIsEnabled});
701736
}
702737

703738
mixin TypeAnalyzerOperationsMixin<
@@ -893,6 +928,96 @@ mixin TypeAnalyzerOperationsMixin<
893928
SharedTypeView<TypeStructure> type) {
894929
return new SharedTypeSchemaView(type.unwrapTypeView());
895930
}
931+
932+
@override
933+
MergedTypeConstraint<TypeStructure, TypeParameterStructure, Variable,
934+
TypeDeclarationType, TypeDeclaration>
935+
mergeInConstraintsFromBound(
936+
{required TypeParameterStructure typeParameterToInfer,
937+
required List<TypeParameterStructure> typeParametersToInfer,
938+
required TypeStructure lower,
939+
required Map<
940+
TypeParameterStructure,
941+
MergedTypeConstraint<TypeStructure, TypeParameterStructure,
942+
Variable, TypeDeclarationType, TypeDeclaration>>
943+
inferencePhaseConstraints,
944+
required TypeConstraintGenerationDataForTesting<TypeStructure,
945+
TypeParameterStructure, Variable, Object>?
946+
dataForTesting,
947+
required bool inferenceUsingBoundsIsEnabled}) {
948+
// The type parameter's bound may refer to itself (or other type
949+
// parameters), so we might have to create an additional constraint.
950+
// Consider this example from
951+
// https://github.com/dart-lang/language/issues/3009:
952+
//
953+
// class A<X extends A<X>> {}
954+
// class B extends A<B> {}
955+
// class C extends B {}
956+
// void f<X extends A<X>>(X x) {}
957+
// void main() {
958+
// f(C()); // should infer f<B>(C()).
959+
// }
960+
//
961+
// In order for `f(C())` to be inferred as `f<B>(C())`, we need to
962+
// generate the constraint `X <: B`. To do this, we first take the lower
963+
// constraint we've accumulated so far (which, in this example, is `C`,
964+
// due to the presence of the actual argument `C()`), and use subtype
965+
// constraint generation to match it against the explicit bound (which
966+
// is `A<X>`; hence we perform `C <# A<X>`). If this produces any
967+
// constraints (i.e. `X <: B` in this example), then they are added to
968+
// the set of constraints just before choosing the final type.
969+
970+
TypeStructure typeParameterToInferBound = typeParameterToInfer.bound!;
971+
972+
// TODO(cstefantsova): Pass [dataForTesting] when
973+
// [InferenceDataForTesting] is merged with [TypeInferenceResultForTesting].
974+
TypeConstraintGenerator<
975+
TypeStructure,
976+
SharedNamedFunctionParameterStructure<TypeStructure>,
977+
Variable,
978+
TypeParameterStructure,
979+
TypeDeclarationType,
980+
TypeDeclaration,
981+
Object> typeConstraintGatherer =
982+
createTypeConstraintGenerator(
983+
typeConstraintGenerationDataForTesting: null,
984+
typeParametersToInfer: typeParametersToInfer,
985+
typeAnalyzerOperations: this,
986+
inferenceUsingBoundsIsEnabled: inferenceUsingBoundsIsEnabled);
987+
typeConstraintGatherer.performSubtypeConstraintGenerationInternal(
988+
lower, typeParameterToInferBound,
989+
leftSchema: true, astNodeForTesting: null);
990+
Map<
991+
TypeParameterStructure,
992+
MergedTypeConstraint<
993+
TypeStructure,
994+
TypeParameterStructure,
995+
Variable,
996+
TypeDeclarationType,
997+
TypeDeclaration>> constraintsPerTypeVariable =
998+
typeConstraintGatherer.computeConstraints();
999+
for (TypeParameterStructure typeParameter
1000+
in constraintsPerTypeVariable.keys) {
1001+
MergedTypeConstraint<TypeStructure, TypeParameterStructure, Variable,
1002+
TypeDeclarationType, TypeDeclaration> constraint =
1003+
constraintsPerTypeVariable[typeParameter]!;
1004+
constraint.origin = new TypeConstraintFromExtendsClause(
1005+
typeParameterName: typeParameterToInfer.displayName,
1006+
boundType: new SharedTypeView(typeParameterToInferBound),
1007+
extendsType: new SharedTypeView(typeParameterToInferBound));
1008+
if (!constraint.isEmpty(this)) {
1009+
MergedTypeConstraint? constraintForParameter =
1010+
inferencePhaseConstraints[typeParameter];
1011+
if (constraintForParameter == null) {
1012+
inferencePhaseConstraints[typeParameter] = constraint;
1013+
} else {
1014+
constraintForParameter.mergeInTypeSchemaUpper(constraint.upper, this);
1015+
constraintForParameter.mergeInTypeSchemaLower(constraint.lower, this);
1016+
}
1017+
}
1018+
}
1019+
return constraintsPerTypeVariable[typeParameterToInfer]!;
1020+
}
8961021
}
8971022

8981023
/// Abstract interface of a type constraint generator.
@@ -1832,6 +1957,12 @@ abstract class TypeConstraintGenerator<
18321957

18331958
return false;
18341959
}
1960+
1961+
/// Returns the set of type constraints that was gathered.
1962+
Map<
1963+
TypeParameterStructure,
1964+
MergedTypeConstraint<TypeStructure, TypeParameterStructure, Variable,
1965+
TypeDeclarationType, TypeDeclaration>> computeConstraints();
18351966
}
18361967

18371968
mixin TypeConstraintGeneratorMixin<

pkg/_fe_analyzer_shared/lib/src/type_inference/type_constraint.dart

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,3 +365,41 @@ class UnknownTypeConstraintOrigin<
365365
return <String>[];
366366
}
367367
}
368+
369+
/// Data structure maintaining intermediate type inference results, such as
370+
/// type constraints, for testing purposes. Under normal execution, no
371+
/// instance of this class should be created.
372+
class TypeConstraintGenerationDataForTesting<
373+
TypeStructure extends SharedTypeStructure<TypeStructure>,
374+
TypeParameterStructure extends SharedTypeParameterStructure<TypeStructure>,
375+
Variable extends Object,
376+
AstNode extends Object> {
377+
/// Map from nodes requiring type inference to the generated type constraints
378+
/// for the node.
379+
final Map<
380+
AstNode,
381+
List<
382+
GeneratedTypeConstraint<TypeStructure, TypeParameterStructure,
383+
Variable>>> generatedTypeConstraints = {};
384+
385+
/// Merges [other] into the receiver, combining the constraints.
386+
///
387+
/// The method reuses data structures from [other] whenever possible, to
388+
/// avoid extra memory allocations. This process is destructive to [other]
389+
/// because the changes made to the reused structures will be visible to
390+
/// [other].
391+
void mergeIn(
392+
TypeConstraintGenerationDataForTesting<TypeStructure,
393+
TypeParameterStructure, Variable, AstNode>
394+
other) {
395+
for (AstNode node in other.generatedTypeConstraints.keys) {
396+
List<GeneratedTypeConstraint>? constraints =
397+
generatedTypeConstraints[node];
398+
if (constraints != null) {
399+
constraints.addAll(other.generatedTypeConstraints[node]!);
400+
} else {
401+
generatedTypeConstraints[node] = other.generatedTypeConstraints[node]!;
402+
}
403+
}
404+
}
405+
}

pkg/_fe_analyzer_shared/test/mini_ast.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,14 @@ import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer.dart'
2828
as shared;
2929
import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer.dart'
3030
hide MapPatternEntry, RecordPatternField;
31+
import 'package:_fe_analyzer_shared/src/type_inference/type_constraint.dart';
3132
import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer_operations.dart';
3233
import 'package:_fe_analyzer_shared/src/type_inference/variable_bindings.dart';
3334
import 'package:_fe_analyzer_shared/src/types/shared_type.dart';
3435
import 'package:test/test.dart';
3536

37+
import 'type_inference/type_constraint_gatherer_test.dart';
38+
3639
import 'mini_ir.dart';
3740
import 'mini_types.dart';
3841

@@ -3225,6 +3228,21 @@ class MiniAstOperations
32253228
Type withNullabilitySuffixInternal(Type type, NullabilitySuffix modifier) {
32263229
return type.withNullability(modifier);
32273230
}
3231+
3232+
@override
3233+
TypeConstraintGenerator<Type, NamedFunctionParameter, Var, TypeParameter,
3234+
Type, String, Node>
3235+
createTypeConstraintGenerator(
3236+
{required TypeConstraintGenerationDataForTesting?
3237+
typeConstraintGenerationDataForTesting,
3238+
required List<TypeParameter> typeParametersToInfer,
3239+
required TypeAnalyzerOperations<Type, Var, TypeParameter, Type,
3240+
String>
3241+
typeAnalyzerOperations,
3242+
required bool inferenceUsingBoundsIsEnabled}) {
3243+
return TypeConstraintGatherer(
3244+
{for (var typeParameter in typeParametersToInfer) typeParameter.name});
3245+
}
32283246
}
32293247

32303248
/// Representation of an expression or statement in the pseudo-Dart language

0 commit comments

Comments
 (0)