From f9c8d3d25cba5f7df6729d8c4e6ffaad5df55f9d Mon Sep 17 00:00:00 2001 From: yuyi Date: Mon, 1 Mar 2021 20:56:07 +0800 Subject: [PATCH] cgen: fix return if true { 0 } else { none } (fix #9005) (#9030) --- vlib/v/checker/checker.v | 9 +- vlib/v/gen/c/cgen.v | 150 +++++++++++++++--------- vlib/v/tests/if_expr_of_optional_test.v | 25 ++++ 3 files changed, 125 insertions(+), 59 deletions(-) create mode 100644 vlib/v/tests/if_expr_of_optional_test.v diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 490c239962..4f7694865e 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -4913,9 +4913,14 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { if branch.stmts.len > 0 && branch.stmts[branch.stmts.len - 1] is ast.ExprStmt { mut last_expr := branch.stmts[branch.stmts.len - 1] as ast.ExprStmt c.expected_type = former_expected_type + if c.expected_type.has_flag(.optional) { + if node.typ == table.void_type { + node.is_expr = true + node.typ = c.expected_type + } + continue + } last_expr.typ = c.expr(last_expr.expr) - // if last_expr.typ != node.typ { - // if !c.check_types(node.typ, last_expr.typ) { if !c.check_types(last_expr.typ, node.typ) { if node.typ == table.void_type { // first branch of if expression diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index f0b121635b..fdd1ce2406 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -33,57 +33,57 @@ struct Gen { pref &pref.Preferences module_built string mut: - table &table.Table - out strings.Builder - cheaders strings.Builder - includes strings.Builder // all C #includes required by V modules - typedefs strings.Builder - typedefs2 strings.Builder - type_definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file) - definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file) - inits map[string]strings.Builder // contents of `void _vinit/2{}` - cleanups map[string]strings.Builder // contents of `void _vcleanup(){}` - gowrappers strings.Builder // all go callsite wrappers - stringliterals strings.Builder // all string literals (they depend on tos3() beeing defined - auto_str_funcs strings.Builder // function bodies of all auto generated _str funcs - comptime_defines strings.Builder // custom defines, given by -d/-define flags on the CLI - pcs_declarations strings.Builder // -prof profile counter declarations for each function - hotcode_definitions strings.Builder // -live declarations & functions - embedded_data strings.Builder // data to embed in the executable/binary - shared_types strings.Builder // shared/lock types - shared_functions strings.Builder // shared constructors - channel_definitions strings.Builder // channel related code - options_typedefs strings.Builder // Option typedefs - options strings.Builder // `Option_xxxx` types - json_forward_decls strings.Builder // json type forward decls - enum_typedefs strings.Builder // enum types - sql_buf strings.Builder // for writing exprs to args via `sqlite3_bind_int()` etc - file ast.File - fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0 - last_fn_c_name string - tmp_count int // counter for unique tmp vars (_tmp1, tmp2 etc) - tmp_count2 int // a separate tmp var counter for autofree fn calls - is_c_call bool // e.g. `C.printf("v")` - is_assign_lhs bool // inside left part of assign expr (for array_set(), etc) - discard_or_result bool // do not safe last ExprStmt of `or` block in tmp variable to defer ongoing expr usage - is_void_expr_stmt bool // ExprStmt whos result is discarded - is_array_set bool - is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc - is_sql bool // Inside `sql db{}` statement, generating sql instead of C (e.g. `and` instead of `&&` etc) - is_shared bool // for initialization of hidden mutex in `[rw]shared` literals - is_vlines_enabled bool // is it safe to generate #line directives when -g is passed - array_set_pos int - vlines_path string // set to the proper path for generating #line directives - optionals []string // to avoid duplicates TODO perf, use map - chan_pop_optionals []string // types for `x := <-ch or {...}` - chan_push_optionals []string // types for `ch <- x or {...}` - shareds []int // types with hidden mutex for which decl has been emitted - inside_ternary int // ?: comma separated statements on a single line - inside_map_postfix bool // inside map++/-- postfix expr - inside_map_infix bool // inside map< 0 && i < stmts.len - 1 { g.write(',') @@ -1076,7 +1105,8 @@ fn (mut g Gen) stmt(node ast.Stmt) { // if af { // g.autofree_call_postgen() // } - if g.inside_ternary == 0 && !node.is_expr && node.expr !is ast.IfExpr { + if g.inside_ternary == 0 && !g.inside_if_optional && !node.is_expr + && node.expr !is ast.IfExpr { g.writeln(';') } } @@ -4039,7 +4069,7 @@ fn (mut g Gen) concat_expr(node ast.ConcatExpr) { fn (mut g Gen) need_tmp_var_in_if(node ast.IfExpr) bool { if node.is_expr && g.inside_ternary == 0 { - if g.is_autofree { + if g.is_autofree || node.typ.has_flag(.optional) { return true } for branch in node.branches { @@ -4076,6 +4106,9 @@ fn (mut g Gen) if_expr(node ast.IfExpr) { tmp := if needs_tmp_var { g.new_tmp_var() } else { '' } mut cur_line := '' if needs_tmp_var { + if node.typ.has_flag(.optional) { + g.inside_if_optional = true + } g.write('/*experimental if expr*/') styp := g.typ(node.typ) // g.insert_before_stmt('$styp $tmp;') @@ -4181,6 +4214,9 @@ fn (mut g Gen) if_expr(node ast.IfExpr) { // g.writeln('$cur_line $tmp; /*Z*/') g.write('$cur_line $tmp /*Z*/') } + if node.typ.has_flag(.optional) { + g.inside_if_optional = false + } } fn (mut g Gen) index_expr(node ast.IndexExpr) { diff --git a/vlib/v/tests/if_expr_of_optional_test.v b/vlib/v/tests/if_expr_of_optional_test.v new file mode 100644 index 0000000000..7670c2cbd5 --- /dev/null +++ b/vlib/v/tests/if_expr_of_optional_test.v @@ -0,0 +1,25 @@ +fn foo1() ?int { + return if true { 0 } else { none } +} + +fn foo2() ?int { + return if true { 1 } else { error('foo2 error') } +} + +fn foo3() ?int { + return if true { 2 } else { 0 } +} + +fn test_if_expr_of_optional() { + a1 := foo1() or { panic('error') } + println(a1) + assert a1 == 0 + + a2 := foo2() or { panic('error') } + println(a2) + assert a2 == 1 + + a3 := foo3() or { panic('error') } + println(a3) + assert a3 == 2 +}