Update shared analysis of object patterns to match spec.

In
ce01d330d7,
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 <cstefantsova@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
Paul Berry 2022-11-21 13:12:00 +00:00 committed by Commit Queue
parent 2d2a15af8f
commit ac1ad9f393
4 changed files with 71 additions and 43 deletions

View file

@ -697,16 +697,23 @@ mixin TypeAnalyzer<
Type matchedType,
MatchContext<Node, Expression, Pattern, Type, Variable> context,
Pattern node, {
required Type? requiredType,
required List<RecordPatternField<Node, Pattern>> fields,
}) {
_reportDuplicateRecordPatternFields(fields);
requiredType ??= downwardInferObjectPatternRequiredType(
Type requiredType = downwardInferObjectPatternRequiredType(
matchedType: matchedType,
pattern: node,
);
// If the required type is `dynamic` or `Never`, then every getter is
// treated as having the same type.
Type? overridePropertyGetType;
if (typeOperations.isDynamic(requiredType) ||
typeOperations.isNever(requiredType)) {
overridePropertyGetType = requiredType;
}
Node? irrefutableContext = context.irrefutableContext;
if (irrefutableContext != null &&
!typeOperations.isAssignableTo(matchedType, requiredType)) {
@ -720,10 +727,11 @@ mixin TypeAnalyzer<
// Stack: ()
for (RecordPatternField<Node, Pattern> field in fields) {
Type propertyType = resolveObjectPatternPropertyGet(
receiverType: requiredType,
field: field,
);
Type propertyType = overridePropertyGetType ??
resolveObjectPatternPropertyGet(
receiverType: requiredType,
field: field,
);
dispatchPattern(propertyType, context, field.pattern);
}
// Stack: (n * Pattern) where n = fields.length

View file

@ -265,11 +265,15 @@ Statement match(Pattern pattern, Expression initializer,
isLate: isLate, isFinal: isFinal, location: computeLocation());
Pattern objectPattern({
required ObjectPatternRequiredType requiredType,
required String requiredType,
required List<RecordPatternField> fields,
}) {
var parsedType = Type(requiredType);
if (parsedType is! PrimaryType) {
fail('Expected a primary type, got $parsedType');
}
return _ObjectPattern(
requiredType: requiredType,
requiredType: parsedType,
fields: fields,
location: computeLocation(),
);
@ -754,6 +758,7 @@ class MiniAstOperations
'int? <: Object': false,
'int? <: Object?': true,
'List<int> <: Object': true,
'Never <: Object': true,
'Never <: Object?': true,
'Null <: double?': true,
'Null <: int': false,
@ -881,7 +886,11 @@ class MiniAstOperations
};
static final Map<String, Type> _coreDownwardInferenceResults = {
'dynamic <: int': Type('dynamic'),
'int <: num': Type('int'),
'List <: Iterable<int>': Type('List<int>'),
'Never <: int': Type('Never'),
'num <: int': Type('num'),
};
static final Map<String, Type> _coreNormalizeResults = {
@ -1155,27 +1164,6 @@ class Node {
String toString() => 'Node#$id';
}
/// Either the type, or the name of a type constructor.
class ObjectPatternRequiredType {
final Type? type;
final String? name;
ObjectPatternRequiredType.name(this.name) : type = null;
ObjectPatternRequiredType.type(String type)
: type = Type(type),
name = null;
@override
String toString() {
if (type != null) {
return '(type: $type)';
} else {
return '(name: $name)';
}
}
}
abstract class Pattern extends Node
with PossiblyGuardedPattern
implements ListPatternElement {
@ -3239,11 +3227,12 @@ class _MiniAstTypeAnalyzer
required Type matchedType,
required covariant _ObjectPattern pattern,
}) {
var name = pattern.requiredType.name;
if (name == null) {
fail('Expected type constructor name at ${pattern.location}');
var requiredType = pattern.requiredType;
if (requiredType.args.isNotEmpty) {
return requiredType;
} else {
return typeOperations.downwardInfer(requiredType.name, matchedType);
}
return typeOperations.downwardInfer(name, matchedType);
}
void finish() {
@ -3675,7 +3664,7 @@ class _NullLiteral extends Expression {
}
class _ObjectPattern extends Pattern {
final ObjectPatternRequiredType requiredType;
final PrimaryType requiredType;
final List<RecordPatternField> fields;
_ObjectPattern({
@ -3686,7 +3675,7 @@ class _ObjectPattern extends Pattern {
@override
Type computeSchema(Harness h) {
return h.typeAnalyzer.analyzeObjectPatternSchema(requiredType.type!);
return h.typeAnalyzer.analyzeObjectPatternSchema(requiredType);
}
@override
@ -3705,9 +3694,8 @@ class _ObjectPattern extends Pattern {
Type matchedType,
SharedMatchContext context,
) {
var requiredType = h.typeAnalyzer.analyzeObjectPattern(
matchedType, context, this,
requiredType: this.requiredType.type, fields: fields);
var requiredType = h.typeAnalyzer
.analyzeObjectPattern(matchedType, context, this, fields: fields);
h.irBuilder.atom(matchedType.type, Kind.type, location: location);
h.irBuilder.atom(requiredType.type, Kind.type, location: location);
h.irBuilder.apply(

View file

@ -2090,7 +2090,7 @@ main() {
ifCase(
expr('A<int>').checkContext('?'),
objectPattern(
requiredType: ObjectPatternRequiredType.name('B'),
requiredType: 'B',
fields: [
Var('foo', errorId: 'foo').pattern().recordField('foo'),
],
@ -2100,13 +2100,46 @@ main() {
'requiredType: B<int>), variables(foo), true, block(), noop)'),
]);
});
test('dynamic type', () {
h.run([
ifCase(
expr('int').checkContext('?'),
objectPattern(
requiredType: 'dynamic',
fields: [
Var('foo', errorId: 'foo').pattern().recordField('foo'),
],
),
).checkIr('ifCase(expr(int), objectPattern(varPattern(foo, '
'matchedType: dynamic, staticType: dynamic), matchedType: int, '
'requiredType: dynamic), variables(foo), true, block(), noop)'),
]);
});
test('Never type', () {
h.run([
ifCase(
expr('int').checkContext('?'),
objectPattern(
requiredType: 'Never',
fields: [
Var('foo', errorId: 'foo').pattern().recordField('foo'),
],
),
).checkIr('ifCase(expr(int), objectPattern(varPattern(foo, '
'matchedType: Never, staticType: Never), matchedType: int, '
'requiredType: Never), variables(foo), true, block(), noop)'),
]);
});
test('duplicate field name', () {
h.addMember('A<int>', 'foo', 'int');
h.run([
ifCase(
expr('A<int>'),
objectPattern(
requiredType: ObjectPatternRequiredType.type('A<int>'),
requiredType: 'A<int>',
fields: [
Var('a', errorId: 'a').pattern().recordField('foo')
..errorId = 'ORIGINAL',
@ -2128,7 +2161,7 @@ main() {
h.run([
match(
objectPattern(
requiredType: ObjectPatternRequiredType.type('num'),
requiredType: 'num',
fields: [
Var('foo').pattern().recordField('foo'),
],
@ -2145,7 +2178,7 @@ main() {
h.run([
(match(
objectPattern(
requiredType: ObjectPatternRequiredType.type('int'),
requiredType: 'int',
fields: [
Var('foo').pattern().recordField('foo'),
],

View file

@ -9452,7 +9452,6 @@ class ObjectPatternImpl extends DartPatternImpl implements ObjectPattern {
matchedType,
context,
this,
requiredType: null,
fields: resolverVisitor.buildSharedRecordPatternFields(fields),
);
}