From 241109516f77edbbf86e497591f81dbe90f3b60a Mon Sep 17 00:00:00 2001 From: yuyi Date: Mon, 9 Jan 2023 02:04:17 +0800 Subject: [PATCH] checker, cgen: fix aliased optional or result fn call (#16908) --- vlib/v/checker/checker.v | 21 ++++++++--- vlib/v/checker/return.v | 6 ++++ .../tests/aliased_optional_fn_call_err.out | 6 ++++ .../tests/aliased_optional_fn_call_err.vv | 9 +++++ vlib/v/gen/c/cgen.v | 35 +++++++++++-------- vlib/v/gen/c/fn.v | 14 +++++++- vlib/v/tests/aliased_optional_fn_call_test.v | 11 ++++++ 7 files changed, 83 insertions(+), 19 deletions(-) create mode 100644 vlib/v/checker/tests/aliased_optional_fn_call_err.out create mode 100644 vlib/v/checker/tests/aliased_optional_fn_call_err.vv create mode 100644 vlib/v/tests/aliased_optional_fn_call_test.v diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index aeac5345f6..1b7261305d 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -923,13 +923,20 @@ fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos to // return the actual type of the expression, once the optional is handled fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type ast.Type) ast.Type { if expr is ast.CallExpr { - if expr.return_type.has_flag(.optional) || expr.return_type.has_flag(.result) { - return_modifier_kind := if expr.return_type.has_flag(.optional) { + mut expr_ret_type := expr.return_type + if expr_ret_type != 0 && c.table.sym(expr_ret_type).kind == .alias { + unaliased_ret_type := c.table.unaliased_type(expr_ret_type) + if unaliased_ret_type.has_flag(.optional) || unaliased_ret_type.has_flag(.result) { + expr_ret_type = unaliased_ret_type + } + } + if expr_ret_type.has_flag(.optional) || expr_ret_type.has_flag(.result) { + return_modifier_kind := if expr_ret_type.has_flag(.optional) { 'an option' } else { 'a result' } - return_modifier := if expr.return_type.has_flag(.optional) { '?' } else { '!' } + return_modifier := if expr_ret_type.has_flag(.optional) { '?' } else { '!' } if expr.or_block.kind == .absent { if c.inside_defer { c.error('${expr.name}() returns ${return_modifier_kind}, so it should have an `or {}` block at the end', @@ -939,7 +946,7 @@ fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type ast.Type) ast.Typ expr.pos) } } else { - c.check_or_expr(expr.or_block, ret_type, expr.return_type) + c.check_or_expr(expr.or_block, ret_type, expr_ret_type) } return ret_type.clear_flag(.optional).clear_flag(.result) } else if expr.or_block.kind == .block { @@ -2329,6 +2336,12 @@ pub fn (mut c Checker) expr(node_ ast.Expr) ast.Type { } ast.CallExpr { mut ret_type := c.call_expr(mut node) + if ret_type != 0 && c.table.sym(ret_type).kind == .alias { + unaliased_type := c.table.unaliased_type(ret_type) + if unaliased_type.has_flag(.optional) || unaliased_type.has_flag(.result) { + ret_type = unaliased_type + } + } if !ret_type.has_flag(.optional) && !ret_type.has_flag(.result) { if node.or_block.kind == .block { c.error('unexpected `or` block, the function `${node.name}` does neither return an optional nor a result', diff --git a/vlib/v/checker/return.v b/vlib/v/checker/return.v index a1fb1c7fd7..c096dca4af 100644 --- a/vlib/v/checker/return.v +++ b/vlib/v/checker/return.v @@ -12,6 +12,12 @@ fn (mut c Checker) return_stmt(mut node ast.Return) { } c.expected_type = c.table.cur_fn.return_type mut expected_type := c.unwrap_generic(c.expected_type) + if expected_type != 0 && c.table.sym(expected_type).kind == .alias { + unaliased_type := c.table.unaliased_type(expected_type) + if unaliased_type.has_flag(.optional) || unaliased_type.has_flag(.result) { + expected_type = unaliased_type + } + } expected_type_sym := c.table.sym(expected_type) if node.exprs.len > 0 && c.table.cur_fn.return_type == ast.void_type { c.error('unexpected argument, current function does not return anything', node.exprs[0].pos()) diff --git a/vlib/v/checker/tests/aliased_optional_fn_call_err.out b/vlib/v/checker/tests/aliased_optional_fn_call_err.out new file mode 100644 index 0000000000..9da634a23e --- /dev/null +++ b/vlib/v/checker/tests/aliased_optional_fn_call_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/aliased_optional_fn_call_err.vv:8:10: error: foo() returns an option, so it should have either an `or {}` block, or `?` at the end + 6 | + 7 | fn main(){ + 8 | println(foo()) + | ~~~~~ + 9 | } diff --git a/vlib/v/checker/tests/aliased_optional_fn_call_err.vv b/vlib/v/checker/tests/aliased_optional_fn_call_err.vv new file mode 100644 index 0000000000..7631dbbc99 --- /dev/null +++ b/vlib/v/checker/tests/aliased_optional_fn_call_err.vv @@ -0,0 +1,9 @@ +type OptStr = ?string + +fn foo() OptStr{ + return 'abc' +} + +fn main(){ + println(foo()) +} diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 328f707461..4ea3d1ad91 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -4485,14 +4485,21 @@ fn (mut g Gen) return_stmt(node ast.Return) { // got to do a correct check for multireturn sym := g.table.sym(g.fn_decl.return_type) + mut fn_ret_type := g.fn_decl.return_type + if sym.kind == .alias { + unaliased_type := g.table.unaliased_type(fn_ret_type) + if unaliased_type.has_flag(.optional) || unaliased_type.has_flag(.result) { + fn_ret_type = unaliased_type + } + } fn_return_is_multi := sym.kind == .multi_return - fn_return_is_optional := g.fn_decl.return_type.has_flag(.optional) - fn_return_is_result := g.fn_decl.return_type.has_flag(.result) + fn_return_is_optional := fn_ret_type.has_flag(.optional) + fn_return_is_result := fn_ret_type.has_flag(.result) mut has_semicolon := false if node.exprs.len == 0 { g.write_defer_stmts_when_needed() if fn_return_is_optional || fn_return_is_result { - styp := g.typ(g.fn_decl.return_type) + styp := g.typ(fn_ret_type) g.writeln('return (${styp}){0};') } else { if g.is_autofree { @@ -4504,7 +4511,7 @@ fn (mut g Gen) return_stmt(node ast.Return) { return } tmpvar := g.new_tmp_var() - ret_typ := g.typ(g.unwrap_generic(g.fn_decl.return_type)) + ret_typ := g.typ(g.unwrap_generic(fn_ret_type)) mut use_tmp_var := g.defer_stmts.len > 0 || g.defer_profile_code.len > 0 || g.cur_lock.lockeds.len > 0 // handle promoting none/error/function returning _option' @@ -4516,7 +4523,7 @@ fn (mut g Gen) return_stmt(node ast.Return) { if g.fn_decl != unsafe { nil } && g.fn_decl.is_test { test_error_var := g.new_tmp_var() g.write('${ret_typ} ${test_error_var} = ') - g.gen_optional_error(g.fn_decl.return_type, node.exprs[0]) + g.gen_optional_error(fn_ret_type, node.exprs[0]) g.writeln(';') g.write_defer_stmts_when_needed() g.gen_failing_return_error_for_test_fn(node, test_error_var) @@ -4527,7 +4534,7 @@ fn (mut g Gen) return_stmt(node ast.Return) { } else { g.write('return ') } - g.gen_optional_error(g.fn_decl.return_type, node.exprs[0]) + g.gen_optional_error(fn_ret_type, node.exprs[0]) g.writeln(';') if use_tmp_var { g.write_defer_stmts_when_needed() @@ -4544,7 +4551,7 @@ fn (mut g Gen) return_stmt(node ast.Return) { if g.fn_decl != unsafe { nil } && g.fn_decl.is_test { test_error_var := g.new_tmp_var() g.write('${ret_typ} ${test_error_var} = ') - g.gen_result_error(g.fn_decl.return_type, node.exprs[0]) + g.gen_result_error(fn_ret_type, node.exprs[0]) g.writeln(';') g.write_defer_stmts_when_needed() g.gen_failing_return_error_for_test_fn(node, test_error_var) @@ -4555,7 +4562,7 @@ fn (mut g Gen) return_stmt(node ast.Return) { } else { g.write('return ') } - g.gen_result_error(g.fn_decl.return_type, node.exprs[0]) + g.gen_result_error(fn_ret_type, node.exprs[0]) g.writeln(';') if use_tmp_var { g.write_defer_stmts_when_needed() @@ -4686,16 +4693,16 @@ fn (mut g Gen) return_stmt(node ast.Return) { } } if fn_return_is_optional && !expr_type_is_opt && return_sym.name != c.option_name { - styp := g.base_type(g.fn_decl.return_type) + styp := g.base_type(fn_ret_type) g.writeln('${ret_typ} ${tmpvar};') g.write('_option_ok(&(${styp}[]) { ') - if !g.fn_decl.return_type.is_ptr() && node.types[0].is_ptr() { + if !fn_ret_type.is_ptr() && node.types[0].is_ptr() { if !(node.exprs[0] is ast.Ident && !g.is_amp) { g.write('*') } } for i, expr in node.exprs { - g.expr_with_cast(expr, node.types[i], g.fn_decl.return_type.clear_flag(.optional).clear_flag(.result)) + g.expr_with_cast(expr, node.types[i], fn_ret_type.clear_flag(.optional).clear_flag(.result)) if i < node.exprs.len - 1 { g.write(', ') } @@ -4715,16 +4722,16 @@ fn (mut g Gen) return_stmt(node ast.Return) { } } if fn_return_is_result && !expr_type_is_result && return_sym.name != c.result_name { - styp := g.base_type(g.fn_decl.return_type) + styp := g.base_type(fn_ret_type) g.writeln('${ret_typ} ${tmpvar};') g.write('_result_ok(&(${styp}[]) { ') - if !g.fn_decl.return_type.is_ptr() && node.types[0].is_ptr() { + if !fn_ret_type.is_ptr() && node.types[0].is_ptr() { if !(node.exprs[0] is ast.Ident && !g.is_amp) { g.write('*') } } for i, expr in node.exprs { - g.expr_with_cast(expr, node.types[i], g.fn_decl.return_type.clear_flag(.result)) + g.expr_with_cast(expr, node.types[i], fn_ret_type.clear_flag(.result)) if i < node.exprs.len - 1 { g.write(', ') } diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 5cd23578a8..976fba0915 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -684,6 +684,12 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { tmp_opt := if gen_or || gen_keep_alive { g.new_tmp_var() } else { '' } if gen_or || gen_keep_alive { mut ret_typ := node.return_type + if g.table.sym(ret_typ).kind == .alias { + unaliased_type := g.table.unaliased_type(ret_typ) + if unaliased_type.has_flag(.optional) || unaliased_type.has_flag(.result) { + ret_typ = unaliased_type + } + } styp := g.typ(ret_typ) if gen_or && !is_gen_or_and_assign_rhs { cur_line = g.go_before_stmt(0) @@ -713,7 +719,13 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { } if gen_or { g.or_block(tmp_opt, node.or_block, node.return_type) - unwrapped_typ := node.return_type.clear_flag(.optional).clear_flag(.result) + mut unwrapped_typ := node.return_type.clear_flag(.optional).clear_flag(.result) + if g.table.sym(unwrapped_typ).kind == .alias { + unaliased_type := g.table.unaliased_type(unwrapped_typ) + if unaliased_type.has_flag(.optional) || unaliased_type.has_flag(.result) { + unwrapped_typ = unaliased_type.clear_flag(.optional).clear_flag(.result) + } + } unwrapped_styp := g.typ(unwrapped_typ) if g.infix_left_var_name.len > 0 { g.indent-- diff --git a/vlib/v/tests/aliased_optional_fn_call_test.v b/vlib/v/tests/aliased_optional_fn_call_test.v new file mode 100644 index 0000000000..8bc9d4c7bc --- /dev/null +++ b/vlib/v/tests/aliased_optional_fn_call_test.v @@ -0,0 +1,11 @@ +type OptStr = ?string + +fn foo() OptStr { + return 'abc' +} + +fn test_aliased_optional_fn_call() { + ret := foo()? + println(ret) + assert ret == 'abc' +}