From 1f5df6f827d46728321ed9fec3fe7d53c7b9df04 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 11 Mar 2026 14:17:10 +0300 Subject: [PATCH] checker: sumtypes: V sometimes thinks 1 == &1 (hidden dereferencing that shouldn't be there?) (fixes #11543) --- vlib/v/checker/fn.v | 10 ++-- vlib/v/checker/infix.v | 46 +++++++++++++++++-- .../tests/infix_value_ref_comparison_err.out | 14 ++++++ .../tests/infix_value_ref_comparison_err.vv | 20 ++++++++ .../v/tests/infix_autoderef_comparison_test.v | 8 ++++ 5 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 vlib/v/checker/tests/infix_value_ref_comparison_err.out create mode 100644 vlib/v/checker/tests/infix_value_ref_comparison_err.vv create mode 100644 vlib/v/tests/infix_autoderef_comparison_test.v diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index c7c7bf7eb..3793bb520 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -2199,8 +2199,9 @@ fn (mut c Checker) check_type_sym_kind(name string, type_idx int, expected_kind parent_type := (sym.info as ast.Alias).parent_type sym = c.table.sym(parent_type) } - if sym.kind != expected_kind { - c.error('expected ${expected_kind}, but `${name}` is ${sym.kind}', pos) + expected_kind_value := *expected_kind + if sym.kind != expected_kind_value { + c.error('expected ${expected_kind_value}, but `${name}` is ${sym.kind}', pos) return false } return true @@ -2213,8 +2214,9 @@ fn (mut c Checker) check_type_and_visibility(name string, type_idx int, expected parent_type := (sym.info as ast.Alias).parent_type sym = c.table.sym(parent_type) } - if sym.kind != expected_kind { - c.error('expected ${expected_kind}, but `${name}` is ${sym.kind}', pos) + expected_kind_value := *expected_kind + if sym.kind != expected_kind_value { + c.error('expected ${expected_kind_value}, but `${name}` is ${sym.kind}', pos) return false } if !sym.is_pub { diff --git a/vlib/v/checker/infix.v b/vlib/v/checker/infix.v index 934204394..c9b8014e3 100644 --- a/vlib/v/checker/infix.v +++ b/vlib/v/checker/infix.v @@ -3,6 +3,18 @@ module checker import v.ast import v.token +fn infix_expr_allows_auto_deref(expr ast.Expr) bool { + return expr is ast.Ident && expr.is_auto_deref_var() +} + +fn infix_expr_is_zero_integer_literal(expr ast.Expr) bool { + return expr is ast.IntegerLiteral && expr.val == '0' +} + +fn infix_expr_is_nil_like(expr ast.Expr) bool { + return expr.is_nil() || (expr is ast.UnsafeExpr && expr.expr.is_nil()) +} + fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { former_expected_type := c.expected_type defer { @@ -900,16 +912,20 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { } types_match := c.symmetric_check(left_type, right_type) && c.symmetric_check(right_type, left_type) + left_allows_auto_deref := infix_expr_allows_auto_deref(node.left) + right_allows_auto_deref := infix_expr_allows_auto_deref(node.right) + pointer_value_mismatch := left_type.is_any_kind_of_pointer() != right_type.is_any_kind_of_pointer() mut types_match_after_deref := false mut deref_left_type := left_type mut deref_right_type := right_type - if !types_match && (node.left.is_auto_deref_var() || node.right.is_auto_deref_var()) { - deref_left_type = if node.left.is_auto_deref_var() { + if (!types_match || pointer_value_mismatch) + && (left_allows_auto_deref || right_allows_auto_deref) { + deref_left_type = if left_allows_auto_deref { left_type.deref() } else { left_type } - deref_right_type = if node.right.is_auto_deref_var() { + deref_right_type = if right_allows_auto_deref { right_type.deref() } else { right_type @@ -917,6 +933,30 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { types_match_after_deref = c.symmetric_check(deref_left_type, deref_right_type) && c.symmetric_check(deref_right_type, deref_left_type) } + if node.op in [.eq, .ne] && right_type.is_any_kind_of_pointer() + && !left_type.is_any_kind_of_pointer() && !infix_expr_is_zero_integer_literal(node.left) + && !infix_expr_is_nil_like(node.left) && !infix_expr_is_nil_like(node.right) + && !c.file.path.ends_with('.c.v') && left_sym.language == .v && right_sym.language == .v + && !types_match_after_deref && !c.pref.translated && !c.file.is_translated + && !c.inside_unsafe { + error_left_name := if left_allows_auto_deref { + c.table.type_to_str(deref_left_type) + } else { + c.table.type_to_str(left_type) + } + error_right_name := if right_allows_auto_deref { + c.table.type_to_str(deref_right_type) + } else { + c.table.type_to_str(right_type) + } + sugg := if left_type in ast.integer_type_idxs || right_type in ast.integer_type_idxs { + ' (you can use it inside an `unsafe` block)' + } else { + '' + } + c.error('infix expr: cannot use `${error_right_name}` (right expression) as `${error_left_name}`${sugg}', + left_right_pos) + } if !types_match && !types_match_after_deref && !c.pref.translated && !c.file.is_translated { // for type-unresolved consts if left_type == ast.void_type || right_type == ast.void_type { diff --git a/vlib/v/checker/tests/infix_value_ref_comparison_err.out b/vlib/v/checker/tests/infix_value_ref_comparison_err.out new file mode 100644 index 000000000..0aa3367c0 --- /dev/null +++ b/vlib/v/checker/tests/infix_value_ref_comparison_err.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/infix_value_ref_comparison_err.vv:6:6: error: infix expr: cannot use `&int` (right expression) as `int` (you can use it inside an `unsafe` block) + 4 | value := 1 + 5 | reference := &value + 6 | _ = value == reference + | ~~~~~~~~~~~~~~~~~~ + 7 | } + 8 | +vlib/v/checker/tests/infix_value_ref_comparison_err.vv:14:8: error: infix expr: cannot use `&int` (right expression) as `int` (you can use it inside an `unsafe` block) + 12 | match a { + 13 | int { + 14 | _ = a == &b + | ~~~~~~ + 15 | } + 16 | else {} diff --git a/vlib/v/checker/tests/infix_value_ref_comparison_err.vv b/vlib/v/checker/tests/infix_value_ref_comparison_err.vv new file mode 100644 index 000000000..c0a697450 --- /dev/null +++ b/vlib/v/checker/tests/infix_value_ref_comparison_err.vv @@ -0,0 +1,20 @@ +type Abc = int | string + +fn plain_value_ref_comparison() { + value := 1 + reference := &value + _ = value == reference +} + +fn sumtype_value_ref_comparison() { + b := 1 + a := Abc(b) + match a { + int { + _ = a == &b + } + else {} + } +} + +fn main() {} diff --git a/vlib/v/tests/infix_autoderef_comparison_test.v b/vlib/v/tests/infix_autoderef_comparison_test.v new file mode 100644 index 000000000..1d1350880 --- /dev/null +++ b/vlib/v/tests/infix_autoderef_comparison_test.v @@ -0,0 +1,8 @@ +fn compare_int_arrays(mut left []int, right []int) bool { + return left == right +} + +fn test_mut_array_arg_comparison_still_works() { + mut left := [1, 2] + assert compare_int_arrays(mut left, [1, 2]) +} -- 2.39.5