Skip to content

Commit 88a23c1

Browse files
chloestefantsovaCommit Queue
authored and
Commit Queue
committed
[cfe] Desugar RecordPattern
Part of #49749 Change-Id: Ibe09a7de3a664323afd18e27fc8902fb6c3f4842 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/271361 Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Chloe Stefantsova <[email protected]>
1 parent 6f4d84f commit 88a23c1

19 files changed

+1075
-50
lines changed

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

+109-6
Original file line numberDiff line numberDiff line change
@@ -6251,6 +6251,7 @@ class NamedPattern extends Pattern {
62516251

62526252
class RecordPattern extends Pattern {
62536253
final List<Pattern> patterns;
6254+
late final RecordType type;
62546255

62556256
@override
62566257
List<VariableDeclaration> get declaredVariables =>
@@ -6293,12 +6294,114 @@ class RecordPattern extends Pattern {
62936294
DartType matchedType,
62946295
Expression variableInitializingContext,
62956296
InferenceVisitorBase inferenceVisitor) {
6296-
return new PatternTransformationResult([
6297-
new PatternTransformationElement(
6298-
condition:
6299-
new InvalidExpression("Unimplemented RecordPattern.transform"),
6300-
variableInitializers: [])
6301-
]);
6297+
bool typeCheckNeeded = !inferenceVisitor.isAssignable(type, matchedType) ||
6298+
matchedType is DynamicType;
6299+
6300+
// recordVariable: `matchedType` RVAR = `matchedExpression`
6301+
VariableDeclaration recordVariable = inferenceVisitor.engine.forest
6302+
.createVariableDeclarationForValue(matchedExpression,
6303+
type: matchedType);
6304+
6305+
PatternTransformationResult transformationResult =
6306+
new PatternTransformationResult([]);
6307+
int recordFieldIndex = 0;
6308+
List<VariableDeclaration> fieldAccessVariables = [];
6309+
for (Pattern fieldPattern in patterns) {
6310+
Expression recordField;
6311+
DartType fieldType;
6312+
Pattern subpattern;
6313+
if (fieldPattern is NamedPattern) {
6314+
// recordField: `recordVariable`[`fieldPattern.name`]
6315+
// ==> RVAR[`fieldPattern.name`]
6316+
recordField = new RecordNameGet(
6317+
inferenceVisitor.engine.forest
6318+
.createVariableGet(fileOffset, recordVariable)
6319+
..promotedType = typeCheckNeeded ? type : null,
6320+
type,
6321+
fieldPattern.name);
6322+
6323+
// [type] is computed by the CFE, so the absence of the named field is
6324+
// an internal error, and we check the condition with an assert rather
6325+
// than reporting a compile-time error.
6326+
assert(type.named.any((named) => named.name == fieldPattern.name));
6327+
fieldType = type.named
6328+
.firstWhere((named) => named.name == fieldPattern.name)
6329+
.type;
6330+
6331+
subpattern = fieldPattern.pattern;
6332+
} else {
6333+
// recordField: `recordVariable`[`recordFieldIndex`]
6334+
// ==> RVAR[`recordFieldIndex`]
6335+
recordField = new RecordIndexGet(
6336+
inferenceVisitor.engine.forest
6337+
.createVariableGet(fileOffset, recordVariable)
6338+
..promotedType = typeCheckNeeded ? type : null,
6339+
type,
6340+
recordFieldIndex);
6341+
6342+
// [type] is computed by the CFE, so the field index out of range is an
6343+
// internal error, and we check the condition with an assert rather than
6344+
// reporting a compile-time error.
6345+
assert(recordFieldIndex < type.positional.length);
6346+
fieldType = type.positional[recordFieldIndex];
6347+
6348+
subpattern = fieldPattern;
6349+
recordFieldIndex++;
6350+
}
6351+
6352+
// recordFieldIndex: `fieldType` FVAR = `recordField`
6353+
VariableDeclaration recordFieldVariable = inferenceVisitor.engine.forest
6354+
.createVariableDeclarationForValue(recordField, type: fieldType);
6355+
6356+
PatternTransformationResult subpatternTransformationResult =
6357+
subpattern.transform(
6358+
inferenceVisitor.engine.forest
6359+
.createVariableGet(fileOffset, recordFieldVariable),
6360+
fieldType,
6361+
inferenceVisitor.engine.forest
6362+
.createVariableGet(fileOffset, recordFieldVariable),
6363+
inferenceVisitor);
6364+
6365+
// If the sub-pattern transformation doesn't declare captured variables
6366+
// and consists of a single empty element, it means that it simply
6367+
// doesn't have a place where it could refer to the element expression.
6368+
// In that case we can avoid creating the intermediary variable for the
6369+
// element expression.
6370+
//
6371+
// An example of such sub-pattern is in the following:
6372+
//
6373+
// if (x case (var _,)) { /* ... */ }
6374+
if (subpattern.declaredVariables.isNotEmpty ||
6375+
!(subpatternTransformationResult.elements.length == 1 &&
6376+
subpatternTransformationResult.elements.single.isEmpty)) {
6377+
fieldAccessVariables.add(recordFieldVariable);
6378+
transformationResult = transformationResult.combine(
6379+
subpatternTransformationResult, inferenceVisitor);
6380+
}
6381+
}
6382+
6383+
// condition: [`recordVariable` is `type`]?
6384+
// ==> [RVAR is `type`]?
6385+
transformationResult = transformationResult.prependElement(
6386+
new PatternTransformationElement(
6387+
condition: !typeCheckNeeded
6388+
? null
6389+
: inferenceVisitor.engine.forest.createIsExpression(
6390+
fileOffset,
6391+
inferenceVisitor.engine.forest
6392+
.createVariableGet(fileOffset, recordVariable),
6393+
type,
6394+
forNonNullableByDefault:
6395+
inferenceVisitor.isNonNullableByDefault),
6396+
variableInitializers: fieldAccessVariables),
6397+
inferenceVisitor);
6398+
6399+
transformationResult = transformationResult.prependElement(
6400+
new PatternTransformationElement(
6401+
condition: null, variableInitializers: [recordVariable]),
6402+
inferenceVisitor);
6403+
6404+
return transformationResult;
63026405
}
63036406
}
63046407

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

+28-9
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
66
import 'package:_fe_analyzer_shared/src/type_inference/type_analysis_result.dart';
77
import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer.dart'
8-
hide NamedType, RecordPatternField, RecordType;
8+
hide NamedType, RecordType;
99
import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer.dart'
1010
as shared;
1111
import 'package:_fe_analyzer_shared/src/type_inference/type_operations.dart';
@@ -8605,7 +8605,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase
86058605
DartType get doubleType => throw new UnimplementedError('TODO(paulberry)');
86068606

86078607
@override
8608-
DartType get dynamicType => throw new UnimplementedError('TODO(paulberry)');
8608+
DartType get dynamicType => const DynamicType();
86098609

86108610
@override
86118611
void finishExpressionCase(Expression node, int caseIndex) {
@@ -8738,8 +8738,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase
87388738
}
87398739

87408740
@override
8741-
DartType get objectQuestionType =>
8742-
throw new UnimplementedError('TODO(paulberry)');
8741+
DartType get objectQuestionType => coreTypes.objectNullableRawType;
87438742

87448743
@override
87458744
void setVariableType(VariableDeclaration variable, DartType type) {
@@ -8914,22 +8913,42 @@ class InferenceVisitorImpl extends InferenceVisitorBase
89148913
required DartType matchedType,
89158914
required SharedMatchContext context,
89168915
}) {
8917-
// TODO(cstefantsova): Implement visitRecordPattern.
8916+
List<RecordPatternField<Node, Pattern>> fields = [
8917+
for (Pattern fieldPattern in pattern.patterns)
8918+
new RecordPatternField(
8919+
node: fieldPattern,
8920+
pattern: fieldPattern,
8921+
name: fieldPattern is NamedPattern ? fieldPattern.name : null)
8922+
];
8923+
DartType patternType =
8924+
analyzeRecordPattern(matchedType, context, pattern, fields: fields);
8925+
pattern.type = patternType as RecordType;
89188926
return const PatternInferenceResult();
89198927
}
89208928

