Skip to content

Commit ac1ad9f

Browse files
stereotype441Commit Queue
authored and
Commit Queue
committed
Update shared analysis of object patterns to match spec.
In dart-lang/language@ce01d33, the patterns spec was changed so that if an object pattern's type resolves to `dynamic` or `Never`, no getters are looked up during static analysis, and the getter type is simply presumed to be `dynamic` or `Never`, respectively. Also, the parameter `requiredType` of `analyzeObjectPattern` is removed (is was not needed, and the caller always passed `null`). Finally, the test artifact `ObjectPatternRequiredType` is removed in favor of just using a PrimaryType directly. This makes the tests a little bit more compact. Change-Id: I6fcef8dab8250905e1d37308bbdc82a4fd65f52c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/270982 Reviewed-by: Chloe Stefantsova <[email protected]> Commit-Queue: Paul Berry <[email protected]>
1 parent 2d2a15a commit ac1ad9f

File tree

4 files changed

+71
-43
lines changed

4 files changed

+71
-43
lines changed

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

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -697,16 +697,23 @@ mixin TypeAnalyzer<
697697
Type matchedType,
698698
MatchContext<Node, Expression, Pattern, Type, Variable> context,
699699
Pattern node, {
700-
required Type? requiredType,
701700
required List<RecordPatternField<Node, Pattern>> fields,
702701
}) {
703702
_reportDuplicateRecordPatternFields(fields);
704703

705-
requiredType ??= downwardInferObjectPatternRequiredType(
704+
Type requiredType = downwardInferObjectPatternRequiredType(
706705
matchedType: matchedType,
707706
pattern: node,
708707
);
709708

709+
// If the required type is `dynamic` or `Never`, then every getter is
710+
// treated as having the same type.
711+
Type? overridePropertyGetType;
712+
if (typeOperations.isDynamic(requiredType) ||
713+
typeOperations.isNever(requiredType)) {
714+
overridePropertyGetType = requiredType;
715+
}
716+
710717
Node? irrefutableContext = context.irrefutableContext;
711718
if (irrefutableContext != null &&
712719
!typeOperations.isAssignableTo(matchedType, requiredType)) {
@@ -720,10 +727,11 @@ mixin TypeAnalyzer<
720727

721728
// Stack: ()
722729
for (RecordPatternField<Node, Pattern> field in fields) {
723-
Type propertyType = resolveObjectPatternPropertyGet(
724-
receiverType: requiredType,
725-
field: field,
726-
);
730+
Type propertyType = overridePropertyGetType ??
731+
resolveObjectPatternPropertyGet(
732+
receiverType: requiredType,
733+
field: field,
734+
);
727735
dispatchPattern(propertyType, context, field.pattern);
728736
}
729737
// Stack: (n * Pattern) where n = fields.length

pkg/_fe_analyzer_shared/test/mini_ast.dart

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -265,11 +265,15 @@ Statement match(Pattern pattern, Expression initializer,
265265
isLate: isLate, isFinal: isFinal, location: computeLocation());
266266

267267
Pattern objectPattern({
268-
required ObjectPatternRequiredType requiredType,
268+
required String requiredType,
269269
required List<RecordPatternField> fields,
270270
}) {
271+
var parsedType = Type(requiredType);
272+
if (parsedType is! PrimaryType) {
273+
fail('Expected a primary type, got $parsedType');
274+
}
271275
return _ObjectPattern(
272-
requiredType: requiredType,
276+
requiredType: parsedType,
273277
fields: fields,
274278
location: computeLocation(),
275279
);
@@ -754,6 +758,7 @@ class MiniAstOperations
754758
'int? <: Object': false,
755759
'int? <: Object?': true,
756760
'List<int> <: Object': true,
761+
'Never <: Object': true,
757762
'Never <: Object?': true,
758763
'Null <: double?': true,
759764
'Null <: int': false,
@@ -881,7 +886,11 @@ class MiniAstOperations
881886
};
882887

883888
static final Map<String, Type> _coreDownwardInferenceResults = {
889+
'dynamic <: int': Type('dynamic'),
890+
'int <: num': Type('int'),
884891
'List <: Iterable<int>': Type('List<int>'),
892+
'Never <: int': Type('Never'),
893+
'num <: int': Type('num'),
885894
};
886895

887896
static final Map<String, Type> _coreNormalizeResults = {
@@ -1155,27 +1164,6 @@ class Node {
11551164
String toString() => 'Node#$id';
11561165
}
11571166

1158-
/// Either the type, or the name of a type constructor.
1159-
class ObjectPatternRequiredType {
1160-
final Type? type;
1161-
final String? name;
1162-
1163-
ObjectPatternRequiredType.name(this.name) : type = null;
1164-
1165-
ObjectPatternRequiredType.type(String type)
1166-
: type = Type(type),
1167-
name = null;
1168-
1169-
@override
1170-
String toString() {
1171-
if (type != null) {
1172-
return '(type: $type)';
1173-
} else {
1174-
return '(name: $name)';
1175-
}
1176-
}
1177-
}
1178-
11791167
abstract class Pattern extends Node
11801168
with PossiblyGuardedPattern
11811169
implements ListPatternElement {
@@ -3239,11 +3227,12 @@ class _MiniAstTypeAnalyzer
32393227
required Type matchedType,
32403228
required covariant _ObjectPattern pattern,
32413229
}) {
3242-
var name = pattern.requiredType.name;
3243-
if (name == null) {
3244-
fail('Expected type constructor name at ${pattern.location}');
3230+
var requiredType = pattern.requiredType;
3231+
if (requiredType.args.isNotEmpty) {
3232+
return requiredType;
3233+
} else {
3234+
return typeOperations.downwardInfer(requiredType.name, matchedType);
32453235
}
3246-
return typeOperations.downwardInfer(name, matchedType);
32473236
}
32483237

32493238
void finish() {
@@ -3675,7 +3664,7 @@ class _NullLiteral extends Expression {
36753664
}
36763665

36773666
class _ObjectPattern extends Pattern {
3678-
final ObjectPatternRequiredType requiredType;
3667+
final PrimaryType requiredType;
36793668
final List<RecordPatternField> fields;
36803669

36813670
_ObjectPattern({
@@ -3686,7 +3675,7 @@ class _ObjectPattern extends Pattern {
36863675

36873676
@override
36883677
Type computeSchema(Harness h) {
3689-
return h.typeAnalyzer.analyzeObjectPatternSchema(requiredType.type!);
3678+
return h.typeAnalyzer.analyzeObjectPatternSchema(requiredType);
36903679
}
36913680

36923681
@override
@@ -3705,9 +3694,8 @@ class _ObjectPattern extends Pattern {
37053694
Type matchedType,
37063695
SharedMatchContext context,
37073696
) {
3708-
var requiredType = h.typeAnalyzer.analyzeObjectPattern(
3709-
matchedType, context, this,
3710-
requiredType: this.requiredType.type, fields: fields);
3697+
var requiredType = h.typeAnalyzer
3698+
.analyzeObjectPattern(matchedType, context, this, fields: fields);
37113699
h.irBuilder.atom(matchedType.type, Kind.type, location: location);
37123700
h.irBuilder.atom(requiredType.type, Kind.type, location: location);
37133701
h.irBuilder.apply(

pkg/_fe_analyzer_shared/test/type_inference/type_inference_test.dart

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2090,7 +2090,7 @@ main() {
20902090
ifCase(
20912091
expr('A<int>').checkContext('?'),
20922092
objectPattern(
2093-
requiredType: ObjectPatternRequiredType.name('B'),
2093+
requiredType: 'B',
20942094
fields: [
20952095
Var('foo', errorId: 'foo').pattern().recordField('foo'),
20962096
],
@@ -2100,13 +2100,46 @@ main() {
21002100
'requiredType: B<int>), variables(foo), true, block(), noop)'),
21012101
]);
21022102
});
2103+
2104+
test('dynamic type', () {
2105+
h.run([
2106+
ifCase(
2107+
expr('int').checkContext('?'),
2108+
objectPattern(
2109+
requiredType: 'dynamic',
2110+
fields: [
2111+
Var('foo', errorId: 'foo').pattern().recordField('foo'),
2112+
],
2113+
),
2114+
).checkIr('ifCase(expr(int), objectPattern(varPattern(foo, '
2115+
'matchedType: dynamic, staticType: dynamic), matchedType: int, '
2116+
'requiredType: dynamic), variables(foo), true, block(), noop)'),
2117+
]);
2118+
});
2119+
2120+
test('Never type', () {
2121+
h.run([
2122+
ifCase(
2123+
expr('int').checkContext('?'),
2124+
objectPattern(
2125+
requiredType: 'Never',
2126+
fields: [
2127+
Var('foo', errorId: 'foo').pattern().recordField('foo'),
2128+
],
2129+
),
2130+
).checkIr('ifCase(expr(int), objectPattern(varPattern(foo, '
2131+
'matchedType: Never, staticType: Never), matchedType: int, '
2132+
'requiredType: Never), variables(foo), true, block(), noop)'),
2133+
]);
2134+
});
2135+
21032136
test('duplicate field name', () {
21042137
h.addMember('A<int>', 'foo', 'int');
21052138
h.run([
21062139
ifCase(
21072140
expr('A<int>'),
21082141
objectPattern(
2109-
requiredType: ObjectPatternRequiredType.type('A<int>'),
2142+
requiredType: 'A<int>',
21102143
fields: [
21112144
Var('a', errorId: 'a').pattern().recordField('foo')
21122145
..errorId = 'ORIGINAL',
@@ -2128,7 +2161,7 @@ main() {
21282161
h.run([
21292162
match(
21302163
objectPattern(
2131-
requiredType: ObjectPatternRequiredType.type('num'),
2164+
requiredType: 'num',
21322165
fields: [
21332166
Var('foo').pattern().recordField('foo'),
21342167
],
@@ -2145,7 +2178,7 @@ main() {
21452178
h.run([
21462179
(match(
21472180
objectPattern(
2148-
requiredType: ObjectPatternRequiredType.type('int'),
2181+
requiredType: 'int',
21492182
fields: [
21502183
Var('foo').pattern().recordField('foo'),
21512184
],

pkg/analyzer/lib/src/dart/ast/ast.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9452,7 +9452,6 @@ class ObjectPatternImpl extends DartPatternImpl implements ObjectPattern {
94529452
matchedType,
94539453
context,
94549454
this,
9455-
requiredType: null,
94569455
fields: resolverVisitor.buildSharedRecordPatternFields(fields),
94579456
);
94589457
}

0 commit comments

Comments
 (0)