diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 52eb09b515..9f21253664 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -940,8 +940,12 @@ pub fn (mut c Checker) check_or_expr(node ast.OrExpr, ret_type ast.Type, expr_re node.pos) } if !expr_return_type.has_flag(.optional) { - c.error('to propagate an option, the call must also return an optional type', - node.pos) + if expr_return_type.has_flag(.result) { + c.warn('propagating a result like an option is deprecated, use `foo()!` instead of `foo()?`', node.pos) + } else { + c.error('to propagate an option, the call must also return an optional type', + node.pos) + } } return } diff --git a/vlib/v/checker/tests/propagate_option_with_result_err.out b/vlib/v/checker/tests/propagate_option_with_result_err.out index 9464691924..71ef918307 100644 --- a/vlib/v/checker/tests/propagate_option_with_result_err.out +++ b/vlib/v/checker/tests/propagate_option_with_result_err.out @@ -1,4 +1,4 @@ -vlib/v/checker/tests/propagate_option_with_result_err.vv:6:7: error: to propagate an option, the call must also return an optional type +vlib/v/checker/tests/propagate_option_with_result_err.vv:6:7: warning: propagating a result like an option is deprecated, use `foo()!` instead of `foo()?` 4 | 5 | fn bar() ?string { 6 | foo()? diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index ab9e2604d3..c5f129d2a1 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -5212,6 +5212,36 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type ast.Ty } } g.or_expr_return_type = ast.void_type + } else if or_block.kind == .propagate_result + || (or_block.kind == .propagate_option && return_type.has_flag(.result)) { + if g.file.mod.name == 'main' && (isnil(g.fn_decl) || g.fn_decl.is_main) { + // In main(), an `opt()!` call is sugar for `opt() or { panic(err) }` + err_msg := 'IError_name_table[${cvar_name}.err._typ]._method_msg(${cvar_name}.err._object)' + if g.pref.is_debug { + paline, pafile, pamod, pafn := g.panic_debug_info(or_block.pos) + g.writeln('panic_debug($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), $err_msg);') + } else { + g.writeln('\tpanic_result_not_set($err_msg);') + } + } else if !isnil(g.fn_decl) && g.fn_decl.is_test { + g.gen_failing_error_propagation_for_test_fn(or_block, cvar_name) + } else { + // In ordinary functions, `opt()!` call is sugar for: + // `opt() or { return err }` + // Since we *do* return, first we have to ensure that + // the defered statements are generated. + g.write_defer_stmts() + // Now that option types are distinct we need a cast here + if g.fn_decl.return_type == ast.void_type { + g.writeln('\treturn;') + } else { + styp := g.typ(g.fn_decl.return_type) + err_obj := g.new_tmp_var() + g.writeln('\t$styp $err_obj;') + g.writeln('\tmemcpy(&$err_obj, &$cvar_name, sizeof($c.result_name));') + g.writeln('\treturn $err_obj;') + } + } } else if or_block.kind == .propagate_option { if g.file.mod.name == 'main' && (isnil(g.fn_decl) || g.fn_decl.is_main) { // In main(), an `opt()?` call is sugar for `opt() or { panic(err) }` @@ -5241,35 +5271,6 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type ast.Ty g.writeln('\treturn $err_obj;') } } - } else if or_block.kind == .propagate_result { - if g.file.mod.name == 'main' && (isnil(g.fn_decl) || g.fn_decl.is_main) { - // In main(), an `opt()!` call is sugar for `opt() or { panic(err) }` - err_msg := 'IError_name_table[${cvar_name}.err._typ]._method_msg(${cvar_name}.err._object)' - if g.pref.is_debug { - paline, pafile, pamod, pafn := g.panic_debug_info(or_block.pos) - g.writeln('panic_debug($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), $err_msg);') - } else { - g.writeln('\tpanic_result_not_set($err_msg);') - } - } else if !isnil(g.fn_decl) && g.fn_decl.is_test { - g.gen_failing_error_propagation_for_test_fn(or_block, cvar_name) - } else { - // In ordinary functions, `opt()!` call is sugar for: - // `opt() or { return err }` - // Since we *do* return, first we have to ensure that - // the defered statements are generated. - g.write_defer_stmts() - // Now that option types are distinct we need a cast here - if g.fn_decl.return_type == ast.void_type { - g.writeln('\treturn;') - } else { - styp := g.typ(g.fn_decl.return_type) - err_obj := g.new_tmp_var() - g.writeln('\t$styp $err_obj;') - g.writeln('\tmemcpy(&$err_obj, &$cvar_name, sizeof($c.result_name));') - g.writeln('\treturn $err_obj;') - } - } } g.writeln('}') g.set_current_pos_as_last_stmt_pos()