Skip to content

Commit d80fab4

Browse files
natebiggsCommit Queue
authored andcommitted
[dart2wasm] Fix dynamic switch casts.
If the switch's expression has type 'dynamic' and all the case expressions have the same type, we compare them using '=='. This requires a cast to ensure all the types match for the dispatch to the '==' function. However, we don't check that the type of the switch expression matches the type of the case expressions. So the cast fails if they don't match. This adds a guard to ensure the types match before running through the case expressions. If the guard fails, we either jump to the default case or if there isn't one, we skip the switch entirely. Fixes: #59782 Change-Id: I12e81f98d1c2046ee47e8ca4371642fd40620636 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/402460 Commit-Queue: Nate Biggs <[email protected]> Reviewed-by: Martin Kustermann <[email protected]>
1 parent a70ab61 commit d80fab4

File tree

2 files changed

+53
-0
lines changed

2 files changed

+53
-0
lines changed

pkg/dart2wasm/lib/code_generator.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1399,6 +1399,14 @@ abstract class AstCodeGenerator
13991399
b.local_set(switchValueNonNullableLocal);
14001400
}
14011401

1402+
final dynamicTypeGuard = switchInfo.dynamicTypeGuard;
1403+
if (dynamicTypeGuard != null) {
1404+
final success = b.block(const [], [translator.topInfo.nonNullableType]);
1405+
dynamicTypeGuard(switchValueNonNullableLocal, success);
1406+
b.br(switchLabels[defaultCase] ?? doneLabel);
1407+
b.end();
1408+
}
1409+
14021410
// Compare against all case values
14031411
for (SwitchCase c in node.cases) {
14041412
for (Expression exp in c.expressions) {
@@ -4114,6 +4122,12 @@ class SwitchInfo {
41144122
/// expression is nullable.
41154123
late final w.ValueType nonNullableType;
41164124

4125+
/// Generates code that will br on [successLabel] if [switchExprLocal] has the
4126+
/// correct type for the case checks on this switch. Only set for switches
4127+
/// where the switch expression is dynamic.
4128+
void Function(w.Local switchExprLocal, w.Label successLabel)?
4129+
dynamicTypeGuard;
4130+
41174131
/// Generates code that compares value of a `case` expression with the
41184132
/// `switch` expression's value. Calls [pushCaseExpr] once.
41194133
late final void Function(
@@ -4179,6 +4193,16 @@ class SwitchInfo {
41794193
// add missing optional parameters.
41804194
assert(equalsMemberSignature.inputs.length == 2);
41814195

4196+
dynamicTypeGuard = (switchExprLocal, successLabel) {
4197+
codeGen.b.local_get(switchExprLocal);
4198+
codeGen.b.br_on_cast(
4199+
successLabel,
4200+
switchExprLocal.type as w.RefType,
4201+
equalsMemberSignature.inputs[0]
4202+
.withNullability(switchExprLocal.type.nullable) as w.RefType);
4203+
codeGen.b.drop();
4204+
};
4205+
41824206
compare = (switchExprLocal, pushCaseExpr) {
41834207
final caseExprType = pushCaseExpr();
41844208
translator.convertType(
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:expect/expect.dart';
6+
7+
void main() {
8+
String? result;
9+
dynamic number = 42;
10+
switch (number) {
11+
case 'a':
12+
result = 'a';
13+
case 'b':
14+
result = 'b';
15+
default:
16+
result = 'default';
17+
}
18+
19+
Expect.equals(result, 'default');
20+
21+
result = null;
22+
switch (number) {
23+
case 'a':
24+
result = 'a';
25+
case 'b':
26+
result = 'b';
27+
}
28+
Expect.isNull(result);
29+
}

0 commit comments

Comments
 (0)