Skip to content

Commit 3d302a6

Browse files
authored
v: add selector option unwrapping inside if tree.root != none { (#22895)
1 parent ae0fdbd commit 3d302a6

10 files changed

+113
-18
lines changed

vlib/v/checker/checker.v

+3-2
Original file line numberDiff line numberDiff line change
@@ -1711,7 +1711,7 @@ fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type {
17111711
if field.is_deprecated && is_used_outside {
17121712
c.deprecate('field', field_name, field.attrs, node.pos)
17131713
}
1714-
if field_sym.kind in [.sum_type, .interface] {
1714+
if field_sym.kind in [.sum_type, .interface] || field.typ.has_flag(.option) {
17151715
if !prevent_sum_type_unwrapping_once {
17161716
if scope_field := node.scope.find_struct_field(node.expr.str(), typ, field_name) {
17171717
return scope_field.smartcasts.last()
@@ -4204,7 +4204,8 @@ fn (mut c Checker) smartcast(mut expr ast.Expr, cur_type ast.Type, to_type_ ast.
42044204
smartcasts << field.smartcasts
42054205
}
42064206
// smartcast either if the value is immutable or if the mut argument is explicitly given
4207-
if !is_mut || expr.is_mut {
4207+
if !is_mut || expr.is_mut
4208+
|| (cur_type.has_flag(.option) && cur_type.clear_flag(.option) == to_type_) {
42084209
smartcasts << to_type
42094210
scope.register_struct_field(expr.expr.str(), ast.ScopeStructField{
42104211
struct_type: expr.expr_type

vlib/v/checker/if.v

+2-1
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,8 @@ fn (mut c Checker) smartcast_if_conds(mut node ast.Expr, mut scope ast.Scope, co
568568
if node.op == .and {
569569
c.smartcast_if_conds(mut node.left, mut scope, control_expr)
570570
c.smartcast_if_conds(mut node.right, mut scope, control_expr)
571-
} else if node.left is ast.Ident && node.op == .ne && node.right is ast.None {
571+
} else if node.left in [ast.Ident, ast.SelectorExpr] && node.op == .ne
572+
&& node.right is ast.None {
572573
if node.left is ast.Ident && c.comptime.get_ct_type_var(node.left) == .smartcast {
573574
node.left_type = c.comptime.get_type(node.left)
574575
c.smartcast(mut node.left, node.left_type, node.left_type.clear_flag(.option), mut

vlib/v/checker/tests/assign_type_mismatch_with_generics_err.out

+7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
vlib/v/checker/tests/assign_type_mismatch_with_generics_err.vv:13:11: error: unexpected `or` block, the field `f` is not an Option or a Result
2+
11 | mut b := false
3+
12 | if f.f != none {
4+
13 | b = f.f or { panic(err) }
5+
| ~~~~~~~~~~~~~~~~~
6+
14 | } else {
7+
15 | b = true
18
vlib/v/checker/tests/assign_type_mismatch_with_generics_err.vv:13:9: error: cannot assign to `b`: expected `bool`, not `fn (Bar) bool`
29
11 | mut b := false
310
12 | if f.f != none {
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
vlib/v/checker/tests/option_ptr_without_unwrapp_err.vv:7:13: error: cannot use `?&Node` as `Node`, it must be unwrapped first in argument 1 to `set_trace`
1+
vlib/v/checker/tests/option_ptr_without_unwrapp_err.vv:6:12: error: cannot use `?&Node` as `Node`, it must be unwrapped first in argument 1 to `set_trace`
2+
4 |
23
5 | fn set_trace(n Node) {
3-
6 | if n.parent != none {
4-
7 | set_trace(n.parent)
5-
| ~~~~~~~~
6-
8 | }
7-
9 | }
4+
6 | set_trace(n.parent)
5+
| ~~~~~~~~
6+
7 | }
7+
8 |

vlib/v/checker/tests/option_ptr_without_unwrapp_err.vv

+1-3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ struct Node {
33
}
44

55
fn set_trace(n Node) {
6-
if n.parent != none {
7-
set_trace(n.parent)
8-
}
6+
set_trace(n.parent)
97
}
108

119
fn main() {

vlib/v/gen/c/cgen.v

+13-3
Original file line numberDiff line numberDiff line change
@@ -4022,23 +4022,30 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
40224022
mut sum_type_deref_field := ''
40234023
mut sum_type_dot := '.'
40244024
mut field_typ := ast.void_type
4025+
mut is_option_unwrap := false
40254026
if f := g.table.find_field_with_embeds(sym, node.field_name) {
40264027
field_sym := g.table.sym(f.typ)
40274028
field_typ = f.typ
40284029
if sym.kind in [.interface, .sum_type] {
40294030
g.write('(*(')
40304031
}
4031-
if field_sym.kind in [.sum_type, .interface] {
4032+
is_option := field_typ.has_flag(.option)
4033+
if field_sym.kind in [.sum_type, .interface] || is_option {
40324034
if !prevent_sum_type_unwrapping_once {
40334035
// check first if field is sum type because scope searching is expensive
40344036
scope := g.file.scope.innermost(node.pos.pos)
40354037
if field := scope.find_struct_field(node.expr.str(), node.expr_type, node.field_name) {
4038+
is_option_unwrap = is_option && field.smartcasts.len > 0
4039+
&& field.typ.clear_flag(.option) == field.smartcasts.last()
40364040
if field.orig_type.is_ptr() {
40374041
sum_type_dot = '->'
40384042
}
40394043
for i, typ in field.smartcasts {
4044+
if i == 0 && is_option_unwrap {
4045+
g.write('(*(${g.styp(typ)}*)')
4046+
}
40404047
g.write('(')
4041-
if field_sym.kind == .sum_type {
4048+
if field_sym.kind == .sum_type && !is_option {
40424049
g.write('*')
40434050
}
40444051
cast_sym := g.table.sym(g.unwrap_generic(typ))
@@ -4047,7 +4054,7 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
40474054
dot := if node.expr_type.is_ptr() { '->' } else { '.' }
40484055
g.write('I_${field_sym.cname}_as_I_${cast_sym.cname}(${ptr}${node.expr}${dot}${node.field_name}))')
40494056
return
4050-
} else {
4057+
} else if !is_option_unwrap {
40514058
if i != 0 {
40524059
dot := if field.typ.is_ptr() { '->' } else { '.' }
40534060
sum_type_deref_field += ')${dot}'
@@ -4194,6 +4201,9 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
41944201
verror('cgen: SelectorExpr | expr_type: 0 | it.expr: `${node.expr}` | field: `${node.field_name}` | file: ${g.file.path} | line: ${node.pos.line_nr}')
41954202
}
41964203
g.write(field_name)
4204+
if is_option_unwrap {
4205+
g.write('.data))')
4206+
}
41974207
if sum_type_deref_field != '' {
41984208
g.write('${sum_type_dot}${sum_type_deref_field})')
41994209
}

vlib/v/tests/generics/generics_chans_select_test.v

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ pub fn (mut ec EventController[T]) emit(e T, options EmitOptions) {
3535
for i, w in ec.wait_fors {
3636
mut b := false
3737
if w.check != none {
38-
func := w.check or { panic(err) }
38+
func := w.check
3939
b = func(e)
4040
} else {
41-
b = true
41+
assert false
4242
}
4343
if b {
4444
w.c.c <- e
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import datatypes { DoublyLinkedList }
2+
3+
pub type LayoutBoxId = usize
4+
5+
pub struct LayoutBox {
6+
}
7+
8+
pub struct LayoutTree {
9+
mut:
10+
root ?LayoutBoxId
11+
boxes []LayoutBox
12+
}
13+
14+
pub fn LayoutTree.new() LayoutTree {
15+
return LayoutTree{
16+
root: ?LayoutBoxId(none)
17+
boxes: []LayoutBox{}
18+
}
19+
}
20+
21+
fn test_main() {
22+
mut tree := LayoutTree.new()
23+
tree.root = 1
24+
if tree.root != none {
25+
mut parents := DoublyLinkedList[LayoutBoxId]{}
26+
parents.push_back(tree.root)
27+
assert parents.len == 1
28+
} else {
29+
assert false
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
type SumType = int | string
2+
3+
struct Struct {
4+
a int
5+
}
6+
7+
struct Foo {
8+
a ?int
9+
b ?string
10+
c ?SumType
11+
d ?Struct
12+
}
13+
14+
fn test_main() {
15+
w := Foo{
16+
a: 123
17+
b: 'foo'
18+
c: SumType(123)
19+
d: Struct{
20+
a: 123
21+
}
22+
}
23+
if w.a != none {
24+
dump(w.a)
25+
assert w.a == 123
26+
} else {
27+
assert false
28+
}
29+
if w.b != none {
30+
dump(w.b)
31+
assert w.b == 'foo'
32+
} else {
33+
assert false
34+
}
35+
if w.c != none {
36+
dump(w.c)
37+
assert w.c is int
38+
} else {
39+
assert false
40+
}
41+
if w.d != none {
42+
dump(w.d)
43+
assert w.d.a == 123
44+
} else {
45+
assert false
46+
}
47+
}

vlib/v/tests/options/option_unwrap_test.v

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub mut:
66

77
fn set_trace(n &Node) int {
88
if n.parent != none {
9-
set_trace(n.parent or { &Node{} })
9+
set_trace(n.parent)
1010
assert n.id != 0
1111
} else {
1212
assert n.id == 1

0 commit comments

Comments
 (0)