1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

cgen: fix option unwrap on assignment (#17551)

This commit is contained in:
Felipe Pena 2023-03-08 16:52:24 -03:00 committed by GitHub
parent ae6a48c0e3
commit e253256c30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 39 additions and 9 deletions

View File

@ -1119,8 +1119,9 @@ fn (mut c Checker) check_or_last_stmt(stmt ast.Stmt, ret_type ast.Type, expr_ret
c.expected_or_type = ret_type.clear_flag(.option).clear_flag(.result)
last_stmt_typ := c.expr(stmt.expr)
if ret_type.has_flag(.option) && last_stmt_typ.has_flag(.option) {
if stmt.expr in [ast.Ident, ast.SelectorExpr, ast.CallExpr] {
if ret_type.has_flag(.option)
&& (last_stmt_typ.has_flag(.option) || last_stmt_typ == ast.none_type) {
if stmt.expr in [ast.Ident, ast.SelectorExpr, ast.CallExpr, ast.None] {
expected_type_name := c.table.type_to_str(ret_type.clear_flag(.option).clear_flag(.result))
got_type_name := c.table.type_to_str(last_stmt_typ)
c.error('`or` block must provide a value of type `${expected_type_name}`, not `${got_type_name}`',

View File

@ -0,0 +1,7 @@
vlib/v/checker/tests/wrong_option_unwrap_err.vv:3:14: error: `or` block must provide a value of type `string`, not `none`
1 | fn main() {
2 | a := ?string('c')
3 | b := a or { none }
| ~~~~
4 | println(b)
5 | }

View File

@ -0,0 +1,5 @@
fn main() {
a := ?string('c')
b := a or { none }
println(b)
}

View File

@ -10,8 +10,8 @@ import v.token
fn (mut g Gen) expr_with_opt_or_block(expr ast.Expr, expr_typ ast.Type, var_expr ast.Expr, ret_typ ast.Type) {
gen_or := expr is ast.Ident && (expr as ast.Ident).or_expr.kind != .absent
if gen_or {
old_inside_opt_data := g.inside_opt_data
g.inside_opt_data = true
old_inside_opt_or_res := g.inside_opt_or_res
g.inside_opt_or_res = true
g.expr_with_cast(expr, expr_typ, ret_typ)
g.writeln(';')
g.writeln('if (${expr}.state != 0) {')
@ -40,7 +40,7 @@ fn (mut g Gen) expr_with_opt_or_block(expr ast.Expr, expr_typ ast.Type, var_expr
}
}
g.writeln('}')
g.inside_opt_data = old_inside_opt_data
g.inside_opt_or_res = old_inside_opt_or_res
} else {
g.expr_with_opt(expr, expr_typ, ret_typ)
}

View File

@ -4093,7 +4093,7 @@ fn (mut g Gen) ident(node ast.Ident) {
if node.obj is ast.Var {
if !g.is_assign_lhs && node.obj.is_comptime_field {
if g.comptime_for_field_type.has_flag(.option) {
if (g.inside_opt_or_res && node.or_expr.kind == .absent) || g.left_is_opt {
if (g.inside_opt_or_res || g.left_is_opt) && node.or_expr.kind == .absent {
g.write('${name}')
} else {
g.write('/*opt*/')
@ -4103,7 +4103,8 @@ fn (mut g Gen) ident(node ast.Ident) {
} else {
g.write('${name}')
}
if node.or_expr.kind != .absent {
if node.or_expr.kind != .absent && !(g.inside_opt_or_res && g.inside_assign
&& !g.is_assign_lhs) {
stmt_str := g.go_before_stmt(0).trim_space()
g.empty_line = true
g.or_block(name, node.or_expr, g.comptime_for_field_type)
@ -4117,14 +4118,15 @@ fn (mut g Gen) ident(node ast.Ident) {
// `x = new_opt()` => `x = new_opt()` (g.right_is_opt == true)
// `println(x)` => `println(*(int*)x.data)`
if node.info.is_option && !(g.is_assign_lhs && g.right_is_opt) {
if (g.inside_opt_or_res && node.or_expr.kind == .absent) || g.left_is_opt {
if (g.inside_opt_or_res || g.left_is_opt) && node.or_expr.kind == .absent {
g.write('${name}')
} else {
g.write('/*opt*/')
styp := g.base_type(node.info.typ)
g.write('(*(${styp}*)${name}.data)')
}
if node.or_expr.kind != .absent && !(g.inside_assign && !g.is_assign_lhs) {
if node.or_expr.kind != .absent && !(g.inside_opt_or_res && g.inside_assign
&& !g.is_assign_lhs) {
stmt_str := g.go_before_stmt(0).trim_space()
g.empty_line = true
g.or_block(name, node.or_expr, node.info.typ)

View File

@ -0,0 +1,15 @@
struct Foo {
mut:
x string
y ?string
}
fn test_main() {
a := ?string(none)
mut foo := Foo{}
foo.x = a or { 'test' }
foo.y = a or { 'test' }
assert foo.x == 'test'
assert foo.y? == 'test'
}