From 88f737e23e8ce600fba1f6b7f264649a633f4ed2 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 11 Mar 2026 11:49:31 +0300 Subject: [PATCH] checker: error: duplicate case value (fixes #25240) --- vlib/v/checker/match.v | 23 ++++++++++- vlib/v/gen/c/match.v | 5 ++- .../enum_allow_multiple_values_match_test.v | 38 +++++++++++++++++++ 3 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 vlib/v/tests/enums/enum_allow_multiple_values_match_test.v diff --git a/vlib/v/checker/match.v b/vlib/v/checker/match.v index b79fb152a..9c63b8b7a 100644 --- a/vlib/v/checker/match.v +++ b/vlib/v/checker/match.v @@ -604,6 +604,9 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym // branch_exprs is a histogram of how many times // an expr was used in the match mut branch_exprs := map[string]int{} + is_multi_allowed_enum_match := cond_type_sym.info is ast.Enum + && cond_type_sym.info.is_multi_allowed + mut branch_enum_values := map[i64]bool{} for branch_i, _ in node.branches { mut branch := node.branches[branch_i] mut expr_types := []ast.TypeNode{} @@ -684,6 +687,9 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym c.error('match case `${key}` is handled more than once', branch.pos) } branch_exprs[key] = val + 1 + if is_multi_allowed_enum_match { + branch_enum_values[i] = true + } } } continue @@ -696,6 +702,13 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym } ast.EnumVal { key = expr.val + if is_multi_allowed_enum_match { + if enum_val := c.table.find_enum_field_val(cond_type_sym.name, + expr.val) + { + branch_enum_values[enum_val] = true + } + } if !enum_ref_checked { enum_ref_checked = true if node.cond_type.is_ptr() { @@ -883,7 +896,15 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym // ast.Enum { for v in cond_type_sym.info.vals { - if v !in branch_exprs { + mut is_handled := v in branch_exprs + if !is_handled && is_multi_allowed_enum_match { + if enum_val := c.table.find_enum_field_val(cond_type_sym.name, + v) + { + is_handled = enum_val in branch_enum_values + } + } + if !is_handled { is_exhaustive = false unhandled << '`.${v}`' } diff --git a/vlib/v/gen/c/match.v b/vlib/v/gen/c/match.v index af929857f..546223ce6 100644 --- a/vlib/v/gen/c/match.v +++ b/vlib/v/gen/c/match.v @@ -127,6 +127,7 @@ fn (mut g Gen) match_expr(node ast.MatchExpr) { g.match_expr_sumtype(node, is_expr, cond_var, tmp_var) } else { cond_fsym := g.table.final_sym(node.cond_type) + enum_is_multi_allowed := cond_fsym.info is ast.Enum && cond_fsym.info.is_multi_allowed mut can_be_a_switch := true all_branches: for branch in node.branches { for expr in branch.exprs { @@ -145,10 +146,10 @@ fn (mut g Gen) match_expr(node ast.MatchExpr) { } // eprintln('> can_be_a_switch: ${can_be_a_switch}') if can_be_a_switch && !is_expr && g.loop_depth == 0 && g.fn_decl != unsafe { nil } - && cond_fsym.is_int() { + && cond_fsym.is_int() && !enum_is_multi_allowed { g.match_expr_switch(node, is_expr, cond_var, tmp_var, cond_fsym) } else if cond_fsym.kind == .enum && g.loop_depth == 0 && node.branches.len > 5 - && g.fn_decl != unsafe { nil } { + && g.fn_decl != unsafe { nil } && !enum_is_multi_allowed { // do not optimize while in top-level g.match_expr_switch(node, is_expr, cond_var, tmp_var, cond_fsym) } else { diff --git a/vlib/v/tests/enums/enum_allow_multiple_values_match_test.v b/vlib/v/tests/enums/enum_allow_multiple_values_match_test.v new file mode 100644 index 000000000..700a34a92 --- /dev/null +++ b/vlib/v/tests/enums/enum_allow_multiple_values_match_test.v @@ -0,0 +1,38 @@ +@[_allow_multiple_values] +enum Kind { + placeholder + i32 + int = 9 + i64 = 9 + isize + u8 +} + +fn classify_with_aliases(x Kind) string { + return match x { + .placeholder { 'placeholder' } + .i32 { 'i32' } + .int { 'int' } + .i64 { 'i64' } + .isize { 'isize' } + .u8 { 'u8' } + } +} + +fn classify_without_alias(x Kind) string { + return match x { + .placeholder { 'placeholder' } + .i32 { 'i32' } + .i64 { 'nine' } + .isize { 'isize' } + .u8 { 'u8' } + } +} + +fn test_match_with_allow_multiple_values_enum() { + assert classify_with_aliases(.placeholder) == 'placeholder' + assert classify_with_aliases(.int) == 'int' + assert classify_with_aliases(.i64) == 'int' + assert classify_without_alias(.int) == 'nine' + assert classify_without_alias(.i64) == 'nine' +} -- 2.39.5