diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 46ead38a1a..200bf41233 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -414,7 +414,10 @@ pub fn (mut c Checker) struct_decl(mut decl ast.StructDecl) { } if field.has_default_expr { c.expected_type = field.typ - field_expr_type := c.expr(field.default_expr) + mut field_expr_type := c.expr(field.default_expr) + if !field.typ.has_flag(.optional) { + c.check_expr_opt_call(field.default_expr, field_expr_type) + } struct_sym.info.fields[i].default_expr_typ = field_expr_type c.check_expected(field_expr_type, field.typ) or { if !(sym.kind == .interface_ @@ -611,7 +614,10 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type { inited_fields << field_name field_type_sym := c.table.get_type_symbol(info_field.typ) c.expected_type = info_field.typ - expr_type := c.expr(field.expr) + mut expr_type := c.expr(field.expr) + if !info_field.typ.has_flag(.optional) { + expr_type = c.check_expr_opt_call(field.expr, expr_type) + } expr_type_sym := c.table.get_type_symbol(expr_type) if field_type_sym.kind == .interface_ { c.type_implements(expr_type, info_field.typ, field.pos) @@ -948,6 +954,7 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type { c.error('array append cannot be used in an expression', infix_expr.pos) } // `array << elm` + c.check_expr_opt_call(infix_expr.right, right_type) infix_expr.auto_locked, _ = c.fail_if_immutable(infix_expr.left) left_value_type := c.table.value_type(left_type) left_value_sym := c.table.get_type_symbol(left_value_type) @@ -2340,7 +2347,8 @@ pub fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type table.Type) t pub fn (mut c Checker) check_or_expr(or_expr ast.OrExpr, ret_type table.Type, expr_return_type table.Type) { if or_expr.kind == .propagate { - if !c.cur_fn.return_type.has_flag(.optional) && c.cur_fn.name != 'main.main' { + if !c.cur_fn.return_type.has_flag(.optional) && c.cur_fn.name != 'main.main' + && !c.inside_const { c.error('to propagate the optional call, `$c.cur_fn.name` must return an optional', or_expr.pos) } @@ -2697,12 +2705,7 @@ pub fn (mut c Checker) const_decl(mut node ast.ConstDecl) { for i, field in node.fields { c.const_decl = field.name c.const_deps << field.name - mut typ := c.expr(field.expr) - if field.expr is ast.CallExpr { - if field.expr.or_block.kind != .absent { - typ = typ.clear_flag(.optional) - } - } + typ := c.check_expr_opt_call(field.expr, c.expr(field.expr)) node.fields[i].typ = c.table.mktyp(typ) for cd in c.const_deps { for j, f in node.fields { @@ -3207,9 +3210,10 @@ pub fn (mut c Checker) array_init(mut array_init ast.ArrayInit) table.Type { } } if array_init.has_default { - default_typ := c.expr(array_init.default_expr) + default_expr := array_init.default_expr + default_typ := c.check_expr_opt_call(default_expr, c.expr(default_expr)) c.check_expected(default_typ, array_init.elem_type) or { - c.error(err.msg, array_init.default_expr.position()) + c.error(err.msg, default_expr.position()) } } if array_init.has_len { @@ -3513,7 +3517,7 @@ fn (mut c Checker) stmt(node ast.Stmt) { fn (mut c Checker) assert_stmt(node ast.AssertStmt) { cur_exp_typ := c.expected_type - assert_type := c.expr(node.expr) + assert_type := c.check_expr_opt_call(node.expr, c.expr(node.expr)) if assert_type != table.bool_type_idx { atype_name := c.table.get_type_symbol(assert_type).name c.error('assert can be used only with `bool` expressions, but found `$atype_name` instead', diff --git a/vlib/v/checker/tests/optional_fn_err.out b/vlib/v/checker/tests/optional_fn_err.out index 0f7652ed6c..7c889dfbaf 100644 --- a/vlib/v/checker/tests/optional_fn_err.out +++ b/vlib/v/checker/tests/optional_fn_err.out @@ -1,104 +1,160 @@ -vlib/v/checker/tests/optional_fn_err.vv:25:2: error: foo() returns an option, so it should have either an `or {}` block, or `?` at the end - 23 | fn main() { - 24 | // Calling foo() without ? or an or block, should be an error. - 25 | foo() - | ~~~~~ - 26 | - 27 | _ := bar(0) -vlib/v/checker/tests/optional_fn_err.vv:27:7: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end - 25 | foo() - 26 | - 27 | _ := bar(0) - | ~~~~~~ - 28 | _ := [bar(0)] - 29 | -vlib/v/checker/tests/optional_fn_err.vv:28:8: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end - 26 | - 27 | _ := bar(0) - 28 | _ := [bar(0)] - | ~~~~~~ - 29 | - 30 | // Calling fn with optional argument -vlib/v/checker/tests/optional_fn_err.vv:31:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end - 29 | - 30 | // Calling fn with optional argument - 31 | println(twice(bar(0))) +vlib/v/checker/tests/optional_fn_err.vv:13:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 11 | + 12 | const ( + 13 | const_value = bar(0) | ~~~~~~ - 32 | // method and fn field - 33 | mut v := Data{fn (_ int) {}, 1} -vlib/v/checker/tests/optional_fn_err.vv:34:8: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end - 32 | // method and fn field - 33 | mut v := Data{fn (_ int) {}, 1} - 34 | v.add(bar(0)) - | ~~~~~~ - 35 | v.f(bar(0)) - 36 | // anon fn -vlib/v/checker/tests/optional_fn_err.vv:35:6: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end - 33 | mut v := Data{fn (_ int) {}, 1} - 34 | v.add(bar(0)) - 35 | v.f(bar(0)) - | ~~~~~~ - 36 | // anon fn - 37 | fn (_ int) {}(bar(0)) -vlib/v/checker/tests/optional_fn_err.vv:37:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end - 35 | v.f(bar(0)) - 36 | // anon fn - 37 | fn (_ int) {}(bar(0)) - | ~~~~~~ - 38 | // array builtin methods - 39 | mut arr := [1, 2] -vlib/v/checker/tests/optional_fn_err.vv:40:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end - 38 | // array builtin methods - 39 | mut arr := [1, 2] - 40 | arr.insert(0, bar(0)) - | ~~~~~~ - 41 | arr.prepend(bar(0)) - 42 | arr.contains(bar(0)) -vlib/v/checker/tests/optional_fn_err.vv:41:14: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end - 39 | mut arr := [1, 2] - 40 | arr.insert(0, bar(0)) - 41 | arr.prepend(bar(0)) + 14 | ) + 15 | +vlib/v/checker/tests/optional_fn_err.vv:19:14: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 17 | f fn (int) + 18 | mut: + 19 | value int = bar(0) | ~~~~~~ - 42 | arr.contains(bar(0)) - 43 | arr.index(bar(0)) -vlib/v/checker/tests/optional_fn_err.vv:42:15: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end - 40 | arr.insert(0, bar(0)) - 41 | arr.prepend(bar(0)) - 42 | arr.contains(bar(0)) + 20 | opt ?int = bar(0) + 21 | } +vlib/v/checker/tests/optional_fn_err.vv:33:2: error: foo() returns an option, so it should have either an `or {}` block, or `?` at the end + 31 | fn main() { + 32 | // call fn + 33 | foo() + | ~~~~~ + 34 | _ := bar(0) + 35 | println(twice(bar(0))) +vlib/v/checker/tests/optional_fn_err.vv:34:7: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 32 | // call fn + 33 | foo() + 34 | _ := bar(0) + | ~~~~~~ + 35 | println(twice(bar(0))) + 36 | +vlib/v/checker/tests/optional_fn_err.vv:35:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 33 | foo() + 34 | _ := bar(0) + 35 | println(twice(bar(0))) + | ~~~~~~ + 36 | + 37 | // anon fn +vlib/v/checker/tests/optional_fn_err.vv:38:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 36 | + 37 | // anon fn + 38 | fn (_ int) {}(bar(0)) + | ~~~~~~ + 39 | + 40 | // assert +vlib/v/checker/tests/optional_fn_err.vv:41:9: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 39 | + 40 | // assert + 41 | assert bar(true) + | ~~~~~~~~~ + 42 | + 43 | // struct +vlib/v/checker/tests/optional_fn_err.vv:46:10: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 44 | mut v := Data{ + 45 | f: fn (_ int) {}, + 46 | value: bar(0), + | ~~~~~~ + 47 | opt: bar(0), + 48 | } +vlib/v/checker/tests/optional_fn_err.vv:49:8: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 47 | opt: bar(0), + 48 | } + 49 | v.add(bar(0)) // call method + | ~~~~~~ + 50 | v.f(bar(0)) // call fn field + 51 | +vlib/v/checker/tests/optional_fn_err.vv:50:6: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 48 | } + 49 | v.add(bar(0)) // call method + 50 | v.f(bar(0)) // call fn field + | ~~~~~~ + 51 | + 52 | // array +vlib/v/checker/tests/optional_fn_err.vv:54:9: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 52 | // array + 53 | mut arr := [1, 2] + 54 | arr << bar(0) + | ~~~~~~ + 55 | // init + 56 | _ := [bar(0)] +vlib/v/checker/tests/optional_fn_err.vv:56:8: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 54 | arr << bar(0) + 55 | // init + 56 | _ := [bar(0)] + | ~~~~~~ + 57 | _ := []int{init: bar(0)} + 58 | _ := [bar(0)]! +vlib/v/checker/tests/optional_fn_err.vv:57:19: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 55 | // init + 56 | _ := [bar(0)] + 57 | _ := []int{init: bar(0)} + | ~~~~~~ + 58 | _ := [bar(0)]! + 59 | _ := [1]int{init: bar(0)} +vlib/v/checker/tests/optional_fn_err.vv:58:8: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 56 | _ := [bar(0)] + 57 | _ := []int{init: bar(0)} + 58 | _ := [bar(0)]! + | ~~~~~~ + 59 | _ := [1]int{init: bar(0)} + 60 | // index +vlib/v/checker/tests/optional_fn_err.vv:61:13: error: cannot use optional as index (array type `[]int`) + 59 | _ := [1]int{init: bar(0)} + 60 | // index + 61 | println(arr[bar(0)]) + | ~~~~~~~~ + 62 | // array builtin methods + 63 | arr.insert(0, bar(0)) +vlib/v/checker/tests/optional_fn_err.vv:63:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 61 | println(arr[bar(0)]) + 62 | // array builtin methods + 63 | arr.insert(0, bar(0)) + | ~~~~~~ + 64 | arr.prepend(bar(0)) + 65 | arr.contains(bar(0)) +vlib/v/checker/tests/optional_fn_err.vv:64:14: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 62 | // array builtin methods + 63 | arr.insert(0, bar(0)) + 64 | arr.prepend(bar(0)) + | ~~~~~~ + 65 | arr.contains(bar(0)) + 66 | arr.index(bar(0)) +vlib/v/checker/tests/optional_fn_err.vv:65:15: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 63 | arr.insert(0, bar(0)) + 64 | arr.prepend(bar(0)) + 65 | arr.contains(bar(0)) | ~~~~~~ - 43 | arr.index(bar(0)) - 44 | println(arr.map(bar(0))) -vlib/v/checker/tests/optional_fn_err.vv:43:12: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end - 41 | arr.prepend(bar(0)) - 42 | arr.contains(bar(0)) - 43 | arr.index(bar(0)) + 66 | arr.index(bar(0)) + 67 | println(arr.map(bar(0))) +vlib/v/checker/tests/optional_fn_err.vv:66:12: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 64 | arr.prepend(bar(0)) + 65 | arr.contains(bar(0)) + 66 | arr.index(bar(0)) | ~~~~~~ - 44 | println(arr.map(bar(0))) - 45 | println(arr.filter(bar(true))) -vlib/v/checker/tests/optional_fn_err.vv:44:18: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end - 42 | arr.contains(bar(0)) - 43 | arr.index(bar(0)) - 44 | println(arr.map(bar(0))) + 67 | println(arr.map(bar(0))) + 68 | println(arr.filter(bar(true))) +vlib/v/checker/tests/optional_fn_err.vv:67:18: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 65 | arr.contains(bar(0)) + 66 | arr.index(bar(0)) + 67 | println(arr.map(bar(0))) | ~~~~~~ - 45 | println(arr.filter(bar(true))) - 46 | println(arr.any(bar(true))) -vlib/v/checker/tests/optional_fn_err.vv:45:21: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end - 43 | arr.index(bar(0)) - 44 | println(arr.map(bar(0))) - 45 | println(arr.filter(bar(true))) + 68 | println(arr.filter(bar(true))) + 69 | println(arr.any(bar(true))) +vlib/v/checker/tests/optional_fn_err.vv:68:21: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 66 | arr.index(bar(0)) + 67 | println(arr.map(bar(0))) + 68 | println(arr.filter(bar(true))) | ~~~~~~~~~ - 46 | println(arr.any(bar(true))) - 47 | println(arr.all(bar(true))) -vlib/v/checker/tests/optional_fn_err.vv:46:18: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end - 44 | println(arr.map(bar(0))) - 45 | println(arr.filter(bar(true))) - 46 | println(arr.any(bar(true))) + 69 | println(arr.any(bar(true))) + 70 | println(arr.all(bar(true))) +vlib/v/checker/tests/optional_fn_err.vv:69:18: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 67 | println(arr.map(bar(0))) + 68 | println(arr.filter(bar(true))) + 69 | println(arr.any(bar(true))) | ~~~~~~~~~ - 47 | println(arr.all(bar(true))) - 48 | } -vlib/v/checker/tests/optional_fn_err.vv:47:18: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end - 45 | println(arr.filter(bar(true))) - 46 | println(arr.any(bar(true))) - 47 | println(arr.all(bar(true))) + 70 | println(arr.all(bar(true))) + 71 | } +vlib/v/checker/tests/optional_fn_err.vv:70:18: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 68 | println(arr.filter(bar(true))) + 69 | println(arr.any(bar(true))) + 70 | println(arr.all(bar(true))) | ~~~~~~~~~ - 48 | } + 71 | } diff --git a/vlib/v/checker/tests/optional_fn_err.vv b/vlib/v/checker/tests/optional_fn_err.vv index 390b47b627..90518a8360 100644 --- a/vlib/v/checker/tests/optional_fn_err.vv +++ b/vlib/v/checker/tests/optional_fn_err.vv @@ -1,3 +1,6 @@ + +// use optional without ? or an or block in places where it is not allowed + fn foo() ? { println('foo is called') } @@ -6,10 +9,15 @@ fn bar(v T) ?T { return none } +const ( + const_value = bar(0) +) + struct Data { f fn (int) mut: - value int + value int = bar(0) + opt ?int = bar(0) } fn (mut v Data) add(n int) { @@ -21,22 +29,37 @@ fn twice(n int) int { } fn main() { - // Calling foo() without ? or an or block, should be an error. + // call fn foo() - _ := bar(0) - _ := [bar(0)] - - // Calling fn with optional argument println(twice(bar(0))) - // method and fn field - mut v := Data{fn (_ int) {}, 1} - v.add(bar(0)) - v.f(bar(0)) + // anon fn fn (_ int) {}(bar(0)) - // array builtin methods + + // assert + assert bar(true) + + // struct + mut v := Data{ + f: fn (_ int) {}, + value: bar(0), + opt: bar(0), + } + v.add(bar(0)) // call method + v.f(bar(0)) // call fn field + + // array mut arr := [1, 2] + arr << bar(0) + // init + _ := [bar(0)] + _ := []int{init: bar(0)} + _ := [bar(0)]! + _ := [1]int{init: bar(0)} + // index + println(arr[bar(0)]) + // array builtin methods arr.insert(0, bar(0)) arr.prepend(bar(0)) arr.contains(bar(0)) diff --git a/vlib/v/checker/tests/optional_index_err.out b/vlib/v/checker/tests/optional_index_err.out deleted file mode 100644 index ba5f4abdd8..0000000000 --- a/vlib/v/checker/tests/optional_index_err.out +++ /dev/null @@ -1,6 +0,0 @@ -vlib/v/checker/tests/optional_index_err.vv:3:14: error: cannot use optional as index (type `string`) - 1 | fn main() { - 2 | v := 'hello' - 3 | println(v[v.last_index('l')]) - | ~~~~~~~~~~~~~~~~~~~ - 4 | } diff --git a/vlib/v/checker/tests/optional_index_err.vv b/vlib/v/checker/tests/optional_index_err.vv deleted file mode 100644 index e34bc6f66a..0000000000 --- a/vlib/v/checker/tests/optional_index_err.vv +++ /dev/null @@ -1,4 +0,0 @@ -fn main() { - v := 'hello' - println(v[v.last_index('l')]) -} diff --git a/vlib/v/checker/tests/optional_method_err.out b/vlib/v/checker/tests/optional_method_err.out deleted file mode 100644 index a67b707bf0..0000000000 --- a/vlib/v/checker/tests/optional_method_err.out +++ /dev/null @@ -1,7 +0,0 @@ -vlib/v/checker/tests/optional_method_err.vv:17:10: error: inc_to_limit() returns an option, so it should have either an `or {}` block, or `?` at the end - 15 | mut a := Abc{} - 16 | for _ in 0 .. 4 { - 17 | _ := a.inc_to_limit(2) - | ~~~~~~~~~~~~~~~ - 18 | eprintln('a: $a') - 19 | } diff --git a/vlib/v/checker/tests/optional_method_err.vv b/vlib/v/checker/tests/optional_method_err.vv deleted file mode 100644 index 01cf15bc16..0000000000 --- a/vlib/v/checker/tests/optional_method_err.vv +++ /dev/null @@ -1,20 +0,0 @@ -struct Abc { -mut: - x int -} - -fn (mut a Abc) inc_to_limit(max int) ?int { - if a.x >= max { - return error('x is already $max') - } - a.x++ - return a.x -} - -fn main() { - mut a := Abc{} - for _ in 0 .. 4 { - _ := a.inc_to_limit(2) - eprintln('a: $a') - } -}