Skip to content

Commit 90b799a

Browse files
chloestefantsovaCommit Queue
authored and
Commit Queue
committed
[cfe] Implement desugaring of ObjectPattern for simple cases
This CL doesn't cover, among other, the following * Non-class identifiers * Some erroneous cases Part of #49749 Change-Id: I9c6aa9194c09cc2b10b438892b26a01c260e1baf Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/275923 Commit-Queue: Chloe Stefantsova <[email protected]> Reviewed-by: Johnni Winther <[email protected]>
1 parent 1c3a92c commit 90b799a

File tree

76 files changed

+1275
-279
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+1275
-279
lines changed

pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12084,6 +12084,17 @@ Message _withArgumentsUnspecified(String string) {
1208412084
problemMessage: """${string}""", arguments: {'string': string});
1208512085
}
1208612086

12087+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
12088+
const Code<Null> codeUnspecifiedGetterNameInObjectPattern =
12089+
messageUnspecifiedGetterNameInObjectPattern;
12090+
12091+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
12092+
const MessageCode messageUnspecifiedGetterNameInObjectPattern = const MessageCode(
12093+
"UnspecifiedGetterNameInObjectPattern",
12094+
analyzerCodes: <String>["MISSING_OBJECT_PATTERN_GETTER_NAME"],
12095+
problemMessage:
12096+
r"""The getter name is not specified explicitly, and the pattern is not a variable. Try specifying the getter name explicitly, or using a variable pattern.""");
12097+
1208712098
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
1208812099
const Code<Null> codeUnsupportedDartExt = messageUnsupportedDartExt;
1208912100

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

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8328,11 +8328,16 @@ class BodyBuilder extends StackListenerImpl
83288328
count)));
83298329
reportIfNotEnabled(
83308330
libraryFeatures.patterns, beginToken.charOffset, beginToken.charCount);
8331+
List<NamedPattern>? fields;
83318332
for (int i = 0; i < count; i++) {
8332-
pop();
8333+
Object? field = pop();
8334+
if (field is NamedPattern) {
8335+
(fields ??= <NamedPattern>[]).add(field);
8336+
} else {
8337+
// TODO(cstefantsova): Report an error.
8338+
}
83338339
}
8334-
// TODO(johnniwinther): Push (named) patterns.
8335-
push(count != 0 ? <Pattern>[] : NullValue.PatternList);
8340+
push(fields ?? NullValue.PatternList);
83368341
}
83378342

83388343
@override
@@ -8347,13 +8352,40 @@ class BodyBuilder extends StackListenerImpl
83478352
reportIfNotEnabled(libraryFeatures.patterns, firstIdentifier.charOffset,
83488353
firstIdentifier.charCount);
83498354

8350-
// ignore: unused_local_variable
8351-
List<Pattern>? fields = pop() as List<Pattern>?;
8352-
// ignore: unused_local_variable
8355+
List<NamedPattern>? fields = pop() as List<NamedPattern>?;
83538356
List<TypeBuilder>? typeArguments = pop() as List<TypeBuilder>?;
83548357

8355-
// TODO(johnniwinther): Create object pattern.
8356-
push(new DummyPattern(firstIdentifier.charOffset));
8358+
// TODO(cstefantsova): Handle the case of secondIdentifier != null
8359+
handleIdentifier(firstIdentifier, IdentifierContext.typeReference);
8360+
Object? resolvedIdentifier = pop();
8361+
if (resolvedIdentifier is TypeUseGenerator) {
8362+
TypeDeclarationBuilder typeDeclaration = resolvedIdentifier.declaration;
8363+
if (typeDeclaration is ClassBuilder) {
8364+
List<DartType>? builtTypeArguments;
8365+
if (typeArguments != null) {
8366+
if (typeArguments.length ==
8367+
typeDeclaration.cls.typeParameters.length) {
8368+
for (TypeBuilder typeBuilder in typeArguments) {
8369+
(builtTypeArguments ??= <DartType>[])
8370+
.add(typeBuilder.build(libraryBuilder, TypeUse.typeArgument));
8371+
}
8372+
} else {
8373+
// TODO(cstefantsova): Report an error.
8374+
}
8375+
}
8376+
push(new ObjectPattern(
8377+
typeDeclaration.cls.reference,
8378+
fields ?? <NamedPattern>[],
8379+
builtTypeArguments,
8380+
firstIdentifier.offset));
8381+
} else {
8382+
// TODO(cstefantsova): Handle this case.
8383+
push(new DummyPattern(firstIdentifier.charOffset));
8384+
}
8385+
} else {
8386+
// TODO(cstefantsova): Handle the error.
8387+
push(new DummyPattern(firstIdentifier.charOffset));
8388+
}
83578389
}
83588390

