checker: Do not deduplicate cases in tagless switches

Tagless switch cases are ordered predicates, so distinct constant conditions may fold to the same Boolean value. Restrict duplicate-case tracking to explicitly tagged switches and add positive and negative regression coverage for issue #7421.
This commit is contained in:
leo119
2026-08-27 14:53:54 +08:00
parent 897c99a9a3
commit 24cb1abf29
5 changed files with 55 additions and 3 deletions

View File

@@ -1167,6 +1167,8 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags
ast_node(ss, SwitchStmt, node);
Operand x = {};
// Tagless switch cases are independent predicates, not case values.
bool check_duplicate_cases = ss->tag != nullptr;
mod_flags |= Stmt_BreakAllowed | Stmt_FallthroughAllowed;
check_open_scope(ctx, node);
@@ -1292,7 +1294,9 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags
Operand b1 = rhs;
check_comparison(ctx, expr, &a1, &b1, Token_LtEq);
add_to_seen_map(ctx, &seen, upper_op, x, lhs, rhs);
if (check_duplicate_cases) {
add_to_seen_map(ctx, &seen, upper_op, x, lhs, rhs);
}
if (is_type_string16(x.type)) {
// NOTE(bill): Force dependency for strings here
@@ -1325,7 +1329,9 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags
}
t = default_type(t);
add_type_info_type(ctx, t);
add_type_to_seen_map(ctx, &seen, y);
if (check_duplicate_cases) {
add_type_to_seen_map(ctx, &seen, y);
}
} else {
convert_to_typed(ctx, &y, x.type);
if (y.mode == Addressing_Invalid) {
@@ -1342,7 +1348,9 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags
continue;
}
update_untyped_expr_type(ctx, z.expr, x.type, !is_type_untyped(x.type));
add_to_seen_map(ctx, &seen, y);
if (check_duplicate_cases) {
add_to_seen_map(ctx, &seen, y);
}
}
}
}

View File

@@ -52,6 +52,8 @@ set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style -ignore-unused
..\..\..\odin check ..\test_issue_foreign_redeclaration_mismatch.odin -no-entry-point %COMMON% 2>&1 | find /c "Error:" | findstr /x "1" || exit /b
..\..\..\odin doc ..\test_issue_asm_doc_category.odin -file 2>&1 | find /c "asm templates" | findstr /x "1" || exit /b
..\..\..\odin build ..\test_issue_7037.odin %COMMON% -o:none || exit /b
..\..\..\odin test ..\test_issue_7421.odin %COMMON% || exit /b
..\..\..\odin check ..\test_issue_7421_tagged_duplicate.odin %COMMON% 2>&1 | find /c "Error: Duplicate case" | findstr /x "1" || exit /b
..\..\..\odin build ..\test_issue_7188.odin %COMMON% || exit /b
clang -c ..\test_issue_sysv_abi.c -o test_issue_sysv_abi_c.o || exit /b
..\..\..\odin test ..\test_issue_sysv_abi.odin %COMMON% || exit /b

View File

@@ -91,6 +91,13 @@ $ODIN check ../test_issue_6979.odin -no-entry-point $COMMON_CHECK
$ODIN test ../test_issue_7008.odin $COMMON
$ODIN check ../test_issue_7012.odin -no-entry-point $COMMON_CHECK
$ODIN build ../test_issue_7037.odin $COMMON -o:none
$ODIN test ../test_issue_7421.odin $COMMON
if [[ $($ODIN check ../test_issue_7421_tagged_duplicate.odin $COMMON_CHECK 2>&1 >/dev/null | grep -c "Error: Duplicate case") -eq 1 ]]; then
echo "SUCCESSFUL 1/1"
else
echo "SUCCESSFUL 0/1"
exit 1
fi
$ODIN check ../test_issue_7429.odin $COMMON_CHECK
$ODIN test ../test_issue_7356.odin $COMMON
$ODIN test ../test_issue_7336.odin $COMMON

View File

@@ -0,0 +1,25 @@
// Tests issue #7421: constant conditions in a tagless switch are conditions,
// not values to compare against one another for duplicate cases.
// https://github.com/odin-lang/Odin/issues/7421
package test_issues
import "core:testing"
Issue_7421_Alias :: u16
@(test)
test_issue_7421_tagless_switch_constant_conditions :: proc(t: ^testing.T) {
selected := 0
switch {
case Issue_7421_Alias == u16:
selected = 1
case Issue_7421_Alias == i32:
selected = 2
case Issue_7421_Alias == string:
selected = 3
case:
selected = 4
}
testing.expect_value(t, selected, 1)
}

View File

@@ -0,0 +1,10 @@
// Regression guard for issue #7421: tagged switches still reject duplicate cases.
package test_issues
main :: proc() {
value := false
switch value {
case false:
case false:
}
}