cgen: fix various issues concerning optionals (#9021)

pull/9046/head
Uwe Krüger 2021-02-28 23:21:03 +01:00 committed by GitHub
parent d7252f4474
commit e937d6249c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 101 additions and 43 deletions

View File

@ -105,6 +105,7 @@ const (
'vlib/v/tests/interface_edge_cases/assign_to_interface_field_test.v',
'vlib/v/tests/interface_fields_test.v',
'vlib/v/tests/interface_variadic_test.v',
'vlib/v/tests/option_2_test.v',
'vlib/v/tests/orm_sub_struct_test.v',
'vlib/v/tests/ref_struct_test.v',
'vlib/v/tests/semaphore_test.v',

View File

@ -1280,7 +1280,9 @@ pub fn (expr Expr) is_lvalue() bool {
pub fn (expr Expr) is_expr() bool {
match expr {
IfExpr { return expr.is_expr }
LockExpr { return expr.is_expr }
MatchExpr { return expr.is_expr }
SelectExpr { return expr.is_expr }
else {}
}
return true

View File

@ -3670,7 +3670,11 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
return c.cast_expr(mut node)
}
ast.CallExpr {
return c.call_expr(mut node)
mut ret_type := c.call_expr(mut node)
if ret_type.has_flag(.optional) && node.or_block.kind != .absent {
ret_type = ret_type.clear_flag(.optional)
}
return ret_type
}
ast.GoExpr {
mut ret_type := c.call_expr(mut node.go_stmt.call_expr)

View File

@ -1,7 +0,0 @@
vlib/v/checker/tests/return_optional_err.vv:4:13: error: cannot use `?string` as type `string` in return argument
2 |
3 | fn my_func() string {
4 | return os.read_file('/some/file/that/exists.txt') or { panic(err.msg) }
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 | }
6 |

View File

@ -1,10 +0,0 @@
import os
fn my_func() string {
return os.read_file('/some/file/that/exists.txt') or { panic(err.msg) }
}
fn main() {
aa := my_func()
println(aa)
}

View File

@ -65,7 +65,8 @@ mut:
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)
is_assign_rhs bool // inside right part of assign after `=` (val expr)
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)
@ -1068,7 +1069,10 @@ fn (mut g Gen) stmt(node ast.Stmt) {
// if af {
// g.autofree_call_pregen(node.expr as ast.CallExpr)
// }
old_is_void_expr_stmt := g.is_void_expr_stmt
g.is_void_expr_stmt = !node.is_expr
g.expr(node.expr)
g.is_void_expr_stmt = old_is_void_expr_stmt
// if af {
// g.autofree_call_postgen()
// }
@ -1758,9 +1762,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
mr_var_name := 'mr_$assign_stmt.pos.pos'
mr_styp := g.typ(return_type)
g.write('$mr_styp $mr_var_name = ')
g.is_assign_rhs = true
g.expr(assign_stmt.right[0])
g.is_assign_rhs = false
g.writeln(';')
for i, lx in assign_stmt.left {
if lx is ast.Ident {
@ -1947,7 +1949,10 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
}
if blank_assign {
if is_call {
old_is_void_expr_stmt := g.is_void_expr_stmt
g.is_void_expr_stmt = true
g.expr(val)
g.is_void_expr_stmt = old_is_void_expr_stmt
} else {
g.write('{$styp _ = ')
g.expr(val)
@ -2000,7 +2005,6 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
g.write(' = /*f*/string_add(')
}
g.is_assign_lhs = false
g.is_assign_rhs = true
str_add = true
}
// Assignment Operator Overloading
@ -2053,7 +2057,6 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
g.expr(left)
}
g.is_assign_lhs = false
g.is_assign_rhs = true
if is_fixed_array_copy {
if is_decl {
g.writeln(';')
@ -2080,7 +2083,6 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
g.write('*($styp*)')
g.write(tmp_opt + '.data/*FFz*/')
g.right_is_opt = false
g.is_assign_rhs = false
if g.inside_ternary == 0 && !assign_stmt.is_simple {
g.writeln(';')
}
@ -2144,7 +2146,6 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
g.is_shared = false
}
g.right_is_opt = false
g.is_assign_rhs = false
if g.inside_ternary == 0 && (assign_stmt.left.len > 1 || !assign_stmt.is_simple) {
g.writeln(';')
}
@ -2468,6 +2469,14 @@ fn (mut g Gen) map_fn_ptrs(key_typ table.TypeSymbol) (string, string, string, st
fn (mut g Gen) expr(node ast.Expr) {
// println('cgen expr() line_nr=$node.pos.line_nr')
old_discard_or_result := g.discard_or_result
old_is_void_expr_stmt := g.is_void_expr_stmt
if g.is_void_expr_stmt {
g.discard_or_result = true
g.is_void_expr_stmt = false
} else {
g.discard_or_result = false
}
// NB: please keep the type names in the match here in alphabetical order:
match mut node {
ast.AnonFn {
@ -2733,7 +2742,7 @@ fn (mut g Gen) expr(node ast.Expr) {
right_sym := g.table.get_type_symbol(node.right_type)
mut right_inf := right_sym.info as table.Chan
elem_type := right_inf.elem_type
is_gen_or_and_assign_rhs := gen_or && g.is_assign_rhs
is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result
cur_line := if is_gen_or_and_assign_rhs {
line := g.go_before_stmt(0)
g.out.write_string(c.tabs[g.indent])
@ -2831,6 +2840,8 @@ fn (mut g Gen) expr(node ast.Expr) {
g.expr(node.expr)
}
}
g.discard_or_result = old_discard_or_result
g.is_void_expr_stmt = old_is_void_expr_stmt
}
// T.name, typeof(expr).name
@ -4320,7 +4331,7 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) {
}
needs_clone := info.elem_type == table.string_type_idx && g.is_autofree
&& !g.is_assign_lhs
is_gen_or_and_assign_rhs := gen_or && g.is_assign_rhs
is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result
cur_line := if is_gen_or_and_assign_rhs {
line := g.go_before_stmt(0)
g.out.write_string(c.tabs[g.indent])
@ -4493,7 +4504,7 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) {
g.write('}, &($elem_type_str[]){ $zero }))')
} else {
zero := g.type_default(info.value_type)
is_gen_or_and_assign_rhs := gen_or && g.is_assign_rhs
is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result
cur_line := if is_gen_or_and_assign_rhs {
line := g.go_before_stmt(0)
g.out.write_string(c.tabs[g.indent])
@ -4724,11 +4735,15 @@ fn (mut g Gen) return_statement(node ast.Return) {
} else if node.exprs.len >= 1 {
// normal return
return_sym := g.table.get_type_symbol(node.types[0])
// `return opt_ok(expr)` for functions that expect an optional
mut expr_type_is_opt := node.types[0].has_flag(.optional)
expr0 := node.exprs[0]
if expr0 is ast.CallExpr {
expr_type_is_opt = expr0.return_type.has_flag(.optional)
// `return opt_ok(expr)` for functions that expect an optional
expr_type_is_opt := match expr0 {
ast.CallExpr {
expr0.return_type.has_flag(.optional) && expr0.or_block.kind == .absent
}
else {
node.types[0].has_flag(.optional)
}
}
if fn_return_is_optional && !expr_type_is_opt && return_sym.name !in ['Option', 'Option2'] {
styp := g.base_type(g.fn_decl.return_type)

View File

@ -410,7 +410,7 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
g.inside_call = false
}
gen_or := node.or_block.kind != .absent // && !g.is_autofree
is_gen_or_and_assign_rhs := gen_or && g.is_assign_rhs
is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result
cur_line := if is_gen_or_and_assign_rhs { // && !g.is_autofree {
// `x := foo() or { ...}`
// cut everything that has been generated to prepend optional variable creation
@ -421,14 +421,8 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
} else {
''
}
// if gen_or && g.pref.autofree && g.inside_return {
if gen_or && g.inside_return {
// TODO optional return af hack (tmp_count gets increased in .return_statement())
g.tmp_count--
}
tmp_opt := if gen_or { g.new_tmp_var() } else { '' }
if gen_or && !g.inside_return {
// if is_gen_or_and_assign_rhs {
if gen_or {
styp := g.typ(node.return_type.set_flag(.optional))
g.write('$styp $tmp_opt = ')
}
@ -455,10 +449,10 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
} else if g.table.get_type_symbol(node.return_type).kind == .multi_return {
g.write('\n $cur_line $tmp_opt /*U*/')
} else {
g.write('\n $cur_line *($unwrapped_styp*)${tmp_opt}.data')
if !g.inside_const {
g.write('\n $cur_line *($unwrapped_styp*)${tmp_opt}.data')
}
}
// g.write('\n /*call_expr cur_line:*/ $cur_line /*C*/ $tmp_opt /*end*/')
// g.insert_before_stmt('\n /* VVV */ $tmp_opt')
}
}
}

View File

@ -0,0 +1,59 @@
fn f(n int) ?int {
if n < 0 {
return error('negative arg')
}
return n
}
fn test_lhs_option() {
mut a := [0, 1, 2, 3, 4]
a[f(-1) or { 2 }] = 7
assert a == [0, 1, 7, 3, 4]
}
fn ret_no_opt(n int) int {
return f(n) or { panic(err.msg) }
}
fn test_opt_return_no_opt() {
aa := ret_no_opt(3)
assert aa == 3
}
fn test_range_for_and_array_push() {
mut a := []int{}
for n in f(-3) or { -1 } .. f(3) or { 12 } {
a << f(n) or { -5 }
}
assert a == [-5, 0, 1, 2]
}
fn test_channel_push() {
ch := chan f64{cap: 2}
ch <- 12.25
ch.close()
mut res := []f64{cap: 3}
for _ in 0 .. 3 {
res << <-ch or { -6.75 }
}
assert res == [12.25, -6.75, -6.75]
}
fn test_thread_wait() {
thrs := [
go f(3)
go f(-7)
go f(12)
]
mut res := []int{cap: 3}
for t in thrs {
res << t.wait() or { -13 }
}
assert res == [3, -13, 12]
}
fn test_nested_opt() {
a := f(f(f(-3) or { -7 }) or { 4 }) or { 17 }
assert a == 4
}