Skip to content

Commit

Permalink
Update shared analysis of object patterns to match spec.
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
stereotype441 authored and Commit Queue committed Nov 21, 2022
1 parent 2d2a15a commit ac1ad9f
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 43 deletions.
20 changes: 14 additions & 6 deletions pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand All @@ -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
Expand Down
52 changes: 20 additions & 32 deletions pkg/_fe_analyzer_shared/test/mini_ast.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -3675,7 +3664,7 @@ class _NullLiteral extends Expression {
}

class _ObjectPattern extends Pattern {
final ObjectPatternRequiredType requiredType;
final PrimaryType requiredType;
final List<RecordPatternField> fields;

_ObjectPattern({
Expand All @@ -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
Expand All @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
],
Expand All @@ -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',
Expand All @@ -2128,7 +2161,7 @@ main() {
h.run([
match(
objectPattern(
requiredType: ObjectPatternRequiredType.type('num'),
requiredType: 'num',
fields: [
Var('foo').pattern().recordField('foo'),
],
Expand All @@ -2145,7 +2178,7 @@ main() {
h.run([
(match(
objectPattern(
requiredType: ObjectPatternRequiredType.type('int'),
requiredType: 'int',
fields: [
Var('foo').pattern().recordField('foo'),
],
Expand Down
1 change: 0 additions & 1 deletion pkg/analyzer/lib/src/dart/ast/ast.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9452,7 +9452,6 @@ class ObjectPatternImpl extends DartPatternImpl implements ObjectPattern {
matchedType,
context,
this,
requiredType: null,
fields: resolverVisitor.buildSharedRecordPatternFields(fields),
);
}
Expand Down

0 comments on commit ac1ad9f

Please sign in to comment.