From e912854e9e71aa04a7dae9ba947fa919dfdbbaf8 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Thu, 3 Apr 2025 10:21:42 -0300 Subject: [PATCH] checker, cgen: fix selector option unwrapping on infix (fix #24108) (#24115) --- vlib/v/checker/if.v | 3 +- vlib/v/gen/c/cgen.v | 3 +- vlib/v/tests/loops/for_smartcast_test.v | 2 +- vlib/v/tests/options/option_if_unwrap_test.v | 54 +++++++++++++++++++ vlib/v/tests/options/option_ptr_unwrap_test.v | 4 +- .../option_struct_init_with_ref_opt_test.v | 2 +- vlib/v/type_resolver/comptime_resolver.v | 5 ++ 7 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 vlib/v/tests/options/option_if_unwrap_test.v diff --git a/vlib/v/checker/if.v b/vlib/v/checker/if.v index 543cfb7f4..e33801af5 100644 --- a/vlib/v/checker/if.v +++ b/vlib/v/checker/if.v @@ -691,7 +691,8 @@ fn (mut c Checker) smartcast_if_conds(mut node ast.Expr, mut scope ast.Scope, co mut first_cond := control_expr.branches[0].cond // handles unwrapping on if var == none { /**/ } else { /*unwrapped var*/ } if mut first_cond is ast.InfixExpr { - if first_cond.left is ast.Ident && first_cond.op == .eq && first_cond.right is ast.None { + if first_cond.left in [ast.Ident, ast.SelectorExpr] && first_cond.op == .eq + && first_cond.right is ast.None { if c.comptime.get_ct_type_var(first_cond.left) == .smartcast { first_cond.left_type = c.type_resolver.get_type(first_cond.left) c.smartcast(mut first_cond.left, first_cond.left_type, first_cond.left_type.clear_flag(.option), mut diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 83181ab0e..2850c1112 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -4185,7 +4185,8 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { for i, typ in field.smartcasts { if i == 0 && (is_option_unwrap || nested_unwrap) { deref := if g.inside_selector { - if is_iface_or_sumtype { + if is_iface_or_sumtype || (field.orig_type.is_ptr() && g.left_is_opt + && is_option_unwrap) { '*'.repeat(field.smartcasts.last().nr_muls()) } else { '*'.repeat(field.smartcasts.last().nr_muls() + 1) diff --git a/vlib/v/tests/loops/for_smartcast_test.v b/vlib/v/tests/loops/for_smartcast_test.v index 393a0652d..ea327cb92 100644 --- a/vlib/v/tests/loops/for_smartcast_test.v +++ b/vlib/v/tests/loops/for_smartcast_test.v @@ -16,7 +16,7 @@ fn test_nested_sumtype_selector() { pos: 1 }))} for c.node is Expr { - assert typeof(c.node).name == 'Node' + assert typeof(c.node).name == 'Expr' break } } diff --git a/vlib/v/tests/options/option_if_unwrap_test.v b/vlib/v/tests/options/option_if_unwrap_test.v new file mode 100644 index 000000000..7e41fe191 --- /dev/null +++ b/vlib/v/tests/options/option_if_unwrap_test.v @@ -0,0 +1,54 @@ +fn unwrap(a ?[3]u8) { + if a == none { + println('${@FN}:${@LINE}: ${typeof(a).name}') + assert typeof(a).name == '?[3]u8' + } else { + println('${@FN}:${@LINE}: ${typeof(a).name}') + assert typeof(a).name == '[3]u8' + } + if a != none { + println('${@FN}:${@LINE}: ${typeof(a).name}') + assert typeof(a).name == '[3]u8' + } else { + println('${@FN}:${@LINE}: ${typeof(a).name}') + assert typeof(a).name == '?[3]u8' + } +} + +struct Opt { +pub mut: + f ?[3]u8 +} + +fn unwrap_field(opt Opt) { + if opt.f == none { + println('${@FN}:${@LINE}: ${typeof(opt.f).name}') + assert opt.f == ?[3]u8(none) + assert typeof(opt.f).name == '?[3]u8' + } else { + println('${@FN}:${@LINE}: ${typeof(opt.f).name}') + assert opt.f == [u8(2), 2, 2]! + assert typeof(opt.f).name == '[3]u8' + } + if opt.f != none { + println('${@FN}:${@LINE}: ${typeof(opt.f).name}') + assert opt.f == [u8(2), 2, 2]! + assert typeof(opt.f).name == '[3]u8' + } else { + println('${@FN}:${@LINE}: ${typeof(opt.f).name}') + assert opt.f == ?[3]u8(none) + assert typeof(opt.f).name == '?[3]u8' + } +} + +fn test_main() { + mut a := ?[3]u8(none) + unwrap(a) + a = [3]u8{init: 1} + unwrap(a) + + mut f := Opt{} + unwrap_field(f) + f.f = [3]u8{init: 2} + unwrap_field(f) +} diff --git a/vlib/v/tests/options/option_ptr_unwrap_test.v b/vlib/v/tests/options/option_ptr_unwrap_test.v index d0334e8b4..4e10a054a 100644 --- a/vlib/v/tests/options/option_ptr_unwrap_test.v +++ b/vlib/v/tests/options/option_ptr_unwrap_test.v @@ -27,9 +27,9 @@ fn (mut li LinkedList[T]) add[T](data T) ? { node.prev = node } else { node.next = li.head - node.prev = li.head?.prev + node.prev = li.head.prev node.prev?.next = node - li.head?.prev = node + li.head.prev = node } li.size += 1 diff --git a/vlib/v/tests/options/option_struct_init_with_ref_opt_test.v b/vlib/v/tests/options/option_struct_init_with_ref_opt_test.v index b618ff9c2..d9d0d0e64 100644 --- a/vlib/v/tests/options/option_struct_init_with_ref_opt_test.v +++ b/vlib/v/tests/options/option_struct_init_with_ref_opt_test.v @@ -31,7 +31,7 @@ pub fn (mut l LinkedList) pop() int { l.head = none l.tail = none } else { // multiple elements - mut prev := tail.prev or { panic('prev cannot be none at this point') } + mut prev := tail.prev prev.next = none l.tail = prev } diff --git a/vlib/v/type_resolver/comptime_resolver.v b/vlib/v/type_resolver/comptime_resolver.v index 53de0f516..e3eed8a58 100644 --- a/vlib/v/type_resolver/comptime_resolver.v +++ b/vlib/v/type_resolver/comptime_resolver.v @@ -75,6 +75,11 @@ pub fn (mut t TypeResolver) typeof_type(node ast.Expr, default_type ast.Type) as if node.expr is ast.Ident && node.is_field_typ { return t.get_type_from_comptime_var(node.expr) } + if field := node.scope.find_struct_field(node.expr.str(), node.expr_type, node.field_name) { + if field.smartcasts.len > 0 { + return field.smartcasts.last() + } + } sym := t.table.sym(t.resolver.unwrap_generic(node.expr_type)) if f := t.table.find_field_with_embeds(sym, node.field_name) { return f.typ -- 2.39.5