Skip to content

Commit 052d862

Browse files
authored
checker: fix option in struct member infix expr and swapped none comparion (fix #26351) (#26373)
1 parent 2e2c4b4 commit 052d862

File tree

9 files changed

+219
-153
lines changed

9 files changed

+219
-153
lines changed

‎vlib/v/checker/containers.v‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -798,12 +798,12 @@ fn (mut c Checker) check_append(mut node ast.InfixExpr, left_type ast.Type, righ
798798
if !node.is_stmt {
799799
c.error('array append cannot be used in an expression', node.pos)
800800
}
801+
mut right_sym := c.table.sym(right_type)
802+
mut left_sym := c.table.sym(left_type)
801803
if left_type.has_flag(.option) && node.left is ast.Ident && node.left.or_expr.kind == .absent {
802-
c.error('unwrapped Option cannot be used in an infix expression', node.pos)
804+
c.check_option_infix_expr(node, left_type, right_type, left_sym, right_sym)
803805
}
804806
right_pos := node.right.pos()
805-
mut right_sym := c.table.sym(right_type)
806-
mut left_sym := c.table.sym(left_type)
807807
// `array << elm`
808808
c.check_expr_option_or_result_call(node.right, right_type)
809809
node.auto_locked, _ = c.fail_if_immutable(mut node.left)

‎vlib/v/checker/infix.v‎

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,8 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
223223
}
224224
}
225225

226+
c.check_option_infix_expr(node, left_type, right_type, left_sym, right_sym)
227+
226228
// Do not allow comparing nil to non-pointers
227229
if node.left.is_nil() {
228230
mut final_type := right_type
@@ -607,11 +609,8 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
607609
}
608610
}
609611
}
610-
} else if node.left !in [ast.Ident, ast.SelectorExpr, ast.ComptimeSelector]
611-
&& (left_type.has_flag(.option) || right_type.has_flag(.option)) {
612-
opt_comp_pos := if left_type.has_flag(.option) { left_pos } else { right_pos }
613-
c.error('unwrapped Option cannot be compared in an infix expression',
614-
opt_comp_pos)
612+
} else {
613+
c.check_option_infix_expr(node, left_type, right_type, left_sym, right_sym)
615614
}
616615
if node.left.is_nil() || node.right.is_nil() {
617616
c.error('cannot use `${node.op.str()}` with `nil`', node.pos)
@@ -829,15 +828,9 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
829828
c.error('cannot use operator `${node.op}` with `${right_sym.name}`', node.pos)
830829
}
831830
// TODO: move this to symmetric_check? Right now it would break `return 0` for `fn()?int `
832-
left_is_option := left_type.has_flag(.option)
833-
right_is_option := right_type.has_flag(.option)
834-
if left_is_option || right_is_option {
835-
opt_infix_pos := if left_is_option { left_pos } else { right_pos }
836-
if (node.left !in [ast.Ident, ast.IndexExpr, ast.SelectorExpr, ast.ComptimeSelector]
837-
|| (node.op in [.eq, .ne] && !right_is_option)
838-
|| node.op in [.lt, .gt, .le, .ge]) && right_sym.kind != .none && !c.inside_sql {
839-
c.error('unwrapped Option cannot be used in an infix expression', opt_infix_pos)
840-
}
831+
if node.left !in [ast.Ident, ast.IndexExpr, ast.SelectorExpr, ast.ComptimeSelector]
832+
|| node.op in [.eq, .ne] {
833+
c.check_option_infix_expr(node, left_type, right_type, left_sym, right_sym)
841834
}
842835

843836
left_is_result := left_type.has_flag(.result)
@@ -886,7 +879,10 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
886879
node.promoted_type = return_type
887880
return return_type
888881
}
889-
if node.right is ast.None && left_is_option {
882+
left_is_option := left_type.has_flag(.option)
883+
right_is_option := right_type.has_flag(.option)
884+
if (node.right is ast.None && left_is_option)
885+
|| (node.left is ast.None && right_is_option) {
890886
return ast.bool_type
891887
}
892888
error_left_sym := if node.left.is_auto_deref_var() {
@@ -1091,3 +1087,31 @@ fn (mut c Checker) check_sort_external_variable_access(node ast.Expr) bool {
10911087
}
10921088
return true
10931089
}
1090+
1091+
fn (mut c Checker) check_option_infix_expr(node ast.InfixExpr, left_type ast.Type, right_type ast.Type, left_sym ast.TypeSymbol, right_sym ast.TypeSymbol) {
1092+
if c.inside_sql {
1093+
return
1094+
}
1095+
left_is_option := left_type.has_flag(.option)
1096+
right_is_option := right_type.has_flag(.option)
1097+
if (node.left is ast.None && right_is_option)
1098+
|| (node.right is ast.None && left_is_option)
1099+
|| (left_sym.kind == .none || right_sym.kind == .none) {
1100+
return
1101+
}
1102+
if left_is_option || right_is_option {
1103+
pos, opt, nopt := if left_is_option {
1104+
node.left.pos(), left_sym.name, right_sym.name
1105+
} else {
1106+
node.right.pos(), right_sym.name, left_sym.name
1107+
}
1108+
if left_is_option && right_is_option {
1109+
if node.op !in [.eq, .ne] {
1110+
c.error('`?${opt}` cannot be used as `${nopt}`, unwrap the option first',
1111+
pos)
1112+
}
1113+
return
1114+
}
1115+
c.error('`?${opt}` cannot be used as `${nopt}`, unwrap the option first', pos)
1116+
}
1117+
}

‎vlib/v/checker/tests/infix_compare_option_err.out‎

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
1-
vlib/v/checker/tests/infix_compare_option_err.vv:6:5: error: unwrapped Option cannot be compared in an infix expression
2-
4 |
3-
5 | fn main() {
4-
6 | if foo() > foo() {
5-
| ~~~~~
6-
7 | }
7-
8 | }
8-
vlib/v/checker/tests/infix_compare_option_err.vv:6:5: error: unwrapped Option cannot be used in an infix expression
9-
4 |
1+
vlib/v/checker/tests/infix_compare_option_err.vv:6:5: error: `?int` cannot be used as `int`, unwrap the option first
2+
4 |
103
5 | fn main() {
114
6 | if foo() > foo() {
125
| ~~~~~

0 commit comments

Comments
 (0)