diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 22b396f371..acdc2a75c6 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3208,8 +3208,13 @@ pub fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type ast.Type) ast if expr is ast.CallExpr { if expr.return_type.has_flag(.optional) { if expr.or_block.kind == .absent { - c.error('${expr.name}() returns an option, so it should have either an `or {}` block, or `?` at the end', - expr.pos) + if c.inside_defer { + c.error('${expr.name}() returns an option, so it should have an `or {}` block at the end', + expr.pos) + } else { + c.error('${expr.name}() returns an option, so it should have either an `or {}` block, or `?` at the end', + expr.pos) + } } else { c.check_or_expr(expr.or_block, ret_type, expr.return_type.clear_flag(.optional)) } diff --git a/vlib/v/checker/tests/defer_optional.out b/vlib/v/checker/tests/defer_optional.out new file mode 100644 index 0000000000..184e35e641 --- /dev/null +++ b/vlib/v/checker/tests/defer_optional.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/defer_optional.vv:5:3: error: opt() returns an option, so it should have an `or {}` block at the end + 3 | fn thing() ?string { + 4 | defer { + 5 | opt() + | ~~~~~ + 6 | } + 7 | return 'ok' \ No newline at end of file diff --git a/vlib/v/checker/tests/defer_optional.vv b/vlib/v/checker/tests/defer_optional.vv new file mode 100644 index 0000000000..f153430535 --- /dev/null +++ b/vlib/v/checker/tests/defer_optional.vv @@ -0,0 +1,8 @@ +fn opt() ? {} + +fn thing() ?string { + defer { + opt() + } + return 'ok' +} \ No newline at end of file diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 1096dc8068..6f2de5a6de 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -4902,7 +4902,9 @@ fn (mut g Gen) return_stmt(node ast.Return) { // `tmp := foo(a, b, c); free(a); free(b); free(c); return tmp;` // Save return value in a temp var so that all args (a,b,c) can be freed // Don't use a tmp var if a variable is simply returned: `return x` - if node.exprs[0] !is ast.Ident { + // Just in case of defer statements exists, that the return values cannot + // be modified. + if node.exprs[0] !is ast.Ident || use_tmp_var { g.write('$ret_typ $tmpvar = ') } else { use_tmp_var = false diff --git a/vlib/v/tests/defer_test.v b/vlib/v/tests/defer_test.v index 0a2be26913..7bebf11e45 100644 --- a/vlib/v/tests/defer_test.v +++ b/vlib/v/tests/defer_test.v @@ -142,3 +142,15 @@ fn test_defer_str_interpol() { t << '${t[0]}' } } + +fn test_defer_not_change_return_values() { + assert num() == 10 +} + +fn num() int { + mut ret := 10 + defer { + ret = 20 + } + return ret +}