Skip to content

Commit 6b4878b

Browse files
stereotype441Commit Queue
authored andcommitted
Front end: initial implementation of type inference for object patterns.
This code handles simple cases. Still TBD: - Object patterns that refer to typedefs - Object patterns that refer to classes with f-bounded polymorphism - Inference of `dynamic` when there is no bound Bug: #51795 Change-Id: I00acae6ba111f7b170650cfeffbfd2aaf7f71e42 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/290347 Reviewed-by: Chloe Stefantsova <[email protected]> Commit-Queue: Paul Berry <[email protected]>
1 parent 4caae04 commit 6b4878b

15 files changed

+197
-22
lines changed

pkg/front_end/lib/src/fasta/kernel/body_builder.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9170,10 +9170,13 @@ class BodyBuilder extends StackListenerImpl
91709170
push(typeArguments ?? NullValues.TypeArguments);
91719171
handleType(firstIdentifier, null);
91729172
TypeBuilder typeBuilder = pop() as TypeBuilder;
9173+
TypeDeclarationBuilder? typeDeclaration = typeBuilder.declaration;
91739174
DartType type = buildDartType(typeBuilder, TypeUse.objectPatternType,
91749175
allowPotentiallyConstantType: true);
9175-
push(forest.createObjectPattern(
9176-
firstIdentifier.charOffset, type, fields ?? <NamedPattern>[]));
9176+
push(new ObjectPatternInternal(type, fields ?? <NamedPattern>[],
9177+
typeDeclaration is TypeAliasBuilder ? typeDeclaration.typedef : null,
9178+
hasExplicitTypeArguments: typeArguments != null)
9179+
..fileOffset = firstIdentifier.charOffset);
91779180
}
91789181

91799182
@override

pkg/front_end/lib/src/fasta/kernel/forest.dart

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,11 +1038,6 @@ class Forest {
10381038
return new NullCheckPattern(pattern)..fileOffset = fileOffset;
10391039
}
10401040

1041-
ObjectPattern createObjectPattern(
1042-
int fileOffset, DartType type, List<NamedPattern> fields) {
1043-
return new ObjectPattern(type, fields)..fileOffset = fileOffset;
1044-
}
1045-
10461041
OrPattern createOrPattern(int fileOffset, Pattern left, Pattern right,
10471042
{required List<VariableDeclaration> orPatternJointVariables}) {
10481043
return new OrPattern(left, right,

pkg/front_end/lib/src/fasta/kernel/internal_ast.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3682,3 +3682,19 @@ class PatternForMapEntry extends TreeNode
36823682
return "PatternForMapEntry(${toStringInternal()})";
36833683
}
36843684
}
3685+
3686+
/// Data structure used by the body builder in place of [ObjectPattern], to
3687+
/// allow additional information to be captured that is needed during type
3688+
/// inference.
3689+
class ObjectPatternInternal extends ObjectPattern {
3690+
/// If the type name in the object pattern refers to a typedef, the typedef in
3691+
/// question; otherwise `null`.
3692+
final Typedef? typedef;
3693+
3694+
/// Indicates whether the object pattern included explicit type arguments; if
3695+
/// `true` this means that no further type inference needs to be performed.
3696+
final bool hasExplicitTypeArguments;
3697+
3698+
ObjectPatternInternal(super.requiredType, super.fields, this.typedef,
3699+
{required this.hasExplicitTypeArguments});
3700+
}

pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10251,6 +10251,8 @@ class InferenceVisitorImpl extends InferenceVisitorBase
1025110251
ValueKinds.Pattern, node.fields.length)
1025210252
]));
1025310253

10254+
node.requiredType = analysisResult.requiredType;
10255+
1025410256
Pattern? replacement;
1025510257

1025610258
InvalidExpression? error =
@@ -10873,15 +10875,65 @@ class InferenceVisitorImpl extends InferenceVisitorBase
1087310875
return new RecordType(positional, namedFields, Nullability.nonNullable);
1087410876
}
1087510877

