From 498584ccd293805fa708742a812911d3caeac657 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 17 Jun 2026 10:30:47 +0300 Subject: [PATCH] cgen: dedup sumtype match-arm tags to avoid -Wduplicated-cond With alias/runtime-tag matching (77b4117c5), a sumtype variant and an alias of it that is also a variant expand to the same set of runtime tags. When both appear as distinct match arms (e.g. `ComptTimeConstValue` has both `u8` and `type EmptyExpr = u8`), each arm compiled to the same `_typ == ...` condition, producing a duplicated `else if` -- a -Wduplicated-cond error under -cstrict, and an unreachable later arm. Track the tags already handled by earlier arms of a sumtype match and drop them from later arms; once an arm has no fresh tags left it is unreachable, so skip it entirely. Ternary `?:` match chains keep every tag (an arm there cannot be dropped without breaking the chain). Runtime behaviour is unchanged -- the dropped arm was already shadowed by the earlier one. --- vlib/v/gen/c/match.v | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/vlib/v/gen/c/match.v b/vlib/v/gen/c/match.v index dd95620e5..e9896ae85 100644 --- a/vlib/v/gen/c/match.v +++ b/vlib/v/gen/c/match.v @@ -194,6 +194,14 @@ fn (mut g Gen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var str dot_or_ptr := g.dot_or_ptr(node.cond_type) use_ternary := is_expr && tmp_var == '' cond_sym := g.table.final_sym(node.cond_type) + // Tracks the runtime type tags already handled by earlier arms of this match. + // With alias/runtime-tag matching, a variant and an alias of it (e.g. `u8` and + // `type EmptyExpr = u8`, both variants of the same sumtype) expand to the same + // set of tags, so distinct arms can produce identical `_typ == ...` conditions. + // Emitting both yields a duplicated `else if` condition (a -Wduplicated-cond + // error under -cstrict) and the later arm is unreachable anyway, so drop the + // already-covered tags and skip an arm once nothing fresh remains. + mut seen_tag_idxs := map[string]bool{} for j, branch in node.branches { mut sumtype_index := 0 // iterates through all types in sumtype branches @@ -210,6 +218,36 @@ fn (mut g Gen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var str branch_type = branch_expr.typ } } + mut sumtype_fresh_idx_exprs := []string{} + if !branch.is_else && cond_sym.kind == .sum_type && sumtype_index < branch.exprs.len { + ce := unsafe { &branch.exprs[sumtype_index] } + if ce is ast.TypeNode { + variant_type := g.unwrap_generic(g.recheck_concrete_type(ce.typ)) + all_idx_exprs := g.matching_sumtype_variant_type_idx_exprs(node.cond_type, + variant_type) + if use_ternary { + // Ternary `?:` chains cannot drop an arm, so keep every tag. + sumtype_fresh_idx_exprs = all_idx_exprs.clone() + } else { + for idx_expr in all_idx_exprs { + if idx_expr !in seen_tag_idxs { + sumtype_fresh_idx_exprs << idx_expr + } + } + if sumtype_fresh_idx_exprs.len == 0 { + // every tag of this arm is already handled above: unreachable, skip it + sumtype_index++ + if branch.exprs.len == 0 || sumtype_index == branch.exprs.len { + break + } + continue + } + for idx_expr in sumtype_fresh_idx_exprs { + seen_tag_idxs[idx_expr] = true + } + } + } + } if branch.is_else || (use_ternary && is_last) { if use_ternary { // TODO: too many branches. maybe separate ?: matches @@ -248,9 +286,7 @@ fn (mut g Gen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var str if cur_expr is ast.None { g.write('${tag_expr} == ${ast.none_type.idx()} /* none */') } else if cur_expr is ast.TypeNode { - variant_type := g.unwrap_generic(g.recheck_concrete_type(cur_expr.typ)) - g.write_type_tag_condition(tag_expr, '==', g.matching_sumtype_variant_type_idx_exprs(node.cond_type, - variant_type)) + g.write_type_tag_condition(tag_expr, '==', sumtype_fresh_idx_exprs) } else { g.write('${tag_expr} == ') g.expr(cur_expr) -- 2.39.5