[dart2js] Update switch block local handling to correctly propagate types that fall through the block.

Fixes #52170

Change-Id: I7c473c3101d446eb17433d583bfe266bf6f193d6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/298760
Reviewed-by: Mayank Patke <fishythefish@google.com>
Commit-Queue: Nate Biggs <natebiggs@google.com>
This commit is contained in:
Nate Biggs 2023-04-27 19:35:45 +00:00 committed by Commit Queue
parent 47b7c1f31d
commit b9c73fdf23
3 changed files with 14 additions and 15 deletions

View file

@ -2564,12 +2564,12 @@ class LocalState {
LocalState mergeAfterBreaks(InferrerEngine inferrer, List<LocalState> states,
{bool keepOwnLocals = true}) {
bool allBranchesAbort = true;
bool allBranchesReturnOrThrow = true;
for (LocalState state in states) {
allBranchesAbort = allBranchesAbort && state.seenReturnOrThrow;
allBranchesReturnOrThrow &= state.seenReturnOrThrow;
}
keepOwnLocals = keepOwnLocals && !seenReturnOrThrow;
keepOwnLocals &= !seenReturnOrThrow;
LocalsHandler locals = _locals.mergeAfterBreaks(
inferrer,
@ -2577,7 +2577,7 @@ class LocalState {
.where((LocalState state) => !state.seenReturnOrThrow)
.map((LocalState state) => state._locals),
keepOwnLocals: keepOwnLocals);
seenReturnOrThrow = allBranchesAbort && !keepOwnLocals;
seenReturnOrThrow = allBranchesReturnOrThrow && !keepOwnLocals;
return LocalState.internal(locals, _fields, _tryBlock,
seenReturnOrThrow: seenReturnOrThrow,
seenBreakOrContinue: seenBreakOrContinue);

View file

@ -472,7 +472,7 @@ class LocalsHandler {
VariableScope merged = tryBlock != null
? VariableScope.tryBlock(tryBlock, parent: _locals)
: VariableScope(parent: _locals);
Set<Local> seenLocals = Setlet<Local>();
Map<Local, int> seenLocals = {};
// Merge all other handlers.
for (LocalsHandler handler in handlers) {
final common = _locals.commonParent(handler._locals);
@ -491,11 +491,12 @@ class LocalsHandler {
final myType = merged[local];
if (myType == null) return;
TypeInformation newType;
if (!seenLocals.contains(local)) {
final seenCount =
seenLocals.update(local, (v) => v + 1, ifAbsent: () => 1);
if (seenCount == 1) {
newType = inferrer.types.allocatePhi(
merged.tryBlock, local, otherType,
isTry: merged.isTry);
seenLocals.add(local);
} else {
newType = inferrer.types.addPhiInput(
local, myType as PhiElementTypeInformation, otherType);
@ -505,17 +506,17 @@ class LocalsHandler {
}
});
}
// If we want to keep own locals, we merge [seenLocals] from [this] into
// [merged] to update the Phi nodes with original values.
if (keepOwnLocals) {
for (Local variable in seenLocals) {
seenLocals.forEach((variable, seenCount) {
// If we want to keep own locals or if some branches do not update a seen
// local we merge the original type from [this] into [merged].
if (seenCount < handlers.length || keepOwnLocals) {
final originalType = _locals[variable];
if (originalType != null) {
merged[variable] = inferrer.types.addPhiInput(variable,
merged[variable] as PhiElementTypeInformation, originalType);
}
}
}
});
// Clean up Phi nodes with single input and store back result into
// actual locals handler.
merged.forEachLocalUntilScope(merged,

View file

@ -5,9 +5,7 @@
/*member: getInt:[exact=JSUInt31]*/
int get getInt => 42;
// TODO(http://dartbug.com/52170): Return type should include int. The return
// within the loop can and will be invoked with local = 3.
/*member: foo:Value([null|exact=JSString], value: "hello")*/
/*member: foo:Union(null, [exact=JSString], [exact=JSUInt31])*/
foo() {
dynamic local = 3;
for (int i = 0;