From 052d862244e04dec20582eec248e5cb548060cc4 Mon Sep 17 00:00:00 2001 From: CreeperFace <165158232+dy-tea@users.noreply.github.com> Date: Sat, 17 Jan 2026 17:00:40 +0000 Subject: [PATCH] checker: fix option in struct member infix expr and swapped none comparion (fix #26351) (#26373) --- vlib/v/checker/containers.v | 6 +- vlib/v/checker/infix.v | 54 +++- .../tests/infix_compare_option_err.out | 11 +- vlib/v/checker/tests/infix_err.out | 261 ++++++++++-------- vlib/v/checker/tests/infix_err.vv | 18 ++ vlib/v/checker/tests/option_ptr_err.out | 2 +- .../tests/option_wrapped_cmp_op_err.out | 8 +- .../checker/tests/unwrapped_option_infix.out | 4 +- .../tests/wrong_shift_left_option_err.out | 8 +- 9 files changed, 219 insertions(+), 153 deletions(-) diff --git a/vlib/v/checker/containers.v b/vlib/v/checker/containers.v index 681f97391..2ac098543 100644 --- a/vlib/v/checker/containers.v +++ b/vlib/v/checker/containers.v @@ -798,12 +798,12 @@ fn (mut c Checker) check_append(mut node ast.InfixExpr, left_type ast.Type, righ if !node.is_stmt { c.error('array append cannot be used in an expression', node.pos) } + mut right_sym := c.table.sym(right_type) + mut left_sym := c.table.sym(left_type) if left_type.has_flag(.option) && node.left is ast.Ident && node.left.or_expr.kind == .absent { - c.error('unwrapped Option cannot be used in an infix expression', node.pos) + c.check_option_infix_expr(node, left_type, right_type, left_sym, right_sym) } right_pos := node.right.pos() - mut right_sym := c.table.sym(right_type) - mut left_sym := c.table.sym(left_type) // `array << elm` c.check_expr_option_or_result_call(node.right, right_type) node.auto_locked, _ = c.fail_if_immutable(mut node.left) diff --git a/vlib/v/checker/infix.v b/vlib/v/checker/infix.v index f233935d7..480681429 100644 --- a/vlib/v/checker/infix.v +++ b/vlib/v/checker/infix.v @@ -223,6 +223,8 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { } } + c.check_option_infix_expr(node, left_type, right_type, left_sym, right_sym) + // Do not allow comparing nil to non-pointers if node.left.is_nil() { mut final_type := right_type @@ -607,11 +609,8 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { } } } - } else if node.left !in [ast.Ident, ast.SelectorExpr, ast.ComptimeSelector] - && (left_type.has_flag(.option) || right_type.has_flag(.option)) { - opt_comp_pos := if left_type.has_flag(.option) { left_pos } else { right_pos } - c.error('unwrapped Option cannot be compared in an infix expression', - opt_comp_pos) + } else { + c.check_option_infix_expr(node, left_type, right_type, left_sym, right_sym) } if node.left.is_nil() || node.right.is_nil() { 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 { c.error('cannot use operator `${node.op}` with `${right_sym.name}`', node.pos) } // TODO: move this to symmetric_check? Right now it would break `return 0` for `fn()?int ` - left_is_option := left_type.has_flag(.option) - right_is_option := right_type.has_flag(.option) - if left_is_option || right_is_option { - opt_infix_pos := if left_is_option { left_pos } else { right_pos } - if (node.left !in [ast.Ident, ast.IndexExpr, ast.SelectorExpr, ast.ComptimeSelector] - || (node.op in [.eq, .ne] && !right_is_option) - || node.op in [.lt, .gt, .le, .ge]) && right_sym.kind != .none && !c.inside_sql { - c.error('unwrapped Option cannot be used in an infix expression', opt_infix_pos) - } + if node.left !in [ast.Ident, ast.IndexExpr, ast.SelectorExpr, ast.ComptimeSelector] + || node.op in [.eq, .ne] { + c.check_option_infix_expr(node, left_type, right_type, left_sym, right_sym) } left_is_result := left_type.has_flag(.result) @@ -886,7 +879,10 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { node.promoted_type = return_type return return_type } - if node.right is ast.None && left_is_option { + left_is_option := left_type.has_flag(.option) + right_is_option := right_type.has_flag(.option) + if (node.right is ast.None && left_is_option) + || (node.left is ast.None && right_is_option) { return ast.bool_type } 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 { } return true } + +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) { + if c.inside_sql { + return + } + left_is_option := left_type.has_flag(.option) + right_is_option := right_type.has_flag(.option) + if (node.left is ast.None && right_is_option) + || (node.right is ast.None && left_is_option) + || (left_sym.kind == .none || right_sym.kind == .none) { + return + } + if left_is_option || right_is_option { + pos, opt, nopt := if left_is_option { + node.left.pos(), left_sym.name, right_sym.name + } else { + node.right.pos(), right_sym.name, left_sym.name + } + if left_is_option && right_is_option { + if node.op !in [.eq, .ne] { + c.error('`?${opt}` cannot be used as `${nopt}`, unwrap the option first', + pos) + } + return + } + c.error('`?${opt}` cannot be used as `${nopt}`, unwrap the option first', pos) + } +} diff --git a/vlib/v/checker/tests/infix_compare_option_err.out b/vlib/v/checker/tests/infix_compare_option_err.out index 64be23591..e3a740057 100644 --- a/vlib/v/checker/tests/infix_compare_option_err.out +++ b/vlib/v/checker/tests/infix_compare_option_err.out @@ -1,12 +1,5 @@ -vlib/v/checker/tests/infix_compare_option_err.vv:6:5: error: unwrapped Option cannot be compared in an infix expression - 4 | - 5 | fn main() { - 6 | if foo() > foo() { - | ~~~~~ - 7 | } - 8 | } -vlib/v/checker/tests/infix_compare_option_err.vv:6:5: error: unwrapped Option cannot be used in an infix expression - 4 | +vlib/v/checker/tests/infix_compare_option_err.vv:6:5: error: `?int` cannot be used as `int`, unwrap the option first + 4 | 5 | fn main() { 6 | if foo() > foo() { | ~~~~~ diff --git a/vlib/v/checker/tests/infix_err.out b/vlib/v/checker/tests/infix_err.out index 82b4819c1..deb04d266 100644 --- a/vlib/v/checker/tests/infix_err.out +++ b/vlib/v/checker/tests/infix_err.out @@ -1,137 +1,168 @@ -vlib/v/checker/tests/infix_err.vv:9:5: error: mismatched types `string` and `?string` - 7 | } - 8 | - 9 | _ = '' + f() +vlib/v/checker/tests/infix_err.vv:13:5: error: mismatched types `string` and `?string` + 11 | } + 12 | + 13 | _ = '' + f() | ~~~~~~~~ - 10 | _ = f() + '' - 11 | _ = f() + f() -vlib/v/checker/tests/infix_err.vv:9:10: error: unwrapped Option cannot be used in an infix expression - 7 | } - 8 | - 9 | _ = '' + f() + 14 | _ = f() + '' + 15 | _ = f() + f() +vlib/v/checker/tests/infix_err.vv:13:10: error: `?string` cannot be used as `string`, unwrap the option first + 11 | } + 12 | + 13 | _ = '' + f() | ~~~ - 10 | _ = f() + '' - 11 | _ = f() + f() -vlib/v/checker/tests/infix_err.vv:10:5: error: mismatched types `?string` and `string` - 8 | - 9 | _ = '' + f() - 10 | _ = f() + '' + 14 | _ = f() + '' + 15 | _ = f() + f() +vlib/v/checker/tests/infix_err.vv:14:5: error: mismatched types `?string` and `string` + 12 | + 13 | _ = '' + f() + 14 | _ = f() + '' | ~~~~~~~~ - 11 | _ = f() + f() + 15 | _ = f() + f() + 16 | +vlib/v/checker/tests/infix_err.vv:14:5: error: `?string` cannot be used as `string`, unwrap the option first 12 | -vlib/v/checker/tests/infix_err.vv:10:5: error: unwrapped Option cannot be used in an infix expression - 8 | - 9 | _ = '' + f() - 10 | _ = f() + '' + 13 | _ = '' + f() + 14 | _ = f() + '' | ~~~ - 11 | _ = f() + f() - 12 | -vlib/v/checker/tests/infix_err.vv:11:9: error: `+` cannot be used with `?string` - 9 | _ = '' + f() - 10 | _ = f() + '' - 11 | _ = f() + f() + 15 | _ = f() + f() + 16 | +vlib/v/checker/tests/infix_err.vv:15:9: error: `+` cannot be used with `?string` + 13 | _ = '' + f() + 14 | _ = f() + '' + 15 | _ = f() + f() | ^ - 12 | - 13 | _ = 4 + g() -vlib/v/checker/tests/infix_err.vv:11:5: error: unwrapped Option cannot be used in an infix expression - 9 | _ = '' + f() - 10 | _ = f() + '' - 11 | _ = f() + f() + 16 | + 17 | _ = 4 + g() +vlib/v/checker/tests/infix_err.vv:15:5: error: `?string` cannot be used as `string`, unwrap the option first + 13 | _ = '' + f() + 14 | _ = f() + '' + 15 | _ = f() + f() | ~~~ - 12 | - 13 | _ = 4 + g() -vlib/v/checker/tests/infix_err.vv:13:7: error: `+` cannot be used with `?int` - 11 | _ = f() + f() - 12 | - 13 | _ = 4 + g() + 16 | + 17 | _ = 4 + g() +vlib/v/checker/tests/infix_err.vv:17:7: error: `+` cannot be used with `?int` + 15 | _ = f() + f() + 16 | + 17 | _ = 4 + g() | ^ - 14 | _ = int(0) + g() - 15 | _ = g() + int(3) -vlib/v/checker/tests/infix_err.vv:13:9: error: unwrapped Option cannot be used in an infix expression - 11 | _ = f() + f() - 12 | - 13 | _ = 4 + g() + 18 | _ = int(0) + g() + 19 | _ = g() + int(3) +vlib/v/checker/tests/infix_err.vv:17:9: error: `?int` cannot be used as `int literal`, unwrap the option first + 15 | _ = f() + f() + 16 | + 17 | _ = 4 + g() | ~~~ - 14 | _ = int(0) + g() - 15 | _ = g() + int(3) -vlib/v/checker/tests/infix_err.vv:14:14: error: unwrapped Option cannot be used in an infix expression - 12 | - 13 | _ = 4 + g() - 14 | _ = int(0) + g() + 18 | _ = int(0) + g() + 19 | _ = g() + int(3) +vlib/v/checker/tests/infix_err.vv:18:14: error: `?int` cannot be used as `int`, unwrap the option first + 16 | + 17 | _ = 4 + g() + 18 | _ = int(0) + g() | ~~~ - 15 | _ = g() + int(3) - 16 | _ = g() + 3 -vlib/v/checker/tests/infix_err.vv:15:9: error: `+` cannot be used with `?int` - 13 | _ = 4 + g() - 14 | _ = int(0) + g() - 15 | _ = g() + int(3) + 19 | _ = g() + int(3) + 20 | _ = g() + 3 +vlib/v/checker/tests/infix_err.vv:19:9: error: `+` cannot be used with `?int` + 17 | _ = 4 + g() + 18 | _ = int(0) + g() + 19 | _ = g() + int(3) | ^ - 16 | _ = g() + 3 - 17 | -vlib/v/checker/tests/infix_err.vv:15:5: error: unwrapped Option cannot be used in an infix expression - 13 | _ = 4 + g() - 14 | _ = int(0) + g() - 15 | _ = g() + int(3) + 20 | _ = g() + 3 + 21 | +vlib/v/checker/tests/infix_err.vv:19:5: error: `?int` cannot be used as `int`, unwrap the option first + 17 | _ = 4 + g() + 18 | _ = int(0) + g() + 19 | _ = g() + int(3) | ~~~ - 16 | _ = g() + 3 - 17 | -vlib/v/checker/tests/infix_err.vv:16:9: error: `+` cannot be used with `?int` - 14 | _ = int(0) + g() - 15 | _ = g() + int(3) - 16 | _ = g() + 3 + 20 | _ = g() + 3 + 21 | +vlib/v/checker/tests/infix_err.vv:20:9: error: `+` cannot be used with `?int` + 18 | _ = int(0) + g() + 19 | _ = g() + int(3) + 20 | _ = g() + 3 | ^ - 17 | - 18 | // binary operands -vlib/v/checker/tests/infix_err.vv:16:5: error: unwrapped Option cannot be used in an infix expression - 14 | _ = int(0) + g() - 15 | _ = g() + int(3) - 16 | _ = g() + 3 + 21 | + 22 | // binary operands +vlib/v/checker/tests/infix_err.vv:20:5: error: `?int` cannot be used as `int literal`, unwrap the option first + 18 | _ = int(0) + g() + 19 | _ = g() + int(3) + 20 | _ = g() + 3 | ~~~ - 17 | - 18 | // binary operands -vlib/v/checker/tests/infix_err.vv:19:5: error: left operand for `&&` is not a boolean - 17 | - 18 | // binary operands - 19 | _ = 1 && 2 + 21 | + 22 | // binary operands +vlib/v/checker/tests/infix_err.vv:23:5: error: left operand for `&&` is not a boolean + 21 | + 22 | // binary operands + 23 | _ = 1 && 2 | ^ - 20 | _ = true || 2 + 24 | _ = true || 2 + 25 | +vlib/v/checker/tests/infix_err.vv:23:10: error: right operand for `&&` is not a boolean 21 | -vlib/v/checker/tests/infix_err.vv:19:10: error: right operand for `&&` is not a boolean - 17 | - 18 | // binary operands - 19 | _ = 1 && 2 + 22 | // binary operands + 23 | _ = 1 && 2 | ^ - 20 | _ = true || 2 - 21 | -vlib/v/checker/tests/infix_err.vv:20:13: error: right operand for `||` is not a boolean - 18 | // binary operands - 19 | _ = 1 && 2 - 20 | _ = true || 2 + 24 | _ = true || 2 + 25 | +vlib/v/checker/tests/infix_err.vv:24:13: error: right operand for `||` is not a boolean + 22 | // binary operands + 23 | _ = 1 && 2 + 24 | _ = true || 2 | ^ - 21 | - 22 | // boolean expressions -vlib/v/checker/tests/infix_err.vv:20:5: error: infix expr: cannot use `int literal` (right expression) as `bool` - 18 | // binary operands - 19 | _ = 1 && 2 - 20 | _ = true || 2 + 25 | + 26 | // boolean expressions +vlib/v/checker/tests/infix_err.vv:24:5: error: infix expr: cannot use `int literal` (right expression) as `bool` + 22 | // binary operands + 23 | _ = 1 && 2 + 24 | _ = true || 2 | ~~~~~~~~~ - 21 | - 22 | // boolean expressions -vlib/v/checker/tests/infix_err.vv:23:22: error: ambiguous boolean expression. use `()` to ensure correct order of operations - 21 | - 22 | // boolean expressions - 23 | _ = 1 == 1 && 2 == 2 || 3 == 3 + 25 | + 26 | // boolean expressions +vlib/v/checker/tests/infix_err.vv:27:22: error: ambiguous boolean expression. use `()` to ensure correct order of operations + 25 | + 26 | // boolean expressions + 27 | _ = 1 == 1 && 2 == 2 || 3 == 3 | ~~ - 24 | _ = 1 == 1 && 2 == 2 || 3 == 3 && 4 == 4 - 25 | _ = 1 + println('') -vlib/v/checker/tests/infix_err.vv:24:22: error: ambiguous boolean expression. use `()` to ensure correct order of operations - 22 | // boolean expressions - 23 | _ = 1 == 1 && 2 == 2 || 3 == 3 - 24 | _ = 1 == 1 && 2 == 2 || 3 == 3 && 4 == 4 + 28 | _ = 1 == 1 && 2 == 2 || 3 == 3 && 4 == 4 + 29 | _ = 1 + println('') +vlib/v/checker/tests/infix_err.vv:28:22: error: ambiguous boolean expression. use `()` to ensure correct order of operations + 26 | // boolean expressions + 27 | _ = 1 == 1 && 2 == 2 || 3 == 3 + 28 | _ = 1 == 1 && 2 == 2 || 3 == 3 && 4 == 4 | ~~ - 25 | _ = 1 + println('') -vlib/v/checker/tests/infix_err.vv:25:5: error: mismatched types `int literal` and `void` - 23 | _ = 1 == 1 && 2 == 2 || 3 == 3 - 24 | _ = 1 == 1 && 2 == 2 || 3 == 3 && 4 == 4 - 25 | _ = 1 + println('') + 29 | _ = 1 + println('') + 30 | +vlib/v/checker/tests/infix_err.vv:29:5: error: mismatched types `int literal` and `void` + 27 | _ = 1 == 1 && 2 == 2 || 3 == 3 + 28 | _ = 1 == 1 && 2 == 2 || 3 == 3 && 4 == 4 + 29 | _ = 1 + println('') | ~~~~~~~~~~~~~~~ + 30 | + 31 | // struct members +vlib/v/checker/tests/infix_err.vv:36:11: error: `?string` cannot be used as `string`, unwrap the option first + 34 | } + 35 | name := 'a name' + 36 | _ := data.name == 'a name' + | ~~~~ + 37 | _ := 'a name' == data.name + 38 | _ := data.name == name +vlib/v/checker/tests/infix_err.vv:37:23: error: `?string` cannot be used as `string`, unwrap the option first + 35 | name := 'a name' + 36 | _ := data.name == 'a name' + 37 | _ := 'a name' == data.name + | ~~~~ + 38 | _ := data.name == name + 39 | _ := name == data.name +vlib/v/checker/tests/infix_err.vv:38:11: error: `?string` cannot be used as `string`, unwrap the option first + 36 | _ := data.name == 'a name' + 37 | _ := 'a name' == data.name + 38 | _ := data.name == name + | ~~~~ + 39 | _ := name == data.name + 40 | +vlib/v/checker/tests/infix_err.vv:39:19: error: `?string` cannot be used as `string`, unwrap the option first + 37 | _ := 'a name' == data.name + 38 | _ := data.name == name + 39 | _ := name == data.name + | ~~~~ + 40 | + 41 | // these should not error diff --git a/vlib/v/checker/tests/infix_err.vv b/vlib/v/checker/tests/infix_err.vv index 08a5a38db..19531a40f 100644 --- a/vlib/v/checker/tests/infix_err.vv +++ b/vlib/v/checker/tests/infix_err.vv @@ -1,3 +1,7 @@ +struct Data { + name ?string +} + fn f() ?string { return none } @@ -23,3 +27,17 @@ _ = true || 2 _ = 1 == 1 && 2 == 2 || 3 == 3 _ = 1 == 1 && 2 == 2 || 3 == 3 && 4 == 4 _ = 1 + println('') + +// struct members +data := Data { + 'a name' +} +name := 'a name' +_ := data.name == 'a name' +_ := 'a name' == data.name +_ := data.name == name +_ := name == data.name + +// these should not error +_ := data.name == none +_ := none == data.name diff --git a/vlib/v/checker/tests/option_ptr_err.out b/vlib/v/checker/tests/option_ptr_err.out index 2c71fc016..37cb902e3 100644 --- a/vlib/v/checker/tests/option_ptr_err.out +++ b/vlib/v/checker/tests/option_ptr_err.out @@ -4,7 +4,7 @@ vlib/v/checker/tests/option_ptr_err.vv:3:10: error: type `?int` is an Option, it 3 | assert *var == 0 | ~~~ 4 | } -vlib/v/checker/tests/option_ptr_err.vv:3:9: error: unwrapped Option cannot be used in an infix expression +vlib/v/checker/tests/option_ptr_err.vv:3:9: error: `?int` cannot be used as `int literal`, unwrap the option first 1 | fn main() { 2 | mut var := unsafe { ?&int(none) } 3 | assert *var == 0 diff --git a/vlib/v/checker/tests/option_wrapped_cmp_op_err.out b/vlib/v/checker/tests/option_wrapped_cmp_op_err.out index 1842a7b22..77efd9ab1 100644 --- a/vlib/v/checker/tests/option_wrapped_cmp_op_err.out +++ b/vlib/v/checker/tests/option_wrapped_cmp_op_err.out @@ -1,25 +1,25 @@ -vlib/v/checker/tests/option_wrapped_cmp_op_err.vv:3:10: error: unwrapped Option cannot be used in an infix expression +vlib/v/checker/tests/option_wrapped_cmp_op_err.vv:3:10: error: `?string` cannot be used as `string`, unwrap the option first 1 | fn main() { 2 | a := ?string('hi') 3 | println(a == 'hi') | ^ 4 | println('hi' == a) 5 | println(a != 'hi') -vlib/v/checker/tests/option_wrapped_cmp_op_err.vv:4:18: error: unwrapped Option cannot be used in an infix expression +vlib/v/checker/tests/option_wrapped_cmp_op_err.vv:4:18: error: `?string` cannot be used as `string`, unwrap the option first 2 | a := ?string('hi') 3 | println(a == 'hi') 4 | println('hi' == a) | ^ 5 | println(a != 'hi') 6 | println('hi' != a) -vlib/v/checker/tests/option_wrapped_cmp_op_err.vv:5:10: error: unwrapped Option cannot be used in an infix expression +vlib/v/checker/tests/option_wrapped_cmp_op_err.vv:5:10: error: `?string` cannot be used as `string`, unwrap the option first 3 | println(a == 'hi') 4 | println('hi' == a) 5 | println(a != 'hi') | ^ 6 | println('hi' != a) 7 | } -vlib/v/checker/tests/option_wrapped_cmp_op_err.vv:6:18: error: unwrapped Option cannot be used in an infix expression +vlib/v/checker/tests/option_wrapped_cmp_op_err.vv:6:18: error: `?string` cannot be used as `string`, unwrap the option first 4 | println('hi' == a) 5 | println(a != 'hi') 6 | println('hi' != a) diff --git a/vlib/v/checker/tests/unwrapped_option_infix.out b/vlib/v/checker/tests/unwrapped_option_infix.out index e3841c2c2..7c6a9cdd5 100644 --- a/vlib/v/checker/tests/unwrapped_option_infix.out +++ b/vlib/v/checker/tests/unwrapped_option_infix.out @@ -1,5 +1,5 @@ -vlib/v/checker/tests/unwrapped_option_infix.vv:5:9: error: unwrapped Option cannot be used in an infix expression +vlib/v/checker/tests/unwrapped_option_infix.vv:5:9: error: `?string` cannot be used as `string`, unwrap the option first 3 | } - 4 | + 4 | 5 | println(test() == '') | ~~~~~~ diff --git a/vlib/v/checker/tests/wrong_shift_left_option_err.out b/vlib/v/checker/tests/wrong_shift_left_option_err.out index abd9f813d..94f7aa0c2 100644 --- a/vlib/v/checker/tests/wrong_shift_left_option_err.out +++ b/vlib/v/checker/tests/wrong_shift_left_option_err.out @@ -5,11 +5,11 @@ vlib/v/checker/tests/wrong_shift_left_option_err.vv:7:2: error: cannot push to O | ^ 8 | mut b := ?[]int([2, 8]) // primitive type 9 | b << [1, 3, 4] -vlib/v/checker/tests/wrong_shift_left_option_err.vv:7:4: error: unwrapped Option cannot be used in an infix expression +vlib/v/checker/tests/wrong_shift_left_option_err.vv:7:2: error: `?[]SomeStruct` cannot be used as `[]SomeStruct`, unwrap the option first 5 | fn main() { 6 | mut a := ?[]SomeStruct([SomeStruct{}, SomeStruct{}]) // struct type 7 | a << []SomeStruct{len: 20} - | ~~ + | ^ 8 | mut b := ?[]int([2, 8]) // primitive type 9 | b << [1, 3, 4] vlib/v/checker/tests/wrong_shift_left_option_err.vv:9:2: error: cannot push to Option array that was not unwrapped first @@ -19,10 +19,10 @@ vlib/v/checker/tests/wrong_shift_left_option_err.vv:9:2: error: cannot push to O | ^ 10 | dump(a?.len) 11 | dump(b) -vlib/v/checker/tests/wrong_shift_left_option_err.vv:9:4: error: unwrapped Option cannot be used in an infix expression +vlib/v/checker/tests/wrong_shift_left_option_err.vv:9:2: error: `?[]int` cannot be used as `[]int`, unwrap the option first 7 | a << []SomeStruct{len: 20} 8 | mut b := ?[]int([2, 8]) // primitive type 9 | b << [1, 3, 4] - | ~~ + | ^ 10 | dump(a?.len) 11 | dump(b) -- 2.39.5