Skip to content

Commit e253256

Browse files
authored
cgen: fix option unwrap on assignment (#17551)
1 parent ae6a48c commit e253256

File tree

6 files changed

+39
-9
lines changed

6 files changed

+39
-9
lines changed

vlib/v/checker/checker.v

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,8 +1119,9 @@ fn (mut c Checker) check_or_last_stmt(stmt ast.Stmt, ret_type ast.Type, expr_ret
11191119
c.expected_or_type = ret_type.clear_flag(.option).clear_flag(.result)
11201120
last_stmt_typ := c.expr(stmt.expr)
11211121

1122-
if ret_type.has_flag(.option) && last_stmt_typ.has_flag(.option) {
1123-
if stmt.expr in [ast.Ident, ast.SelectorExpr, ast.CallExpr] {
1122+
if ret_type.has_flag(.option)
1123+
&& (last_stmt_typ.has_flag(.option) || last_stmt_typ == ast.none_type) {
1124+
if stmt.expr in [ast.Ident, ast.SelectorExpr, ast.CallExpr, ast.None] {
11241125
expected_type_name := c.table.type_to_str(ret_type.clear_flag(.option).clear_flag(.result))
11251126
got_type_name := c.table.type_to_str(last_stmt_typ)
11261127
c.error('`or` block must provide a value of type `${expected_type_name}`, not `${got_type_name}`',
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
vlib/v/checker/tests/wrong_option_unwrap_err.vv:3:14: error: `or` block must provide a value of type `string`, not `none`
2+
1 | fn main() {
3+
2 | a := ?string('c')
4+
3 | b := a or { none }
5+
| ~~~~
6+
4 | println(b)
7+
5 | }
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
fn main() {
2+
a := ?string('c')
3+
b := a or { none }
4+
println(b)
5+
}

vlib/v/gen/c/assign.v

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import v.token
1010
fn (mut g Gen) expr_with_opt_or_block(expr ast.Expr, expr_typ ast.Type, var_expr ast.Expr, ret_typ ast.Type) {
1111
gen_or := expr is ast.Ident && (expr as ast.Ident).or_expr.kind != .absent
1212
if gen_or {
13-
old_inside_opt_data := g.inside_opt_data
14-
g.inside_opt_data = true
13+
old_inside_opt_or_res := g.inside_opt_or_res
14+
g.inside_opt_or_res = true
1515
g.expr_with_cast(expr, expr_typ, ret_typ)
1616
g.writeln(';')
1717
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
4040
}
4141
}
4242
g.writeln('}')
43-
g.inside_opt_data = old_inside_opt_data
43+
g.inside_opt_or_res = old_inside_opt_or_res
4444
} else {
4545
g.expr_with_opt(expr, expr_typ, ret_typ)
4646
}

vlib/v/gen/c/cgen.v

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4093,7 +4093,7 @@ fn (mut g Gen) ident(node ast.Ident) {
40934093
if node.obj is ast.Var {
40944094
if !g.is_assign_lhs && node.obj.is_comptime_field {
40954095
if g.comptime_for_field_type.has_flag(.option) {
4096-
if (g.inside_opt_or_res && node.or_expr.kind == .absent) || g.left_is_opt {
4096+
if (g.inside_opt_or_res || g.left_is_opt) && node.or_expr.kind == .absent {
40974097
g.write('${name}')
40984098
} else {
40994099
g.write('/*opt*/')
@@ -4103,7 +4103,8 @@ fn (mut g Gen) ident(node ast.Ident) {
41034103
} else {
41044104
g.write('${name}')
41054105
}
4106-
if node.or_expr.kind != .absent {
4106+
if node.or_expr.kind != .absent && !(g.inside_opt_or_res && g.inside_assign
4107+
&& !g.is_assign_lhs) {
41074108
stmt_str := g.go_before_stmt(0).trim_space()
41084109
g.empty_line = true
41094110
g.or_block(name, node.or_expr, g.comptime_for_field_type)
@@ -4117,14 +4118,15 @@ fn (mut g Gen) ident(node ast.Ident) {
41174118
// `x = new_opt()` => `x = new_opt()` (g.right_is_opt == true)
41184119
// `println(x)` => `println(*(int*)x.data)`
41194120
if node.info.is_option && !(g.is_assign_lhs && g.right_is_opt) {
4120-
if (g.inside_opt_or_res && node.or_expr.kind == .absent) || g.left_is_opt {
4121+
if (g.inside_opt_or_res || g.left_is_opt) && node.or_expr.kind == .absent {
41214122
g.write('${name}')
41224123
} else {
41234124
g.write('/*opt*/')
41244125
styp := g.base_type(node.info.typ)
41254126
g.write('(*(${styp}*)${name}.data)')
41264127
}
4127-
if node.or_expr.kind != .absent && !(g.inside_assign && !g.is_assign_lhs) {
4128+
if node.or_expr.kind != .absent && !(g.inside_opt_or_res && g.inside_assign
4129+
&& !g.is_assign_lhs) {
41284130
stmt_str := g.go_before_stmt(0).trim_space()
41294131
g.empty_line = true
41304132
g.or_block(name, node.or_expr, node.info.typ)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
struct Foo {
2+
mut:
3+
x string
4+
y ?string
5+
}
6+
7+
fn test_main() {
8+
a := ?string(none)
9+
mut foo := Foo{}
10+
foo.x = a or { 'test' }
11+
foo.y = a or { 'test' }
12+
13+
assert foo.x == 'test'
14+
assert foo.y? == 'test'
15+
}

0 commit comments

Comments
 (0)