89218929
@override
89228930
shared.RecordType<DartType>? asRecordType(DartType type) {
8923-
// TODO(scheglov): implement asRecordType
8924-
throw new UnimplementedError('TODO(scheglov)');
8931+
if (type is RecordType) {
8932+
return new shared.RecordType(
8933+
positional: type.positional,
8934+
named: type.named
8935+
.map((field) => new shared.NamedType(field.name, field.type))
8936+
.toList());
8937+
} else {
8938+
return null;
8939+
}
89258940
}
89268941

89278942
@override
89288943
DartType recordType(
89298944
{required List<DartType> positional,
89308945
required List<shared.NamedType<DartType>> named}) {
8931-
// TODO(scheglov): implement recordType
8932-
throw new UnimplementedError('TODO(scheglov)');
8946+
List<NamedType> namedFields = [];
8947+
for (shared.NamedType<DartType> namedType in named) {
8948+
namedFields.add(new NamedType(namedType.name, namedType.type));
8949+
}
8950+
namedFields.sort((f1, f2) => f1.name.compareTo(f2.name));
8951+
return new RecordType(positional, namedFields, Nullability.nonNullable);
89338952
}
89348953

89358954
@override

pkg/front_end/test/spell_checking_list_code.txt

+2
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,7 @@ function's
567567
fuse
568568
futured
569569
futureor
570+
fvar
570571
g
571572
gardening
572573
gb
@@ -1237,6 +1238,7 @@ rparen
12371238
rpc
12381239
rs
12391240
runnable
1241+
rvar
12401242
s
12411243
safer
12421244
sampled

