From 1c34ac6eda831af9cea273ff6c6a2844ef3fcf96 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Fri, 8 May 2026 15:00:29 +0300 Subject: [PATCH] cgen: pointer compare instead of deep struct_eq when one side is &lvalue (fixes #27089) --- vlib/v/gen/c/infix.v | 12 ++++++++- .../struct_address_of_field_comparison_test.v | 27 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 vlib/v/tests/structs/struct_address_of_field_comparison_test.v diff --git a/vlib/v/gen/c/infix.v b/vlib/v/gen/c/infix.v index b223f6a55..c1fb2a4ac 100644 --- a/vlib/v/gen/c/infix.v +++ b/vlib/v/gen/c/infix.v @@ -428,7 +428,17 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) { .struct { ptr_typ := g.equality_fn(left.unaliased) if left.typ.is_ptr() || right.typ.is_ptr() { - g.gen_struct_pointer_eq_op(node, left_type, right_type, ptr_typ) + // `&lvalue` on either side means the user is comparing addresses; skip the deep `_struct_eq` (`&StructInit{}` still does deep eq). + left_is_addr_of_lvalue := node.left is ast.PrefixExpr && node.left.op == .amp + && node.left.right.is_lvalue() + right_is_addr_of_lvalue := node.right is ast.PrefixExpr && node.right.op == .amp + && node.right.right.is_lvalue() + if left.typ.is_ptr() && right.typ.is_ptr() + && (left_is_addr_of_lvalue || right_is_addr_of_lvalue) { + g.gen_plain_infix_expr(node) + } else { + g.gen_struct_pointer_eq_op(node, left_type, right_type, ptr_typ) + } } else { if node.op == .ne { g.write('!') diff --git a/vlib/v/tests/structs/struct_address_of_field_comparison_test.v b/vlib/v/tests/structs/struct_address_of_field_comparison_test.v new file mode 100644 index 000000000..f25bca6cf --- /dev/null +++ b/vlib/v/tests/structs/struct_address_of_field_comparison_test.v @@ -0,0 +1,27 @@ +// Regression test for https://github.com/vlang/v/issues/27089 +// Comparing a stored reference with `&expr` should compare addresses, +// not perform a deep struct equality check. +struct Data { +mut: + a f32 + b int + c string +} + +struct Holder { +mut: + buf Data + ref &Data = unsafe { nil } +} + +fn test_address_of_field_compares_addresses() { + mut t := Holder{} + // `t.ref` is nil, so address comparison must be true (nil != &t.buf). + assert t.ref != &t.buf + t.ref = &t.buf + // Now both refer to the same address. + assert t.ref == &t.buf + other := Data{} + // Different addresses with structurally equal contents must still be unequal. + assert &other != &t.buf +} -- 2.39.5