From 7270d5439e5d2a0f87c33ec8b423b2eddf953d7a Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 15 Apr 2026 15:17:33 +0300 Subject: [PATCH] checker: fix nested `if` expression causing compilation error (fixes #26865) --- vlib/v/ast/ast.v | 11 ++++++----- vlib/v/checker/if.v | 2 +- vlib/v/parser/expr.v | 15 +++++++++++++++ vlib/v/tests/conditions/ifs/if_expression_test.v | 10 ++++++++++ 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index e8e49f255..dfff71d53 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -1337,11 +1337,12 @@ pub: pos token.Pos post_comments []Comment pub mut: - left Expr // `a` in `a := if ...` - branches []IfBranch // includes all `else if` branches - is_expr bool - typ Type - has_else bool + left Expr // `a` in `a := if ...` + branches []IfBranch // includes all `else if` branches + is_expr bool + force_expr bool + typ Type + has_else bool // implements bool // comptime $if implements interface } diff --git a/vlib/v/checker/if.v b/vlib/v/checker/if.v index df4e6fc8e..84912eacd 100644 --- a/vlib/v/checker/if.v +++ b/vlib/v/checker/if.v @@ -127,7 +127,7 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type { if c.expected_type == ast.void_type && node_is_expr { c.expected_type = c.expected_or_type } - expr_required := c.expected_type != ast.void_type + expr_required := node.force_expr || c.expected_type != ast.void_type || (node.is_comptime && node.is_expr && node.has_else && c.fn_level > 0) former_expected_type := c.expected_type if node_is_expr { diff --git a/vlib/v/parser/expr.v b/vlib/v/parser/expr.v index 3dbd07cbf..09daa1292 100644 --- a/vlib/v/parser/expr.v +++ b/vlib/v/parser/expr.v @@ -668,6 +668,7 @@ fn (mut p Parser) expr_with_left(left ast.Expr, precedence int, is_stmt_ident bo for { if p.tok.kind == .lpar && p.tok.line_nr == p.prev_tok.line_nr && node in [ast.CallExpr, ast.IndexExpr, ast.SelectorExpr] { + p.promote_if_expr_to_value(mut node) node = p.call_expr_with_left(node) p.is_stmt_ident = is_stmt_ident continue @@ -681,6 +682,7 @@ fn (mut p Parser) expr_with_left(left ast.Expr, precedence int, is_stmt_ident bo && p.tok.pos - p.prev_tok.pos > p.prev_tok.len { return node } + p.promote_if_expr_to_value(mut node) node = p.dot_expr(node) if p.name_error { return node @@ -689,6 +691,7 @@ fn (mut p Parser) expr_with_left(left ast.Expr, precedence int, is_stmt_ident bo } else if left !is ast.IntegerLiteral && p.tok.kind in [.lsbr, .nilsbr] && (p.tok.line_nr == p.prev_tok.line_nr || (p.prev_tok.kind == .string && p.tok.line_nr == p.prev_tok.line_nr + p.prev_tok.lit.count('\n'))) { + p.promote_if_expr_to_value(mut node) if p.peek_tok.kind == .question && p.peek_token(2).kind == .name { p.next() p.error_with_pos('cannot use Option type name as concrete type', p.tok.pos()) @@ -702,6 +705,7 @@ fn (mut p Parser) expr_with_left(left ast.Expr, precedence int, is_stmt_ident bo // sum type as cast `x := SumType as Variant` if !p.inside_asm { pos := p.tok.pos() + p.promote_if_expr_to_value(mut node) p.next() typ := p.parse_type() node = ast.AsCast{ @@ -736,6 +740,7 @@ fn (mut p Parser) expr_with_left(left ast.Expr, precedence int, is_stmt_ident bo && !(p.tok.kind in [.minus, .amp, .mul, .arrow, .key_as, .key_in, .key_is] && p.tok.line_nr != p.prev_tok.line_nr) { // continue on infix expr + p.promote_if_expr_to_value(mut node) node = p.infix_expr(node) // return early `if bar is SumType as b {` if p.tok.kind == .key_as && p.inside_if { @@ -801,6 +806,16 @@ fn (mut p Parser) expr_with_left(left ast.Expr, precedence int, is_stmt_ident bo return node } +fn (mut p Parser) promote_if_expr_to_value(mut expr ast.Expr) { + match mut expr { + ast.IfExpr { + expr.is_expr = true + expr.force_expr = true + } + else {} + } +} + fn (mut p Parser) call_expr_with_left(left ast.Expr) ast.CallExpr { p.next() pos := p.tok.pos() diff --git a/vlib/v/tests/conditions/ifs/if_expression_test.v b/vlib/v/tests/conditions/ifs/if_expression_test.v index 9287fd547..8d13b16ed 100644 --- a/vlib/v/tests/conditions/ifs/if_expression_test.v +++ b/vlib/v/tests/conditions/ifs/if_expression_test.v @@ -170,6 +170,16 @@ fn test_multi_if_expr_with_infix() { assert a == 7 } +fn test_nested_if_expr_with_infix_in_branch() { + a := if false { 0 } else { if false { 2 } else { 3 } + 4 + } + 5 + assert a == 12 + + b := if false { 'a' } else { if false { 'c' } else { 'd' } + 'e' + } + 'f' + assert b == 'def' +} + fn test_if_expr_with_array_map() { num_string := '2 3' -- 2.39.5