pkg/front_end/testcases/patterns/record_pattern_inside_if_case.dart

+6
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,10 @@ test(dynamic x) {
77
if (x case (1, a: 2)) {}
88
if (x case (a: 1, 2)) {}
99
if (x case (a: 1, b: 2)) {}
10+
if (x case (int _, double y, foo: String _!, bar: var _)) {
11+
return 0;
12+
} else {
13+
return 1;
14+
}
15+
1016
}
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,64 @@
11
library /*isNonNullableByDefault*/;
22
import self as self;
3+
import "dart:core" as core;
34

45
static method test(dynamic x) → dynamic {
56
final dynamic #t1 = x;
6-
if(invalid-expression "Unimplemented RecordPattern.transform") {
7+
final dynamic #t2 = #t1;
8+
if(#t2 is{ForNonNullableByDefault} (core::Object?, core::Object?)) {
9+
final core::Object? #t3 = #t2{(core::Object?, core::Object?)}.$0{core::Object?};
10+
final core::Object? #t4 = #t2{(core::Object?, core::Object?)}.$1{core::Object?};
11+
if(#t3 =={core::Object::==}{(core::Object) → core::bool} 1 && #t4 =={core::Object::==}{(core::Object) → core::bool} 2) {
12+
}
713
}
8-
final dynamic #t2 = x;
9-
if(invalid-expression "Unimplemented RecordPattern.transform") {
14+
final dynamic #t5 = x;
15+
final dynamic #t6 = #t5;
16+
if(#t6 is{ForNonNullableByDefault} (core::Object?, {a: core::Object?})) {
17+
final core::Object? #t7 = #t6{(core::Object?, {a: core::Object?})}.$0{core::Object?};
18+
final core::Object? #t8 = #t6{(core::Object?, {a: core::Object?})}.a{core::Object?};
19+
if(#t7 =={core::Object::==}{(core::Object) → core::bool} 1 && #t8 =={core::Object::==}{(core::Object) → core::bool} 2) {
20+
}
1021
}
11-
final dynamic #t3 = x;
12-
if(invalid-expression "Unimplemented RecordPattern.transform") {
22+
final dynamic #t9 = x;
23+
final dynamic #t10 = #t9;
24+
if(#t10 is{ForNonNullableByDefault} (core::Object?, {a: core::Object?})) {
25+
final core::Object? #t11 = #t10{(core::Object?, {a: core::Object?})}.a{core::Object?};
26+
final core::Object? #t12 = #t10{(core::Object?, {a: core::Object?})}.$0{core::Object?};
27+
if(#t11 =={core::Object::==}{(core::Object) → core::bool} 1 && #t12 =={core::Object::==}{(core::Object) → core::bool} 2) {
28+
}
1329
}
14-
final dynamic #t4 = x;
15-
if(invalid-expression "Unimplemented RecordPattern.transform") {
30+
final dynamic #t13 = x;
31+
final dynamic #t14 = #t13;
32+
if(#t14 is{ForNonNullableByDefault} ({a: core::Object?, b: core::Object?})) {
33+
final core::Object? #t15 = #t14{({a: core::Object?, b: core::Object?})}.a{core::Object?};
34+
final core::Object? #t16 = #t14{({a: core::Object?, b: core::Object?})}.b{core::Object?};
35+
if(#t15 =={core::Object::==}{(core::Object) → core::bool} 1 && #t16 =={core::Object::==}{(core::Object) → core::bool} 2) {
36+
}
37+
}
38+
final dynamic #t17 = x;
39+
final core::bool #t18 = true;
40+
final dynamic #t19 = #t17;
41+
if(#t19 is{ForNonNullableByDefault} (core::Object?, core::Object?, {bar: core::Object?, foo: core::Object?})) {
42+
final core::Object? #t20 = #t19{(core::Object?, core::Object?, {bar: core::Object?, foo: core::Object?})}.$0{core::Object?};
43+
final core::Object? #t21 = #t19{(core::Object?, core::Object?, {bar: core::Object?, foo: core::Object?})}.$1{core::Object?};
44+
final core::Object? #t22 = #t19{(core::Object?, core::Object?, {bar: core::Object?, foo: core::Object?})}.foo{core::Object?};
45+
if(#t20 is core::int) {
46+
final core::Object? #t23 = #t21;
47+
if(#t23 is core::double) {
48+
final core::Object #t24 = #t22!;
49+
if(#t24 is core::String) {
50+
#t18 = false;
51+
{
52+
core::double y = #t23{core::double};
53+
{
54+
return 0;
55+
}
56+
}
57+
}
58+
}
59+
}
60+
}
61+
if(#t18) {
62+
return 1;
1663
}
1764
}
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,64 @@
11
library /*isNonNullableByDefault*/;
22
import self as self;
3+
import "dart:core" as core;
34

45
static method test(dynamic x) → dynamic {
56
final dynamic #t1 = x;
6-
if(invalid-expression "Unimplemented RecordPattern.transform") {
7+
final dynamic #t2 = #t1;
8+
if(#t2 is{ForNonNullableByDefault} (core::Object?, core::Object?)) {
9+
final core::Object? #t3 = #t2{(core::Object?, core::Object?)}.$0{core::Object?};
10+
final core::Object? #t4 = #t2{(core::Object?, core::Object?)}.$1{core::Object?};
11+
if(#t3 =={core::Object::==}{(core::Object) → core::bool} 1 && #t4 =={core::Object::==}{(core::Object) → core::bool} 2) {
12+
}
713
}
8-
final dynamic #t2 = x;
9-
if(invalid-expression "Unimplemented RecordPattern.transform") {
14+
final dynamic #t5 = x;
15+
final dynamic #t6 = #t5;
16+
if(#t6 is{ForNonNullableByDefault} (core::Object?, {a: core::Object?})) {
17+
final core::Object? #t7 = #t6{(core::Object?, {a: core::Object?})}.$0{core::Object?};
18+
final core::Object? #t8 = #t6{(core::Object?, {a: core::Object?})}.a{core::Object?};
19+
if(#t7 =={core::Object::==}{(core::Object) → core::bool} 1 && #t8 =={core::Object::==}{(core::Object) → core::bool} 2) {
20+
}
1021
}
11-
final dynamic #t3 = x;
12-
if(invalid-expression "Unimplemented RecordPattern.transform") {
22+
final dynamic #t9 = x;
23+
final dynamic #t10 = #t9;
24+
if(#t10 is{ForNonNullableByDefault} (core::Object?, {a: core::Object?})) {
25+
final core::Object? #t11 = #t10{(core::Object?, {a: core::Object?})}.a{core::Object?};
26+
final core::Object? #t12 = #t10{(core::Object?, {a: core::Object?})}.$0{core::Object?};
27+
if(#t11 =={core::Object::==}{(core::Object) → core::bool} 1 && #t12 =={core::Object::==}{(core::Object) → core::bool} 2) {
28+
}
1329
}
14-
final dynamic #t4 = x;
15-
if(invalid-expression "Unimplemented RecordPattern.transform") {
30+
final dynamic #t13 = x;
31+
final dynamic #t14 = #t13;
32+
if(#t14 is{ForNonNullableByDefault} ({a: core::Object?, b: core::Object?})) {
33+
final core::Object? #t15 = #t14{({a: core::Object?, b: core::Object?})}.a{core::Object?};
34+
final core::Object? #t16 = #t14{({a: core::Object?, b: core::Object?})}.b{core::Object?};
35+
if(#t15 =={core::Object::==}{(core::Object) → core::bool} 1 && #t16 =={core::Object::==}{(core::Object) → core::bool} 2) {
36+
}
37+
}
38+
final dynamic #t17 = x;
39+
final core::bool #t18 = true;
40+
final dynamic #t19 = #t17;
41+
if(#t19 is{ForNonNullableByDefault} (core::Object?, core::Object?, {bar: core::Object?, foo: core::Object?})) {
42+
final core::Object? #t20 = #t19{(core::Object?, core::Object?, {bar: core::Object?, foo: core::Object?})}.$0{core::Object?};
43+
final core::Object? #t21 = #t19{(core::Object?, core::Object?, {bar: core::Object?, foo: core::Object?})}.$1{core::Object?};
44+
final core::Object? #t22 = #t19{(core::Object?, core::Object?, {bar: core::Object?, foo: core::Object?})}.foo{core::Object?};
45+
if(#t20 is core::int) {
46+
final core::Object? #t23 = #t21;
47+
if(#t23 is core::double) {
48+
final core::Object #t24 = #t22!;
49+
if(#t24 is core::String) {
50+
#t18 = false;
51+
{
52+
core::double y = #t23{core::double};
53+
{
54+
return 0;
55+
}
56+
}
57+
}
58+
}
59+
}
60+
}
61+
if(#t18) {
62+
return 1;
1663
}
1764
}

0 commit comments

Comments
 (0)