83598391
@override

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

Lines changed: 212 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
/// with the same kind of root node.
2020
import 'package:kernel/ast.dart';
2121
import 'package:kernel/clone.dart';
22+
import 'package:kernel/src/bounds_checks.dart';
2223
import 'package:kernel/src/printer.dart';
2324
import 'package:kernel/text/ast_to_text.dart' show Precedence, Printer;
2425
import 'package:kernel/type_environment.dart';
@@ -3947,8 +3948,14 @@ class OrPattern extends Pattern {
39473948
};
39483949
List<VariableDeclaration> declaredVariables = this.declaredVariables;
39493950
for (VariableDeclaration variable in declaredVariables) {
3950-
VariableDeclaration leftVariable = leftVariablesByName[variable.name!]!;
3951-
VariableDeclaration rightVariable = rightVariablesByName[variable.name!]!;
3951+
VariableDeclaration? leftVariable = leftVariablesByName[variable.name!];
3952+
VariableDeclaration? rightVariable = rightVariablesByName[variable.name!];
3953+
3954+
if (leftVariable == null || rightVariable == null) {
3955+
// TODO(cstefantsova): Make sure an error is reported.
3956+
continue;
3957+
}
3958+
39523959
variable.initializer = inferenceVisitor.engine.forest
39533960
.createConditionalExpression(
39543961
fileOffset,
@@ -4535,6 +4542,209 @@ class ListPattern extends Pattern {
45354542
}
45364543
}
45374544

4545+
class ObjectPattern extends Pattern {
4546+
final Reference classReference;
4547+
final List<NamedPattern> fields;
4548+
final List<DartType>? typeArguments;
4549+
Class get classNode => classReference.asClass;
4550+
4551+
ObjectPattern(
4552+
this.classReference, this.fields, this.typeArguments, int fileOffset)
4553+
: super(fileOffset) {
4554+
setParents(fields, this);
4555+
}
4556+
4557+
@override
4558+
PatternInferenceResult acceptInference(InferenceVisitorImpl visitor,
4559+
{required SharedMatchContext context}) {
4560+
return visitor.visitObjectPattern(this, context: context);
4561+
}
4562+
4563+
@override
4564+
List<VariableDeclaration> get declaredVariables {
4565+
return [for (NamedPattern field in fields) ...field.declaredVariables];
4566+
}
4567+
4568+
@override
4569+
void toTextInternal(AstPrinter printer) {
4570+
printer.write('${classNode.name}');
4571+
if (typeArguments != null) {
4572+
printer.write('<');
4573+
printer.writeTypes(typeArguments!);
4574+
printer.write('>');
4575+
}
4576+
printer.write('(');
4577+
String comma = '';
4578+
for (Pattern field in fields) {
4579+
printer.write(comma);
4580+
field.toTextInternal(printer);
4581+
comma = ', ';
4582+
}
4583+
printer.write(')');
4584+
}
4585+
4586+
@override
4587+
String toString() {
4588+
return "ObjectPattern(${toStringInternal()})";
4589+
}
4590+
4591+
@override
4592+
PatternTransformationResult transform(
4593+
Expression matchedExpression,
4594+
DartType matchedType,
4595+
Expression variableInitializingContext,
4596+
InferenceVisitorBase inferenceVisitor) {
4597+
// targetObjectType: `classNode`<`typeArguments`>
4598+
DartType targetObjectType;
4599+
if (typeArguments != null &&
4600+
typeArguments!.length == classNode.typeParameters.length) {
4601+
targetObjectType =
4602+
new InterfaceType(classNode, Nullability.nonNullable, typeArguments);
4603+
} else {
4604+
if (typeArguments != null) {
4605+
// TODO(cstefantsova): Report an error.
4606+
}
4607+
targetObjectType = new InterfaceType(
4608+
classNode,
4609+
Nullability.nonNullable,
4610+
calculateBounds(
4611+
classNode.typeParameters,
4612+
inferenceVisitor.coreTypes.objectClass,
4613+
inferenceVisitor.libraryBuilder.library));
4614+
}
4615+
4616+
bool typeCheckForTargetNeeded =
4617+
!inferenceVisitor.isAssignable(targetObjectType, matchedType) ||
4618+
matchedType is DynamicType;
4619+
4620+
// objectVariable: `matchedType` OVAR = `matchedExpression`
4621+
VariableDeclaration objectVariable = inferenceVisitor.engine.forest
4622+
.createVariableDeclarationForValue(matchedExpression,
4623+
type: matchedType);
4624+
4625+
// typeCheck: `objectVariable` is `targetObjectType`
4626+
// ==> OVAR is `classNode`<`typeArguments`>
4627+
Expression? typeCheck;
4628+
if (typeCheckForTargetNeeded) {
4629+
typeCheck = inferenceVisitor.engine.forest.createIsExpression(
4630+
fileOffset,
4631+
inferenceVisitor.engine.forest
4632+
.createVariableGet(fileOffset, objectVariable),
4633+
targetObjectType,
4634+
forNonNullableByDefault: false);
4635+
}
4636+
4637+
List<VariableDeclaration> elementAccessVariables = [];
4638+
PatternTransformationResult transformationResult =
4639+
new PatternTransformationResult([]);
4640+
for (NamedPattern field in fields) {
4641+
String? fieldNameString;
4642+
if (field.name.isNotEmpty) {
4643+
fieldNameString = field.name;
4644+
} else {
4645+
// The name is defined by the nested variable pattern.
4646+
Pattern nestedPattern = field.pattern;
4647+
if (nestedPattern is VariablePattern) {
4648+
fieldNameString = nestedPattern.name;
4649+
}
4650+
}
4651+
4652+
Expression objectElement;
4653+
DartType fieldType;
4654+
if (fieldNameString != null) {
4655+
Name fieldName = new Name(fieldNameString);
4656+
4657+
ObjectAccessTarget fieldAccessTarget = inferenceVisitor
4658+
.findInterfaceMember(targetObjectType, fieldName, fileOffset,
4659+
callSiteAccessKind: CallSiteAccessKind.getterInvocation);
4660+
4661+
if (fieldAccessTarget.member != null) {
4662+
fieldType = fieldAccessTarget.getGetterType(inferenceVisitor);
4663+
4664+
// objectElement: `objectVariable`.`fieldName`
4665+
// ==> OVAR.`fieldName`
4666+
objectElement = new InstanceGet(
4667+
InstanceAccessKind.Instance,
4668+
inferenceVisitor.engine.forest
4669+
.createVariableGet(fileOffset, objectVariable)
4670+
..promotedType = targetObjectType,
4671+
fieldName,
4672+
resultType: fieldType,
4673+
interfaceTarget: fieldAccessTarget.member!)
4674+
..fileOffset = fileOffset;
4675+
} else {
4676+
objectElement = inferenceVisitor.helper.buildProblem(
4677+
templateUndefinedGetter.withArguments(fieldNameString,
4678+
targetObjectType, inferenceVisitor.isNonNullableByDefault),
4679+
fileOffset,
4680+
noLength);
4681+
fieldType = const InvalidType();
4682+
}
4683+
} else {
4684+
objectElement = inferenceVisitor.helper.buildProblem(
4685+
messageUnspecifiedGetterNameInObjectPattern, fileOffset, noLength);
4686+
fieldType = const InvalidType();
4687+
}
4688+
4689+
// objectElementVariable: `fieldType` EVAR = `objectElement`
4690+
// ==> `fieldType` EVAR = OVAR.`fieldName`
4691+
VariableDeclaration objectElementVariable = inferenceVisitor.engine.forest
4692+
.createVariableDeclarationForValue(objectElement, type: fieldType);
4693+
4694+
PatternTransformationResult subpatternTransformationResult = field.pattern
4695+
.transform(
4696+
inferenceVisitor.engine.forest
4697+
.createVariableGet(fileOffset, objectElementVariable),
4698+
fieldType,
4699+
inferenceVisitor.engine.forest
4700+
.createVariableGet(fileOffset, objectElementVariable),
4701+
inferenceVisitor);
4702+
4703+
// If the sub-pattern transformation doesn't declare captured variables
4704+
// and consists of a single empty element, it means that it simply
4705+
// doesn't have a place where it could refer to the element expression.
4706+
// In that case we can avoid creating the intermediary variable for the
4707+
// element expression.
4708+
//
4709+
// An example of such sub-pattern is in the following:
4710+
//
4711+
// if (x case A(foo: var _) { /* ... */ }
4712+
if (field.declaredVariables.isNotEmpty ||
4713+
!(subpatternTransformationResult.elements.length == 1 &&
4714+
subpatternTransformationResult.elements.single.isEmpty)) {
4715+
elementAccessVariables.add(objectElementVariable);
4716+
transformationResult = transformationResult.combine(
4717+
subpatternTransformationResult, inferenceVisitor);
4718+
}
4719+
}
4720+
4721+
transformationResult = transformationResult.prependElement(
4722+
new PatternTransformationElement(
4723+
kind: PatternTransformationElementKind.regular,
4724+
condition: null,
4725+
variableInitializers: elementAccessVariables),
4726+
inferenceVisitor);
4727+
4728+
if (typeCheck != null) {
4729+
transformationResult = transformationResult.prependElement(
4730+
new PatternTransformationElement(
4731+
kind: PatternTransformationElementKind.regular,
4732+
condition: typeCheck,
4733+
variableInitializers: []),
4734+
inferenceVisitor);
4735+
}
4736+
4737+
transformationResult = transformationResult.prependElement(
4738+
new PatternTransformationElement(
4739+
kind: PatternTransformationElementKind.regular,
4740+
condition: null,
4741+
variableInitializers: [objectVariable]),
4742+
inferenceVisitor);
4743+
4744+
return transformationResult;
4745+
}
4746+
}
4747+
45384748
enum RelationalPatternKind {
45394749
equals,
45404750
notEquals,

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

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9120,6 +9120,17 @@ class InferenceVisitorImpl extends InferenceVisitorBase
91209120
return const PatternInferenceResult();
91219121
}
91229122

9123+
PatternInferenceResult visitObjectPattern(ObjectPattern pattern,
9124+
{required SharedMatchContext context}) {
9125+
analyzeObjectPattern(context, pattern,
9126+
fields: <RecordPatternField<Node, Pattern>>[
9127+
for (NamedPattern field in pattern.fields)
9128+
new RecordPatternField(
9129+
node: field, name: field.name, pattern: field.pattern)
9130+
]);
9131+
return const PatternInferenceResult();
9132+
}
9133+
91239134
PatternInferenceResult visitRelationalPattern(
91249135
RelationalPattern pattern, {
91259136
required SharedMatchContext context,
@@ -9208,8 +9219,27 @@ class InferenceVisitorImpl extends InferenceVisitorBase
92089219
required DartType matchedType,
92099220
required Pattern pattern,
92109221
}) {
9211-
// TODO(scheglov): implement downwardInferObjectPatternRequiredType
9212-
throw new UnimplementedError('TODO(scheglov)');
9222+
if (pattern is! ObjectPattern) return const InvalidType();
9223+
if (pattern.classNode.typeParameters.isEmpty) {
9224+
return new InterfaceType(pattern.classNode, Nullability.nonNullable, []);
9225+
}
9226+
if (pattern.typeArguments != null) {
9227+
return new InterfaceType(
9228+
pattern.classNode, Nullability.nonNullable, pattern.typeArguments);
9229+
}
9230+
9231+
DartType typeToInfer =
9232+
pattern.classNode.getThisType(coreTypes, Nullability.nonNullable);
9233+
List<TypeParameter> typeParametersToInfer =
9234+
pattern.classNode.typeParameters;
9235+
TypeConstraintGatherer gatherer =
9236+
typeSchemaEnvironment.setupGenericTypeInference(typeToInfer,
9237+
typeParametersToInfer, matchedType, libraryBuilder.library);
9238+
List<DartType> inferredTypes = typeSchemaEnvironment.partialInfer(
9239+
gatherer, typeParametersToInfer, null, libraryBuilder.library);
9240+
Substitution substitution =
9241+
Substitution.fromPairs(typeParametersToInfer, inferredTypes);
9242+
return substitution.substituteType(typeToInfer);
92139243
}
92149244

92159245
@override
@@ -9223,8 +9253,11 @@ class InferenceVisitorImpl extends InferenceVisitorBase
92239253
required DartType receiverType,
92249254
required shared.RecordPatternField<Node, Pattern> field,
92259255
}) {
9226-
// TODO(scheglov): implement resolveObjectPatternPropertyGet
9227-
throw new UnimplementedError('TODO(scheglov)');
9256+
// TODO(cstefantsova): Provide a better fileOffset.
9257+
ObjectAccessTarget fieldAccessTarget = findInterfaceMember(
9258+
receiverType, new Name(field.name!), field.pattern.fileOffset,
9259+
callSiteAccessKind: CallSiteAccessKind.getterInvocation);
9260+
return fieldAccessTarget.getGetterType(this);
92289261
}
92299262

92309263
@override

pkg/front_end/messages.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6025,3 +6025,12 @@ CantUseClassAsMixin:
60256025
script: |
60266026
class A {}
60276027
class B with A {}
6028+
6029+
UnspecifiedGetterNameInObjectPattern:
6030+
problemMessage: "The getter name is not specified explicitly, and the pattern is not a variable. Try specifying the getter name explicitly, or using a variable pattern."
6031+
experiments: patterns
6032+
analyzerCode: MISSING_OBJECT_PATTERN_GETTER_NAME
6033+
script: |
6034+
abstract class A { int get foo;}
6035+
test(dynamic x) { if (x case A(: 0)) {} }
6036+

0 commit comments

Comments
 (0)