From 82c9d4ab5bda5e2a2db0f3ad5181dc41a6f47a60 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Wed, 11 Dec 2024 01:07:29 -0300 Subject: [PATCH] checker, cgen: fix codegen for returning different option alias type (fix #23087) (#23125) --- vlib/builtin/option.v | 13 +++++++ vlib/v/checker/return.v | 2 +- vlib/v/gen/c/assign.v | 37 +++++++++++-------- vlib/v/gen/c/cgen.v | 10 ++++- .../tests/options/option_alias_fn_ret_test.v | 22 +++++++++++ 5 files changed, 66 insertions(+), 18 deletions(-) create mode 100644 vlib/v/tests/options/option_alias_fn_ret_test.v diff --git a/vlib/builtin/option.v b/vlib/builtin/option.v index dfd246758..c11b4a4c6 100644 --- a/vlib/builtin/option.v +++ b/vlib/builtin/option.v @@ -41,6 +41,19 @@ fn _option_ok(data voidptr, mut option _option, size int) { } } +@[markused] +fn _option_clone(current &_option, mut option _option, size int) { + unsafe { + *option = _option{ + state: current.state + err: current.err + } + // use err to get the end of OptionBase and then memcpy into it + vmemcpy(&u8(&option.err) + sizeof(IError), &u8(¤t.err) + sizeof(IError), + size) + } +} + // const none__ = IError(&None__{}) diff --git a/vlib/v/checker/return.v b/vlib/v/checker/return.v index e970f72dc..dc80406f2 100644 --- a/vlib/v/checker/return.v +++ b/vlib/v/checker/return.v @@ -197,7 +197,7 @@ fn (mut c Checker) return_stmt(mut node ast.Return) { } got_type := c.unwrap_generic(got_types[i]) if got_type.has_flag(.option) && (!exp_type.has_flag(.option) - || got_type.clear_flag(.option) != exp_type.clear_flag(.option)) { + || !c.check_types(got_type.clear_flag(.option), exp_type.clear_flag(.option))) { pos := node.exprs[expr_idxs[i]].pos() c.error('cannot use `${c.table.type_to_str(got_type)}` as ${c.error_type_name(exp_type)} in return argument', pos) diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index 0232004d2..f05fbc719 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -68,28 +68,35 @@ fn (mut g Gen) expr_opt_with_cast(expr ast.Expr, expr_typ ast.Type, ret_typ ast. defer { g.past_tmp_var_done(past) } - styp := g.base_type(ret_typ) decl_styp := g.styp(ret_typ).replace('*', '_ptr') g.writeln('${decl_styp} ${past.tmp_var};') is_none := expr is ast.CastExpr && expr.expr is ast.None - if is_none { - g.write('_option_none(&(${styp}[]) {') - } else { - g.write('_option_ok(&(${styp}[]) {') - } - if expr is ast.CastExpr && expr_typ.has_flag(.option) { - g.write('*((${g.base_type(expr_typ)}*)') + if expr is ast.CallExpr && expr.return_type.has_flag(.option) { + tmp_var := g.new_tmp_var() + ret_styp := g.styp(expr.return_type).replace('*', '_ptr') + g.write('${ret_styp} ${tmp_var} = ') g.expr(expr) - g.write('.data)') + g.writeln(';') + g.writeln('_option_clone(&${tmp_var}, (${option_name}*)(&${past.tmp_var}), sizeof(${styp}));') } else { - old_inside_opt_or_res := g.inside_opt_or_res - g.inside_opt_or_res = false - g.expr_with_cast(expr, expr_typ, ret_typ) - g.inside_opt_or_res = old_inside_opt_or_res + if is_none { + g.write('_option_none(&(${styp}[]) {') + } else { + g.write('_option_ok(&(${styp}[]) {') + } + if expr is ast.CastExpr && expr_typ.has_flag(.option) { + g.write('*((${g.base_type(expr_typ)}*)') + g.expr(expr) + g.write('.data)') + } else { + old_inside_opt_or_res := g.inside_opt_or_res + g.inside_opt_or_res = false + g.expr_with_cast(expr, expr_typ, ret_typ) + g.inside_opt_or_res = old_inside_opt_or_res + } + g.writeln(' }, (${option_name}*)(&${past.tmp_var}), sizeof(${styp}));') } - g.writeln(' }, (${option_name}*)(&${past.tmp_var}), sizeof(${styp}));') - return past.tmp_var } } diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 301a66538..978ba1dab 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -5631,7 +5631,6 @@ fn (mut g Gen) return_stmt(node ast.Return) { fn_return_is_fixed_array := sym.is_array_fixed() fn_return_is_fixed_array_non_result := fn_return_is_fixed_array && !fn_ret_type.has_option_or_result() - mut has_semicolon := false if node.exprs.len == 0 { g.write_defer_stmts_when_needed() @@ -6020,7 +6019,14 @@ fn (mut g Gen) return_stmt(node ast.Return) { } } else { if g.fn_decl.return_type.has_flag(.option) { - g.expr_with_opt(node.exprs[0], node.types[0], g.fn_decl.return_type) + expr0_is_alias_fn_ret := expr0 is ast.CallExpr && node.types[0].has_flag(.option) + && g.table.type_kind(node.types[0]) in [.placeholder, .alias] + // return foo() where foo() returns different option alias than current fn + if expr0_is_alias_fn_ret { + g.expr_opt_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type) + } else { + g.expr_with_opt(node.exprs[0], node.types[0], g.fn_decl.return_type) + } } else { if fn_return_is_fixed_array && !node.types[0].has_option_or_result() { if node.exprs[0] is ast.Ident { diff --git a/vlib/v/tests/options/option_alias_fn_ret_test.v b/vlib/v/tests/options/option_alias_fn_ret_test.v new file mode 100644 index 000000000..4cf7730fa --- /dev/null +++ b/vlib/v/tests/options/option_alias_fn_ret_test.v @@ -0,0 +1,22 @@ +import time + +fn test() ?i64 { + return bar() +} + +fn test2() ?i64 { + return none +} + +fn bar() ?time.Duration { + return time.Duration(0) +} + +fn baz() ?time.Duration { + return none +} + +fn test_main() { + assert test() != none + assert test2() == none +} -- 2.39.5