10878+
/// Infers type arguments corresponding to [typeParameters] so that, when
10879+
/// substituted into [declaredType], the resulting type matches [contextType].
10880+
List<DartType> _inferTypeArguments(
10881+
{required List<TypeParameter> typeParameters,
10882+
required DartType declaredType,
10883+
required DartType contextType}) {
10884+
TypeConstraintGatherer gatherer = typeSchemaEnvironment
10885+
.setupGenericTypeInference(declaredType, typeParameters, contextType,
10886+
isNonNullableByDefault: isNonNullableByDefault);
10887+
List<DartType> inferredTypes = typeSchemaEnvironment.partialInfer(
10888+
gatherer, typeParameters, null,
10889+
isNonNullableByDefault: isNonNullableByDefault);
10890+
// Type inference may not be able to infer a type for every type parameter;
10891+
// if it can't fall back on the type parameter's bound.
10892+
// TODO(paulberry): this doesn't work if the type is F-bounded because in
10893+
// that case, the bound may refer to other type variables.
10894+
for (int i = 0; i < inferredTypes.length; i++) {
10895+
if (inferredTypes[i] is UnknownType) {
10896+
DartType bound = typeParameters[i].bound;
10897+
inferredTypes[i] = bound;
10898+
}
10899+
}
10900+
return inferredTypes;
10901+
}
10902+
1087610903
@override
1087710904
DartType downwardInferObjectPatternRequiredType({
1087810905
required DartType matchedType,
10879-
required Pattern pattern,
10906+
required covariant ObjectPatternInternal pattern,
1088010907
}) {
10881-
if (pattern is! ObjectPattern) return const InvalidType();
10882-
// TODO(johnniwinther): Update this when language issue #2770 has been
10883-
// resolved.
10884-
return pattern.requiredType;
10908+
DartType requiredType = pattern.requiredType;
10909+
if (!pattern.hasExplicitTypeArguments) {
10910+
if (pattern.typedef != null) {
10911+
// TODO(paulberry): handle typedefs properly.
10912+
}
10913+
if (requiredType is InterfaceType &&
10914+
requiredType.classNode.typeParameters.isNotEmpty) {
10915+
List<TypeParameter> typeParameters =
10916+
requiredType.classNode.typeParameters;
10917+
10918+
// It's possible that one of the callee type parameters might match a
10919+
// type that already exists as part of inference. This might happen,
10920+
// for instance, in the case where a method in a generic class contains
10921+
// an object pattern naming the enclosing class. To avoid creating
10922+
// invalid inference results, we need to create fresh type parameters.
10923+
FreshTypeParameters fresh = getFreshTypeParameters(typeParameters);
10924+
InterfaceType declaredType = new InterfaceType(requiredType.classNode,
10925+
requiredType.declaredNullability, fresh.freshTypeArguments);
10926+
typeParameters = fresh.freshTypeParameters;
10927+
10928+
List<DartType> inferredTypeArguments = _inferTypeArguments(
10929+
typeParameters: typeParameters,
10930+
declaredType: declaredType,
10931+
contextType: matchedType);
10932+
requiredType = new InterfaceType(requiredType.classNode,
10933+
requiredType.declaredNullability, inferredTypeArguments);
10934+
}
10935+
}
10936+
return requiredType;
1088510937
}
1088610938

1088710939
@override

pkg/front_end/testcases/patterns/non_constant_pattern_in_if.dart.strong.expect

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ static method test(dynamic value, core::bool expected) → void {
6464
}
6565
{
6666
final synthesized dynamic #4#0 = value;
67-
if(#4#0 is{ForNonNullableByDefault} self::Const<dynamic>) {
67+
if(#4#0 is{ForNonNullableByDefault} self::Const<core::Object?>) {
6868
matched = true;
6969
}
7070
}

pkg/front_end/testcases/patterns/non_constant_pattern_in_if.dart.strong.transformed.expect

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ static method test(dynamic value, core::bool expected) → void {
6464
}
6565
{
6666
final synthesized dynamic #4#0 = value;
67-
if(#4#0 is{ForNonNullableByDefault} self::Const<dynamic>) {
67+
if(#4#0 is{ForNonNullableByDefault} self::Const<core::Object?>) {
6868
matched = true;
6969
}
7070
}

pkg/front_end/testcases/patterns/non_constant_pattern_in_if.dart.weak.expect

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ static method test(dynamic value, core::bool expected) → void {
6464
}
6565
{
6666
final synthesized dynamic #4#0 = value;
67-
if(#4#0 is{ForNonNullableByDefault} self::Const<dynamic>) {
67+
if(#4#0 is{ForNonNullableByDefault} self::Const<core::Object?>) {
6868
matched = true;
6969
}
7070
}

pkg/front_end/testcases/patterns/non_constant_pattern_in_if.dart.weak.modular.expect

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ static method test(dynamic value, core::bool expected) → void {
6464
}
6565
{
6666
final synthesized dynamic #4#0 = value;
67-
if(#4#0 is{ForNonNullableByDefault} self::Const<dynamic>) {
67+
if(#4#0 is{ForNonNullableByDefault} self::Const<core::Object?>) {
6868
matched = true;
6969
}
7070
}

pkg/front_end/testcases/patterns/non_constant_pattern_in_if.dart.weak.transformed.expect

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ static method test(dynamic value, core::bool expected) → void {
6464
}
6565
{
6666
final synthesized dynamic #4#0 = value;
67-
if(#4#0 is{ForNonNullableByDefault} self::Const<dynamic>) {
67+
if(#4#0 is{ForNonNullableByDefault} self::Const<core::Object?>) {
6868
matched = true;
6969
}
7070
}

pkg/front_end/testcases/patterns/non_constant_pattern_in_switch.dart.strong.expect

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ static method test(dynamic value, core::bool expected) → void {
8282
}
8383
}
8484
{
85-
if(#0#0 is{ForNonNullableByDefault} self::Const<dynamic>) {
85+
if(#0#0 is{ForNonNullableByDefault} self::Const<core::Object?>) {
8686
{
8787
matched = true;
8888
break #L1;

0 commit comments

Comments
 (0)