Skip to content

Commit e680c98

Browse files
johnniwintherCommit Queue
authored and
Commit Queue
committed
[_fe_shared_analyzer] Pass context type when creating spaces from patterns
This passes the [StaticType] for the context of a pattern to the pattern converter. This is used to restrict the static type of the created space. For instance when creating the space for an untyped wildcard pattern, the context type will be used instead of the nullable-object (top) type. For other patterns, if the context type is a subtype of the type for the pattern itself, the context type will be used instead. This has the benefit that when checking for unreachable cases we will not take a too broad case to mean that it has some that it could match. For instance in `Foo(:var hashCode)` the space for `var hashCode` would have been the nullable-object type, and if the preceeding case was `Foo(:int hashCode)`, the reachable check would fail to see that all cases are covered since nullable-object is not a subtype of int. With this change, such cases are now handled. Change-Id: I5b6248bf79c8cf0b365eee2f3ca78090c3f43512 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/288902 Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Johnni Winther <[email protected]>
1 parent aab39d3 commit e680c98

File tree

15 files changed

+285
-169
lines changed

15 files changed

+285
-169
lines changed

pkg/_fe_analyzer_shared/lib/src/exhaustiveness/shared.dart

Lines changed: 126 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -316,16 +316,31 @@ class ExhaustivenessCache<
316316
mixin SpaceCreator<Pattern extends Object, Type extends Object> {
317317
TypeOperations<Type> get typeOperations;
318318

319+
ObjectFieldLookup get objectFieldLookup;
320+
319321
/// Creates a [StaticType] for an unknown type.
320322
///
321323
/// This is used when the type of the pattern is unknown or can't be
322324
/// represented as a [StaticType]. This type is unique and ensures that it
323325
/// is neither matches anything nor is matched by anything.
324326
StaticType createUnknownStaticType();
325327

326-
/// Creates the [StaticType] for [type]. If [nonNull] is `true`, the created
327-
/// type is non-nullable.
328-
StaticType createStaticType(Type type, {required bool nonNull});
328+
/// Creates the [StaticType] for [type].
329+
StaticType createStaticType(Type type);
330+
331+
/// Creates the [StaticType] for [type] restricted by the [contextType].
332+
/// If [nonNull] is `true`, the created type is non-nullable.
333+
StaticType _createStaticTypeWithContext(StaticType contextType, Type type,
334+
{required bool nonNull}) {
335+
StaticType staticType = createStaticType(type);
336+
if (contextType.isSubtypeOf(staticType)) {
337+
staticType = contextType;
338+
}
339+
if (nonNull && staticType is NullableStaticType) {
340+
staticType = staticType.underlying;
341+
}
342+
return staticType;
343+
}
329344

330345
/// Creates the [StaticType] for the list [type] with the given [identity].
331346
StaticType createListType(Type type, ListTypeIdentity<Type> identity);
@@ -335,75 +350,100 @@ mixin SpaceCreator<Pattern extends Object, Type extends Object> {
335350

336351
/// Creates the [Space] for [pattern] at the given [path].
337352
///
353+
/// The [contextType] is the [StaticType] in which the pattern match is
354+
/// performed. This is used to the restrict type of the created [Space] to
355+
/// the types allowed by the context. For instance `Object(:var hashCode)` is
356+
/// in itself unrestricted and would yield the top space for matching
357+
/// `var hashCode`. Using the [contextType] `int`, as given by the type of
358+
/// the `Object.hashCode`, the created space is all `int` values rather than
359+
/// all values.
360+
///
338361
/// If [nonNull] is `true`, the space is implicitly non-nullable.
339-
Space dispatchPattern(Path path, Pattern pattern, {required bool nonNull});
362+
Space dispatchPattern(Path path, StaticType contextType, Pattern pattern,
363+
{required bool nonNull});
340364

341365
/// Creates the root space for [pattern].
342-
Space createRootSpace(Pattern pattern, {required bool hasGuard}) {
366+
Space createRootSpace(StaticType contextType, Pattern pattern,
367+
{required bool hasGuard}) {
343368
if (hasGuard) {
344369
return createUnknownSpace(const Path.root());
345370
} else {
346-
return dispatchPattern(const Path.root(), pattern, nonNull: false);
371+
return dispatchPattern(const Path.root(), contextType, pattern,
372+
nonNull: false);
347373
}
348374
}
349375

350376
/// Creates the [Space] at [path] for a variable pattern of the declared
351377
/// [type].
352378
///
353379
/// If [nonNull] is `true`, the space is implicitly non-nullable.
354-
Space createVariableSpace(Path path, Type type, {required bool nonNull}) {
355-
return new Space(path, createStaticType(type, nonNull: nonNull));
380+
Space createVariableSpace(Path path, StaticType contextType, Type type,
381+
{required bool nonNull}) {
382+
StaticType staticType =
383+
_createStaticTypeWithContext(contextType, type, nonNull: nonNull);
384+
return new Space(path, staticType);
356385
}
357386

358387
/// Creates the [Space] at [path] for an object pattern of the required [type]
359388
/// and [fieldPatterns].
360389
///
361390
/// If [nonNull] is `true`, the space is implicitly non-nullable.
362-
Space createObjectSpace(
363-
Path path, Type type, Map<String, Pattern> fieldPatterns,
391+
Space createObjectSpace(Path path, StaticType contextType, Type type,
392+
Map<String, Pattern> fieldPatterns,
364393
{required bool nonNull}) {
394+
StaticType staticType =
395+
_createStaticTypeWithContext(contextType, type, nonNull: nonNull);
365396
Map<String, Space> fields = <String, Space>{};
366397
for (MapEntry<String, Pattern> entry in fieldPatterns.entries) {
367398
String name = entry.key;
368-
fields[name] =
369-
dispatchPattern(path.add(name), entry.value, nonNull: false);
399+
StaticType fieldType = staticType.getField(objectFieldLookup, name) ??
400+
StaticType.nullableObject;
401+
fields[name] = dispatchPattern(path.add(name), fieldType, entry.value,
402+
nonNull: false);
370403
}
371-
StaticType staticType = createStaticType(type, nonNull: nonNull);
372404
return new Space(path, staticType, fields: fields);
373405
}
374406

375407
/// Creates the [Space] at [path] for a record pattern of the required [type],
376408
/// [positionalFields], and [namedFields].
377-
Space createRecordSpace(Path path, Type recordType,
409+
Space createRecordSpace(Path path, StaticType contextType, Type recordType,
378410
List<Pattern> positionalFields, Map<String, Pattern> namedFields) {
411+
StaticType staticType =
412+
_createStaticTypeWithContext(contextType, recordType, nonNull: true);
379413
Map<String, Space> fields = <String, Space>{};
380414
for (int index = 0; index < positionalFields.length; index++) {
381415
String name = '\$${index + 1}';
382-
fields[name] = dispatchPattern(path.add(name), positionalFields[index],
416+
StaticType fieldType = staticType.getField(objectFieldLookup, name) ??
417+
StaticType.nullableObject;
418+
fields[name] = dispatchPattern(
419+
path.add(name), fieldType, positionalFields[index],
383420
nonNull: false);
384421
}
385422
for (MapEntry<String, Pattern> entry in namedFields.entries) {
386423
String name = entry.key;
387-
fields[name] =
388-
dispatchPattern(path.add(name), entry.value, nonNull: false);
424+
StaticType fieldType = staticType.getField(objectFieldLookup, name) ??
425+
StaticType.nullableObject;
426+
fields[name] = dispatchPattern(path.add(name), fieldType, entry.value,
427+
nonNull: false);
389428
}
390-
return new Space(path, createStaticType(recordType, nonNull: true),
391-
fields: fields);
429+
return new Space(path, staticType, fields: fields);
392430
}
393431

394432
/// Creates the [Space] at [path] for a wildcard pattern with the declared
395433
/// [type].
396434
///
397435
/// If [nonNull] is `true`, the space is implicitly non-nullable.
398-
Space createWildcardSpace(Path path, Type? type, {required bool nonNull}) {
436+
Space createWildcardSpace(Path path, StaticType contextType, Type? type,
437+
{required bool nonNull}) {
399438
if (type == null) {
400-
if (nonNull) {
401-
return new Space(path, StaticType.nonNullableObject);
402-
} else {
403-
return new Space(path, StaticType.nullableObject);
439+
StaticType staticType = contextType;
440+
if (nonNull && staticType is NullableStaticType) {
441+
staticType = staticType.underlying;
404442
}
443+
return new Space(path, staticType);
405444
} else {
406-
StaticType staticType = createStaticType(type, nonNull: nonNull);
445+
StaticType staticType =
446+
_createStaticTypeWithContext(contextType, type, nonNull: nonNull);
407447
return new Space(path, staticType);
408448
}
409449
}
@@ -418,45 +458,49 @@ mixin SpaceCreator<Pattern extends Object, Type extends Object> {
418458
/// [subPattern].
419459
///
420460
/// If [nonNull] is `true`, the space is implicitly non-nullable.
421-
Space createCastSpace(Path path, Pattern subPattern,
461+
Space createCastSpace(Path path, StaticType contextType, Pattern subPattern,
422462
{required bool nonNull}) {
423463
// TODO(johnniwinther): Handle types (sibling sealed types?) implicitly
424464
// handled by the throw of the invalid cast.
425-
return dispatchPattern(path, subPattern, nonNull: nonNull);
465+
return dispatchPattern(path, contextType, subPattern, nonNull: nonNull);
426466
}
427467

428468
/// Creates the [Space] at [path] for a null check pattern with the given
429469
/// [subPattern].
430-
Space createNullCheckSpace(Path path, Pattern subPattern) {
431-
return dispatchPattern(path, subPattern, nonNull: true);
470+
Space createNullCheckSpace(
471+
Path path, StaticType contextType, Pattern subPattern) {
472+
return dispatchPattern(path, contextType, subPattern, nonNull: true);
432473
}
433474

434475
/// Creates the [Space] at [path] for a null assert pattern with the given
435476
/// [subPattern].
436-
Space createNullAssertSpace(Path path, Pattern subPattern) {
437-
Space space = dispatchPattern(path, subPattern, nonNull: true);
477+
Space createNullAssertSpace(
478+
Path path, StaticType contextType, Pattern subPattern) {
479+
Space space = dispatchPattern(path, contextType, subPattern, nonNull: true);
438480
return space.union(new Space(path, StaticType.nullType));
439481
}
440482

441483
/// Creates the [Space] at [path] for a logical or pattern with the given
442484
/// [left] and [right] subpatterns.
443485
///
444486
/// If [nonNull] is `true`, the space is implicitly non-nullable.
445-
Space createLogicalOrSpace(Path path, Pattern left, Pattern right,
487+
Space createLogicalOrSpace(
488+
Path path, StaticType contextType, Pattern left, Pattern right,
446489
{required bool nonNull}) {
447-
Space aSpace = dispatchPattern(path, left, nonNull: nonNull);
448-
Space bSpace = dispatchPattern(path, right, nonNull: nonNull);
490+
Space aSpace = dispatchPattern(path, contextType, left, nonNull: nonNull);
491+
Space bSpace = dispatchPattern(path, contextType, right, nonNull: nonNull);
449492
return aSpace.union(bSpace);
450493
}
451494

452495
/// Creates the [Space] at [path] for a logical and pattern with the given
453496
/// [left] and [right] subpatterns.
454497
///
455498
/// If [nonNull] is `true`, the space is implicitly non-nullable.
456-
Space createLogicalAndSpace(Path path, Pattern left, Pattern right,
499+
Space createLogicalAndSpace(
500+
Path path, StaticType contextType, Pattern left, Pattern right,
457501
{required bool nonNull}) {
458-
Space aSpace = dispatchPattern(path, left, nonNull: nonNull);
459-
Space bSpace = dispatchPattern(path, right, nonNull: nonNull);
502+
Space aSpace = dispatchPattern(path, contextType, left, nonNull: nonNull);
503+
Space bSpace = dispatchPattern(path, contextType, right, nonNull: nonNull);
460504
return _createSpaceIntersection(path, aSpace, bSpace);
461505
}
462506

@@ -469,47 +513,56 @@ mixin SpaceCreator<Pattern extends Object, Type extends Object> {
469513
required List<Pattern> tailElements,
470514
required bool hasRest,
471515
required bool hasExplicitTypeArgument}) {
472-
Map<Key, Space> additionalFields = {};
473516
int headSize = headElements.length;
474517
int tailSize = tailElements.length;
518+
519+
String typeArgumentText;
520+
if (hasExplicitTypeArgument) {
521+
StringBuffer sb = new StringBuffer();
522+
sb.write('<');
523+
sb.write(typeOperations.typeToString(elementType));
524+
sb.write('>');
525+
typeArgumentText = sb.toString();
526+
} else {
527+
typeArgumentText = '';
528+
}
529+
530+
ListTypeIdentity<Type> identity = new ListTypeIdentity(
531+
elementType, typeArgumentText,
532+
size: headSize + tailSize, hasRest: hasRest);
533+
534+
StaticType staticType = createListType(type, identity);
535+
536+
Map<Key, Space> additionalFields = {};
475537
for (int index = 0; index < headSize; index++) {
476538
Key key = new HeadKey(index);
539+
StaticType fieldType =
540+
staticType.getAdditionalField(key) ?? StaticType.nullableObject;
477541
additionalFields[key] = dispatchPattern(
478-
path.add(key.name), headElements[index],
542+
path.add(key.name), fieldType, headElements[index],
479543
nonNull: false);
480544
}
481545
if (hasRest) {
482546
Key key = new RestKey(headSize, tailSize);
547+
StaticType fieldType =
548+
staticType.getAdditionalField(key) ?? StaticType.nullableObject;
483549
if (restElement != null) {
484-
additionalFields[key] =
485-
dispatchPattern(path.add(key.name), restElement, nonNull: false);
550+
additionalFields[key] = dispatchPattern(
551+
path.add(key.name), fieldType, restElement,
552+
nonNull: false);
486553
} else {
487-
additionalFields[key] =
488-
new Space(path.add(key.name), StaticType.nullableObject);
554+
additionalFields[key] = new Space(path.add(key.name), fieldType);
489555
}
490556
}
491557
for (int index = 0; index < tailSize; index++) {
492558
Key key = new TailKey(index);
493-
additionalFields[key] = dispatchPattern(
494-
path.add(key.name), tailElements[tailElements.length - index - 1],
559+
StaticType fieldType =
560+
staticType.getAdditionalField(key) ?? StaticType.nullableObject;
561+
additionalFields[key] = dispatchPattern(path.add(key.name), fieldType,
562+
tailElements[tailElements.length - index - 1],
495563
nonNull: false);
496564
}
497-
String typeArgumentText;
498-
if (hasExplicitTypeArgument) {
499-
StringBuffer sb = new StringBuffer();
500-
sb.write('<');
501-
sb.write(typeOperations.typeToString(elementType));
502-
sb.write('>');
503-
typeArgumentText = sb.toString();
504-
} else {
505-
typeArgumentText = '';
506-
}
507-
508-
ListTypeIdentity<Type> identity = new ListTypeIdentity(
509-
elementType, typeArgumentText,
510-
size: headSize + tailSize, hasRest: hasRest);
511-
return new Space(path, createListType(type, identity),
512-
additionalFields: additionalFields);
565+
return new Space(path, staticType, additionalFields: additionalFields);
513566
}
514567

515568
/// Creates the [Space] at [path] for a map pattern.
@@ -520,12 +573,6 @@ mixin SpaceCreator<Pattern extends Object, Type extends Object> {
520573
required Map<MapKey, Pattern> entries,
521574
required bool hasRest,
522575
required bool hasExplicitTypeArguments}) {
523-
Map<Key, Space> additionalFields = {};
524-
for (MapEntry<Key, Pattern> entry in entries.entries) {
525-
Key key = entry.key;
526-
additionalFields[key] =
527-
dispatchPattern(path.add(key.name), entry.value, nonNull: false);
528-
}
529576
String typeArgumentsText;
530577
if (hasExplicitTypeArguments) {
531578
StringBuffer sb = new StringBuffer();
@@ -542,8 +589,18 @@ mixin SpaceCreator<Pattern extends Object, Type extends Object> {
542589
MapTypeIdentity<Type> identity = new MapTypeIdentity(
543590
keyType, valueType, entries.keys.toSet(), typeArgumentsText,
544591
hasRest: hasRest);
545-
return new Space(path, createMapType(type, identity),
546-
additionalFields: additionalFields);
592+
StaticType staticType = createMapType(type, identity);
593+
594+
Map<Key, Space> additionalFields = {};
595+
for (MapEntry<Key, Pattern> entry in entries.entries) {
596+
Key key = entry.key;
597+
StaticType fieldType =
598+
staticType.getAdditionalField(key) ?? StaticType.nullableObject;
599+
additionalFields[key] = dispatchPattern(
600+
path.add(key.name), fieldType, entry.value,
601+
nonNull: false);
602+
}
603+
return new Space(path, staticType, additionalFields: additionalFields);
547604
}
548605

549606
/// Creates the [Space] at [path] for a pattern with unknown space.

pkg/_fe_analyzer_shared/test/exhaustiveness/data/and_pattern.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ class E extends B {}
1717
and(A o1, A o2) {
1818
var a = /*type=A*/switch (o1) {
1919
A() && var a /*space=A*/=> 0,
20-
_ /*space=()*/=> 1,
20+
_ /*
21+
error=unreachable,
22+
space=A
23+
*/=> 1,
2124
};
2225

2326
var b = /*type=A*/switch (o1) {

pkg/_fe_analyzer_shared/test/exhaustiveness/data/cast.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ sealedCast(B b1, B b2) {
6161
type=B
6262
*/switch (b1) {
6363
/*space=C*/case C():
64-
/*space=()*/case _ as D:
64+
/*space=B*/case _ as D:
6565
}
6666
/*
6767
error=non-exhaustive:E,

0 commit comments

Comments
 (0)