Skip to content

Commit

Permalink
[analyzer][cfe] Share inference-using-bounds routines
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
chloestefantsova authored and Commit Queue committed Jan 9, 2025
1 parent a6b99af commit f49c620
Show file tree
Hide file tree
Showing 20 changed files with 484 additions and 353 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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<
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]!;
}
}
}
}
18 changes: 18 additions & 0 deletions pkg/_fe_analyzer_shared/test/mini_ast.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit f49c620

Please sign in to comment.