[_fe_analyzer_shared] Use subpattern for CastPattern exhaustiveness

This changes the Space computation for CastPattern to just the
Space for the subpattern. For a non-throwing cast, this is exactly
what the pattern will match.

There is still potential for handling the types rejected by the cast.
For instance recognizing that the (yet) unhandled subtypes of a
sealed type are exhausted by the throw.

Change-Id: Ia846895449f37a970f87a7f6c54a0ff8285df6b0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/286825
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Johnni Winther 2023-03-06 21:08:11 +00:00 committed by Commit Queue
parent 90ff190627
commit 8617cc68fa
3 changed files with 44 additions and 13 deletions

View file

@ -8,23 +8,30 @@ class A {
A(this.field);
}
sealed class B {}
class C extends B {}
class D extends B {}
class E extends B {}
simpleCast(o1, o2) {
var a = /*
fields={},
subtypes={Object,Null},
type=Object?
*/switch (o1) {
_ as A /*space=??*/=> 0,
_ /*space=()*/=> 1
_ as A /*space=()*/=> 0,
_ /*
error=unreachable,
space=()
*/=> 1
};
var b = /*
error=non-exhaustive:Object,
fields={},
subtypes={Object,Null},
type=Object?
*/switch (o2) {
_ as A /*space=??*/=> 0,
_ as A /*space=()*/=> 0,
};
}
@ -36,7 +43,7 @@ restrictedCase(o1, o2) {
subtypes={Object,Null},
type=Object?
*/switch (o1) {
A(field: 42) as A /*space=??*/=> 0,
A(field: 42) as A /*cfe.space=A(field: IntConstant(42))*//*analyzer.space=A(field: int (42))*/=> 0,
_ /*space=()*/=> 1
};
@ -46,6 +53,26 @@ restrictedCase(o1, o2) {
subtypes={Object,Null},
type=Object?
*/switch (o2) {
A(field: 42) as A /*space=??*/=> 0,
A(field: 42) as A /*cfe.space=A(field: IntConstant(42))*//*analyzer.space=A(field: int (42))*/=> 0,
};
}
sealedCast(B b1, B b2) {
/*
fields={hashCode:int,runtimeType:Type},
subtypes={C,D,E},
type=B
*/switch (b1) {
/*space=C*/case C():
/*space=()*/case _ as D:
}
/*
error=non-exhaustive:E,
fields={hashCode:int,runtimeType:Type},
subtypes={C,D,E},
type=B
*/switch (b2) {
/*space=D*/case D():
/*space=C*/case var c as C:
}
}

View file

@ -381,12 +381,12 @@ class PatternConverter {
} else if (pattern is NullAssertPattern) {
Space space = convertPattern(pattern.pattern, nonNull: true, path: path);
return space.union(Space(path, StaticType.nullType));
} else if (pattern is CastPattern ||
pattern is RelationalPattern ||
pattern is LogicalAndPattern) {
} else if (pattern is CastPattern) {
// TODO(johnniwinther): Handle types (sibling sealed types?) implicitly
// handled by the throw of the invalid cast.
return convertPattern(pattern.pattern, nonNull: nonNull, path: path);
} else if (pattern is RelationalPattern || pattern is LogicalAndPattern) {
// These pattern do not add to the exhaustiveness coverage.
// TODO(johnniwinther): Handle `Null` aspect implicitly covered by
// [NullAssertPattern] and `as Null`.
// TODO(johnniwinther): Handle top in [AndPattern] branches.
return Space(path, cache.getUnknownStaticType());
} else if (pattern is ListPattern || pattern is MapPattern) {

View file

@ -480,8 +480,12 @@ Space convertPatternToSpace(CfeExhaustivenessCache cache, Pattern pattern,
cache, pattern.pattern, constants, context,
nonNull: true, path: path);
return space.union(new Space(path, StaticType.nullType));
} else if (pattern is CastPattern ||
pattern is InvalidPattern ||
} else if (pattern is CastPattern) {
// TODO(johnniwinther): Handle types (sibling sealed types?) implicitly
// handled by the throw of the invalid cast.
return convertPatternToSpace(cache, pattern.pattern, constants, context,
nonNull: nonNull, path: path);
} else if (pattern is InvalidPattern ||
pattern is RelationalPattern ||
pattern is AndPattern) {
// These pattern do not add to the exhaustiveness coverage.