From c7e00b395b3b5f4ed39ef658cc5a1610d3501118 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Fri, 25 Apr 2025 00:55:51 -0300 Subject: [PATCH] cgen: fix codegen for nested selector unwrapping on lhs (fix #24292) (#24293) --- vlib/v/gen/c/cgen.v | 19 ++++++-- .../options/option_selector_unwrapping_test.v | 44 +++++++++++++++++++ 2 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 vlib/v/tests/options/option_selector_unwrapping_test.v diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 355c5c351..213ff9ad1 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -153,6 +153,7 @@ mut: inside_cast_in_heap int // inside cast to interface type in heap (resolve recursive calls) inside_cast bool inside_selector bool + inside_selector_deref bool // indicates if the inside selector was already dereferenced inside_memset bool inside_const bool inside_array_item bool @@ -4173,9 +4174,9 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { deref := if g.inside_selector { if is_iface_or_sumtype || (field.orig_type.is_ptr() && g.left_is_opt && is_option_unwrap) { - '*'.repeat(field.smartcasts.last().nr_muls()) + '*'.repeat(typ.nr_muls()) } else { - '*'.repeat(field.smartcasts.last().nr_muls() + 1) + '*'.repeat(typ.nr_muls() + 1) } } else if sym.kind == .interface && !typ.is_ptr() && field.orig_type.has_flag(.option) { @@ -4183,7 +4184,14 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { } else { '*' } - g.write('(${deref}(${g.styp(typ)}*)') + suffix := if g.inside_selector && field.orig_type.has_flag(.option) + && field.orig_type.is_ptr() && g.is_assign_lhs { + '' + } else { + '*' + } + g.inside_selector_deref = suffix == '' && deref.len > 0 + g.write('(${deref}(${g.styp(typ)}${suffix})') } if i == 0 || !nested_unwrap { g.write('(') @@ -4289,6 +4297,7 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { g.write('((${g.base_type(field_typ)})') } old_inside_selector := g.inside_selector + old_inside_selector_deref := g.inside_selector_deref g.inside_selector = node.expr is ast.SelectorExpr && node.expr.expr is ast.Ident n_ptr := node.expr_type.nr_muls() - 1 if n_ptr > 0 { @@ -4298,7 +4307,9 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { } else { g.expr(node.expr) } + mut opt_ptr_already_deref := g.inside_selector_deref g.inside_selector = old_inside_selector + g.inside_selector_deref = old_inside_selector_deref if field_is_opt { g.write(')') } @@ -4350,7 +4361,7 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { || (((!is_dereferenced && unwrapped_expr_type.is_ptr()) || sym.kind == .chan || alias_to_ptr) && node.from_embed_types.len == 0) || (node.expr.is_as_cast() && g.inside_smartcast) - if !has_embed && left_is_ptr { + if !has_embed && left_is_ptr && !(opt_ptr_already_deref && !g.inside_selector) { g.write('->') } else { g.write('.') diff --git a/vlib/v/tests/options/option_selector_unwrapping_test.v b/vlib/v/tests/options/option_selector_unwrapping_test.v new file mode 100644 index 000000000..01e32070a --- /dev/null +++ b/vlib/v/tests/options/option_selector_unwrapping_test.v @@ -0,0 +1,44 @@ +module main + +@[heap] +interface IGameObject { +mut: + name string + parent ?&IGameObject + next ?&IGameObject + child ?&IGameObject + last_child ?&IGameObject + add_child(mut o IGameObject) +} + +@[heap] +struct GameObject implements IGameObject { +mut: + name string + parent ?&IGameObject + next ?&IGameObject + child ?&IGameObject + last_child ?&IGameObject +} + +fn (mut gameobject GameObject) add_child(mut o IGameObject) { + o.parent = gameobject + if gameobject.last_child != none { + gameobject.last_child.next = o + } else { + gameobject.child = o + } + gameobject.last_child = o +} + +fn test_main() { + mut v1 := &GameObject{ + name: 'v1' + } + mut v2 := &GameObject{ + name: 'v2' + } + v1.add_child(mut v2) + assert v1.child? == IGameObject(v2) + assert v1.last_child? == IGameObject(v2) +} -- 2.39.5