From 56739fe97126504c58950c8d6fd243c050adc40a Mon Sep 17 00:00:00 2001 From: CreeperFace <165158232+dy-tea@users.noreply.github.com> Date: Thu, 18 Dec 2025 07:40:05 +0000 Subject: [PATCH] checker,cgen: allow init of struct from struct with array of optional values (fix #26000) (#26005) --- vlib/v/checker/checker.v | 3 +- vlib/v/checker/struct.v | 11 +++- vlib/v/gen/c/index.v | 30 +++++++++- .../options/option_struct_init_default_test.v | 58 +++++++++++++++++++ 4 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 vlib/v/tests/options/option_struct_init_default_test.v diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 7c4b93d12..042bcaa66 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1467,6 +1467,7 @@ fn (mut c Checker) check_expr_option_or_result_call(expr ast.Expr, ret_type ast. expr) c.cur_or_expr = last_cur_or_expr } + return ret_type.clear_flag(.result) } else if expr.left is ast.SelectorExpr && expr.left_type.has_option_or_result() { with_modifier_kind := if expr.left_type.has_flag(.option) { 'an Option' @@ -5342,7 +5343,7 @@ fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type { c.expected_or_type = typ } c.stmts_ending_with_expression(mut node.or_expr.stmts, c.expected_or_type) - c.check_expr_option_or_result_call(node, typ) + typ = c.check_expr_option_or_result_call(node, typ) node.typ = typ return typ } diff --git a/vlib/v/checker/struct.v b/vlib/v/checker/struct.v index 62e1df03a..626640d45 100644 --- a/vlib/v/checker/struct.v +++ b/vlib/v/checker/struct.v @@ -718,10 +718,17 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini exp_type_is_option := exp_type.has_flag(.option) if !exp_type_is_option { got_type = c.check_expr_option_or_result_call(init_field.expr, got_type) - if got_type.has_flag(.option) { + init_field.typ = got_type + has_or_block := match mut init_field.expr { + ast.IndexExpr { init_field.expr.or_expr.kind != .absent } + ast.CallExpr { init_field.expr.or_block.kind != .absent } + ast.SelectorExpr { init_field.expr.or_block.kind != .absent } + else { false } + } + if got_type.has_flag(.option) && !has_or_block { c.error('cannot assign an Option value to a non-option struct field', init_field.pos) - } else if got_type.has_flag(.result) { + } else if got_type.has_flag(.result) && !has_or_block { c.error('cannot assign a Result value to a non-option struct field', init_field.pos) } diff --git a/vlib/v/gen/c/index.v b/vlib/v/gen/c/index.v index c68c93568..f26db2669 100644 --- a/vlib/v/gen/c/index.v +++ b/vlib/v/gen/c/index.v @@ -171,11 +171,28 @@ fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) { info := sym.info as ast.Array elem_type := info.elem_type elem_sym := g.table.final_sym(elem_type) + result_type := match true { + gen_or && elem_type.has_flag(.option) { + node.typ.clear_flag(.option) + } + gen_or { + node.typ + } + else { + elem_type + } + } + result_sym := g.table.final_sym(result_type) elem_type_str := if elem_sym.kind == .function { 'voidptr' } else { g.styp(info.elem_type) } + result_type_str := if result_sym.kind == .function { + 'voidptr' + } else { + g.styp(result_type) + } left_is_shared := node.left_type.has_flag(.shared_f) // `vals[i].field = x` is an exception and requires `array_get`: // `(*(Val*)array_get(vals, i)).field = x;` @@ -334,7 +351,16 @@ fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) { opt_elem_type := g.styp(elem_type.set_flag(.option)) g.writeln('${opt_elem_type} ${tmp_opt} = {0};') g.writeln('if (${tmp_opt_ptr}) {') - g.writeln('\t*((${elem_type_str}*)&${tmp_opt}.data) = *((${elem_type_str}*)${tmp_opt_ptr});') + if elem_type.has_flag(.option) && !g.inside_opt_or_res { + g.writeln('\tif (${tmp_opt_ptr}->state == 0) {') + g.writeln('\t\t*((${result_type_str}*)&${tmp_opt}.data) = *((${result_type_str}*)${tmp_opt_ptr}->data);') + g.writeln('\t} else {') + g.writeln('\t\t${tmp_opt}.state = ${tmp_opt_ptr}->state;') + g.writeln('\t\t${tmp_opt}.err = ${tmp_opt_ptr}->err;') + g.writeln('\t}') + } else { + g.writeln('\t*((${elem_type_str}*)&${tmp_opt}.data) = *((${elem_type_str}*)${tmp_opt_ptr});') + } g.writeln('} else {') g.writeln('\t${tmp_opt}.state = 2; ${tmp_opt}.err = builtin___v_error(_S("array index out of range"));') g.writeln('}') @@ -344,6 +370,8 @@ fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) { if !g.is_amp { if g.inside_opt_or_res && elem_type.has_flag(.option) && g.inside_assign { g.write('\n${cur_line}(*(${elem_type_str}*)&${tmp_opt})') + } else if elem_type.has_flag(.option) && !g.inside_opt_or_res { + g.write('\n${cur_line}(*(${result_type_str}*)${tmp_opt}.data)') } else { g.write('\n${cur_line}(*(${elem_type_str}*)${tmp_opt}.data)') } diff --git a/vlib/v/tests/options/option_struct_init_default_test.v b/vlib/v/tests/options/option_struct_init_default_test.v new file mode 100644 index 000000000..7a64bc306 --- /dev/null +++ b/vlib/v/tests/options/option_struct_init_default_test.v @@ -0,0 +1,58 @@ +struct Struct { + f1 string + f2 int + f3 bool + f4 f64 +} + +struct StructWithFieldsOfOptional { + f1 ?string + f2 ?int + f3 ?bool + f4 ?f64 +} + +struct StructWithFieldsOfOptionalArray { + f1 []?string + f2 []?int + f3 []?bool + f4 []?f64 +} + +fn test_struct_with_fields_of_optional() { + v1 := StructWithFieldsOfOptional{ + f1: ?string('a') + f2: ?int(1) + f3: ?bool(true) + f4: ?f64(1.1) + } + v2 := Struct{ + f1: v1.f1 or { '' } + f2: v1.f2 or { 0 } + f3: v1.f3 or { false } + f4: v1.f4 or { 0.0 } + } + assert v2.f1 == 'a' + assert v2.f2 == 1 + assert v2.f3 == true + assert v2.f4 == 1.1 +} + +fn test_struct_with_fields_of_optional_array() { + v1 := StructWithFieldsOfOptionalArray{ + f1: [?string('a'), 'b'] + f2: [?int(1), 2] + f3: [?bool(true), false] + f4: [?f64(1.1), 2.2] + } + v2 := Struct{ + f1: v1.f1[0] or { '' } + f2: v1.f2[0] or { 0 } + f3: v1.f3[0] or { false } + f4: v1.f4[0] or { 0.0 } + } + assert v2.f1 == 'a' + assert v2.f2 == 1 + assert v2.f3 == true + assert v2.f4 == 1.1 +} -- 2.39.5