cgen: fix various issues concerning optionals (#9021)
parent
d7252f4474
commit
e937d6249c
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 |
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue