From 6b29d628c3d10bd3252b3635e7c3a7462413840e Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Mon, 10 Jul 2023 15:24:13 -0300 Subject: [PATCH] cgen: fix generated code for returning generic result/option to comptime var (#18834) --- vlib/v/gen/c/assign.v | 9 ++++ vlib/v/gen/c/cgen.v | 11 ++--- vlib/v/gen/c/fn.v | 5 ++- vlib/v/tests/option_generic_return_test.v | 55 +++++++++++++++++++++++ 4 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 vlib/v/tests/option_generic_return_test.v diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index 995f96039d..92ca3236f8 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -127,9 +127,11 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { is_decl := node.op == .decl_assign g.assign_op = node.op g.inside_assign = true + g.assign_ct_type = 0 defer { g.assign_op = .unknown g.inside_assign = false + g.assign_ct_type = 0 } op := if is_decl { token.Kind.assign } else { node.op } right_expr := node.right[0] @@ -241,6 +243,7 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { var_type = val_type.clear_flag(.option) } left.obj.typ = var_type + g.assign_ct_type = var_type } } else if val is ast.ComptimeSelector { key_str := g.get_comptime_selector_key_type(val) @@ -252,11 +255,13 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { } else { val_type = g.comptime_var_type_map[key_str] or { var_type } } + g.assign_ct_type = var_type } } else if val is ast.ComptimeCall { key_str := '${val.method_name}.return_type' var_type = g.comptime_var_type_map[key_str] or { var_type } left.obj.typ = var_type + g.assign_ct_type = var_type } else if is_decl && val is ast.Ident && val.info is ast.IdentVar { val_info := (val as ast.Ident).info gen_or = val.or_expr.kind != .absent @@ -271,6 +276,7 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { val_type = var_type left.obj.typ = var_type } + g.assign_ct_type = var_type } else if val is ast.IndexExpr { if val.left is ast.Ident && g.is_generic_param_var(val.left) { ctyp := g.unwrap_generic(g.get_gn_var_type(val.left as ast.Ident)) @@ -278,6 +284,7 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { var_type = ctyp val_type = var_type left.obj.typ = var_type + g.assign_ct_type = var_type } } } @@ -294,11 +301,13 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { val_type = g.comptime_var_type_map[key_str_right] or { var_type } } } + g.assign_ct_type = var_type } else if mut left is ast.IndexExpr && val is ast.ComptimeSelector { key_str := g.get_comptime_selector_key_type(val) if key_str != '' { val_type = g.comptime_var_type_map[key_str] or { var_type } } + g.assign_ct_type = val_type } mut styp := g.typ(var_type) mut is_fixed_array_init := false diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 5a458227f5..a0aca7adc6 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -150,11 +150,12 @@ mut: loop_depth int ternary_names map[string]string ternary_level_names map[string][]string - arraymap_set_pos int // map or array set value position - stmt_path_pos []int // positions of each statement start, for inserting C statements before the current statement - skip_stmt_pos bool // for handling if expressions + autofree (since both prepend C statements) - left_is_opt bool // left hand side on assignment is an option - right_is_opt bool // right hand side on assignment is an option + arraymap_set_pos int // map or array set value position + stmt_path_pos []int // positions of each statement start, for inserting C statements before the current statement + skip_stmt_pos bool // for handling if expressions + autofree (since both prepend C statements) + left_is_opt bool // left hand side on assignment is an option + right_is_opt bool // right hand side on assignment is an option + assign_ct_type ast.Type // left hand side resolved comptime type indent int empty_line bool assign_op token.Kind // *=, =, etc (for array_set) diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 861487370d..846566e112 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -757,7 +757,7 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { ret_typ = unaliased_type } } - styp := g.typ(ret_typ) + mut styp := g.typ(ret_typ) if gen_or && !is_gen_or_and_assign_rhs { cur_line = g.go_before_stmt(0) } @@ -767,6 +767,9 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { g.indent++ g.write('${tmp_opt} = ') } else if !g.inside_curry_call { + if g.assign_ct_type != 0 && node.or_block.kind in [.propagate_option, .propagate_result] { + styp = g.typ(g.assign_ct_type.derive(ret_typ)) + } g.write('${styp} ${tmp_opt} = ') if node.left is ast.AnonFn { g.expr(node.left) diff --git a/vlib/v/tests/option_generic_return_test.v b/vlib/v/tests/option_generic_return_test.v new file mode 100644 index 0000000000..6b7a76c41f --- /dev/null +++ b/vlib/v/tests/option_generic_return_test.v @@ -0,0 +1,55 @@ +fn opt_parse[T]() ?T { + mut cfg := T{} + opt_set_val(mut cfg)? + return cfg +} + +fn opt_set_val[T](mut cfg T) ? { + $for field in T.fields { + $if field.is_array { + mut arr := cfg.$(field.name) + cfg.$(field.name) = opt_add_val(mut arr)? + } + } +} + +fn opt_add_val[T](mut arr []T) ?[]T { + return arr +} + +fn res_parse[T]() !T { + mut cfg := T{} + res_set_val(mut cfg)! + return cfg +} + +fn res_set_val[T](mut cfg T) ! { + $for field in T.fields { + $if field.is_array { + mut arr := cfg.$(field.name) + cfg.$(field.name) = res_add_val(mut arr)! + } + } +} + +fn res_add_val[T](mut arr []T) ![]T { + return arr +} + +struct Strings { + val []string +} + +struct Ints { + val []int +} + +fn test_option() { + assert dump(opt_parse[Strings]()?) == Strings{} + assert dump(opt_parse[Ints]()?) == Ints{} +} + +fn test_result() { + assert dump(res_parse[Strings]()!) == Strings{} + assert dump(res_parse[Ints]()!) == Ints{} +}