Skip to content

Commit 56739fe

Browse files
authored
checker,cgen: allow init of struct from struct with array of optional values (fix #26000) (#26005)
1 parent bb39b59 commit 56739fe

File tree

4 files changed

+98
-4
lines changed

4 files changed

+98
-4
lines changed

vlib/v/checker/checker.v

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1467,6 +1467,7 @@ fn (mut c Checker) check_expr_option_or_result_call(expr ast.Expr, ret_type ast.
14671467
expr)
14681468
c.cur_or_expr = last_cur_or_expr
14691469
}
1470+
return ret_type.clear_flag(.result)
14701471
} else if expr.left is ast.SelectorExpr && expr.left_type.has_option_or_result() {
14711472
with_modifier_kind := if expr.left_type.has_flag(.option) {
14721473
'an Option'
@@ -5342,7 +5343,7 @@ fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type {
53425343
c.expected_or_type = typ
53435344
}
53445345
c.stmts_ending_with_expression(mut node.or_expr.stmts, c.expected_or_type)
5345-
c.check_expr_option_or_result_call(node, typ)
5346+
typ = c.check_expr_option_or_result_call(node, typ)
53465347
node.typ = typ
53475348
return typ
53485349
}

vlib/v/checker/struct.v

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -718,10 +718,17 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini
718718
exp_type_is_option := exp_type.has_flag(.option)
719719
if !exp_type_is_option {
720720
got_type = c.check_expr_option_or_result_call(init_field.expr, got_type)
721-
if got_type.has_flag(.option) {
721+
init_field.typ = got_type
722+
has_or_block := match mut init_field.expr {
723+
ast.IndexExpr { init_field.expr.or_expr.kind != .absent }
724+
ast.CallExpr { init_field.expr.or_block.kind != .absent }
725+
ast.SelectorExpr { init_field.expr.or_block.kind != .absent }
726+
else { false }
727+
}
728+
if got_type.has_flag(.option) && !has_or_block {
722729
c.error('cannot assign an Option value to a non-option struct field',
723730
init_field.pos)
724-
} else if got_type.has_flag(.result) {
731+
} else if got_type.has_flag(.result) && !has_or_block {
725732
c.error('cannot assign a Result value to a non-option struct field',
726733
init_field.pos)
727734
}

vlib/v/gen/c/index.v

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,11 +171,28 @@ fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) {
171171
info := sym.info as ast.Array
172172
elem_type := info.elem_type
173173
elem_sym := g.table.final_sym(elem_type)
174+
result_type := match true {
175+
gen_or && elem_type.has_flag(.option) {
176+
node.typ.clear_flag(.option)
177+
}
178+
gen_or {
179+
node.typ
180+
}
181+
else {
182+
elem_type
183+
}
184+
}
185+
result_sym := g.table.final_sym(result_type)
174186
elem_type_str := if elem_sym.kind == .function {
175187
'voidptr'
176188
} else {
177189
g.styp(info.elem_type)
178190
}
191+
result_type_str := if result_sym.kind == .function {
192+
'voidptr'
193+
} else {
194+
g.styp(result_type)
195+
}
179196
left_is_shared := node.left_type.has_flag(.shared_f)
180197
// `vals[i].field = x` is an exception and requires `array_get`:
181198
// `(*(Val*)array_get(vals, i)).field = x;`
@@ -334,7 +351,16 @@ fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) {
334351
opt_elem_type := g.styp(elem_type.set_flag(.option))
335352
g.writeln('${opt_elem_type} ${tmp_opt} = {0};')
336353
g.writeln('if (${tmp_opt_ptr}) {')
337-
g.writeln('\t*((${elem_type_str}*)&${tmp_opt}.data) = *((${elem_type_str}*)${tmp_opt_ptr});')
354+
if elem_type.has_flag(.option) && !g.inside_opt_or_res {
355+
g.writeln('\tif (${tmp_opt_ptr}->state == 0) {')
356+
g.writeln('\t\t*((${result_type_str}*)&${tmp_opt}.data) = *((${result_type_str}*)${tmp_opt_ptr}->data);')
357+
g.writeln('\t} else {')
358+
g.writeln('\t\t${tmp_opt}.state = ${tmp_opt_ptr}->state;')
359+
g.writeln('\t\t${tmp_opt}.err = ${tmp_opt_ptr}->err;')
360+
g.writeln('\t}')
361+
} else {
362+
g.writeln('\t*((${elem_type_str}*)&${tmp_opt}.data) = *((${elem_type_str}*)${tmp_opt_ptr});')
363+
}
338364
g.writeln('} else {')
339365
g.writeln('\t${tmp_opt}.state = 2; ${tmp_opt}.err = builtin___v_error(_S("array index out of range"));')
340366
g.writeln('}')
@@ -344,6 +370,8 @@ fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) {
344370
if !g.is_amp {
345371
if g.inside_opt_or_res && elem_type.has_flag(.option) && g.inside_assign {
346372
g.write('\n${cur_line}(*(${elem_type_str}*)&${tmp_opt})')
373+
} else if elem_type.has_flag(.option) && !g.inside_opt_or_res {
374+
g.write('\n${cur_line}(*(${result_type_str}*)${tmp_opt}.data)')
347375
} else {
348376
g.write('\n${cur_line}(*(${elem_type_str}*)${tmp_opt}.data)')
349377
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
struct Struct {
2+
f1 string
3+
f2 int
4+
f3 bool
5+
f4 f64
6+
}
7+
8+
struct StructWithFieldsOfOptional {
9+
f1 ?string
10+
f2 ?int
11+
f3 ?bool
12+
f4 ?f64
13+
}
14+
15+
struct StructWithFieldsOfOptionalArray {
16+
f1 []?string
17+
f2 []?int
18+
f3 []?bool
19+
f4 []?f64
20+
}
21+
22+
fn test_struct_with_fields_of_optional() {
23+
v1 := StructWithFieldsOfOptional{
24+
f1: ?string('a')
25+
f2: ?int(1)
26+
f3: ?bool(true)
27+
f4: ?f64(1.1)
28+
}
29+
v2 := Struct{
30+
f1: v1.f1 or { '' }
31+
f2: v1.f2 or { 0 }
32+
f3: v1.f3 or { false }
33+
f4: v1.f4 or { 0.0 }
34+
}
35+
assert v2.f1 == 'a'
36+
assert v2.f2 == 1
37+
assert v2.f3 == true
38+
assert v2.f4 == 1.1
39+
}
40+
41+
fn test_struct_with_fields_of_optional_array() {
42+
v1 := StructWithFieldsOfOptionalArray{
43+
f1: [?string('a'), 'b']
44+
f2: [?int(1), 2]
45+
f3: [?bool(true), false]
46+
f4: [?f64(1.1), 2.2]
47+
}
48+
v2 := Struct{
49+
f1: v1.f1[0] or { '' }
50+
f2: v1.f2[0] or { 0 }
51+
f3: v1.f3[0] or { false }
52+
f4: v1.f4[0] or { 0.0 }
53+
}
54+
assert v2.f1 == 'a'
55+
assert v2.f2 == 1
56+
assert v2.f3 == true
57+
assert v2.f4 == 1.1
58+
}

0 commit comments

Comments
 (0)