From 2dce525d90ca36715367ad565916961c088cef25 Mon Sep 17 00:00:00 2001 From: shove Date: Fri, 29 Dec 2023 05:07:02 +0800 Subject: [PATCH] checker: fix and cleanup uninitialized checks for array initialisers with `len:` (fix #20272) (#20279) --- vlib/v/checker/containers.v | 72 +++++++++---------- ...um_type_without_init_value_and_len_err.out | 14 ---- ...sum_type_without_init_value_and_len_err.vv | 9 --- .../array_init_without_init_value_err.out | 70 ++++++++++++++++++ .../array_init_without_init_value_err.vv | 57 +++++++++++++++ ...ay_of_interfaces_with_len_without_init.out | 7 -- ...ray_of_interfaces_with_len_without_init.vv | 16 ----- vlib/v/checker/tests/ptr_array_init.out | 27 ------- vlib/v/checker/tests/ptr_array_init.vv | 6 -- vlib/v/checker/tests/shared_element_lock.out | 2 +- 10 files changed, 160 insertions(+), 120 deletions(-) delete mode 100644 vlib/v/checker/tests/array_init_sum_type_without_init_value_and_len_err.out delete mode 100644 vlib/v/checker/tests/array_init_sum_type_without_init_value_and_len_err.vv create mode 100644 vlib/v/checker/tests/array_init_without_init_value_err.out create mode 100644 vlib/v/checker/tests/array_init_without_init_value_err.vv delete mode 100644 vlib/v/checker/tests/array_of_interfaces_with_len_without_init.out delete mode 100644 vlib/v/checker/tests/array_of_interfaces_with_len_without_init.vv delete mode 100644 vlib/v/checker/tests/ptr_array_init.out delete mode 100644 vlib/v/checker/tests/ptr_array_init.vv diff --git a/vlib/v/checker/containers.v b/vlib/v/checker/containers.v index a75ae46c44c771..efb556fd1e8705 100644 --- a/vlib/v/checker/containers.v +++ b/vlib/v/checker/containers.v @@ -7,6 +7,7 @@ import v.token fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type { mut elem_type := ast.void_type + unwrap_elem_type := c.unwrap_generic(node.elem_type) // `x := []string{}` (the type was set in the parser) if node.typ != ast.void_type { if node.elem_type != 0 { @@ -76,14 +77,13 @@ fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type { if len_typ.has_flag(.option) { c.error('cannot use unwrapped Option as length', node.len_expr.pos()) } - if node.has_len && !node.has_init { - elem_type_sym := c.table.sym(node.elem_type) - if elem_type_sym.kind == .interface_ { - c.error('cannot instantiate an array of interfaces without also giving a default `init:` value', - node.len_expr.pos()) + // check &int{}, interface, sum_type initialized + if !node.has_init { + c.check_elements_initialized(unwrap_elem_type) or { + c.warn('${err.msg()}, therefore `len:` cannot be used (unless inside `unsafe`, or if you also use `init:`)', + node.pos) } } - c.ensure_sumtype_array_has_default_value(node) } if node.has_cap { cap_typ := c.check_expr_opt_call(node.cap_expr, c.expr(mut node.cap_expr)) @@ -97,26 +97,21 @@ fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type { c.error('generic struct cannot be used in non-generic function', node.pos) } - // `&int{}` check - if node.has_len && !c.check_elements_ref_containers_initialized(node.elem_type) { - c.warn('arrays of references need to be initialized right away, therefore `len:` cannot be used (unless inside `unsafe`)', - node.pos) - } // `&Struct{} check if node.has_len { - c.check_elements_ref_fields_initialized(node.elem_type, node.pos) + c.check_elements_ref_fields_initialized(unwrap_elem_type, node.pos) } return node.typ } if node.is_fixed { - c.ensure_sumtype_array_has_default_value(node) c.ensure_type_exists(node.elem_type, node.elem_type_pos) - if !c.is_builtin_mod && !c.check_elements_ref_containers_initialized(node.elem_type) { - c.warn('fixed arrays of references need to be initialized right away (unless inside `unsafe`)', - node.pos) + if !c.is_builtin_mod { + c.check_elements_initialized(unwrap_elem_type) or { + c.warn('fixed ${err.msg()} (unless inside `unsafe`)', node.pos) + } } - c.check_elements_ref_fields_initialized(node.elem_type, node.pos) + c.check_elements_ref_fields_initialized(unwrap_elem_type, node.pos) } // `a = []` if node.exprs.len == 0 { @@ -351,13 +346,6 @@ fn (mut c Checker) check_array_init_para_type(para string, mut expr ast.Expr, po } } -fn (mut c Checker) ensure_sumtype_array_has_default_value(node ast.ArrayInit) { - sym := c.table.sym(node.elem_type) - if sym.kind == .sum_type && !node.has_init { - c.error('cannot initialize sum type array without default value', node.pos) - } -} - fn (mut c Checker) map_init(mut node ast.MapInit) ast.Type { // `map = {}` if node.keys.len == 0 && node.vals.len == 0 && node.typ == 0 { @@ -574,42 +562,46 @@ fn (mut c Checker) do_check_elements_ref_fields_initialized(sym &ast.TypeSymbol, } } -// check the element, and its children for ref uninitialized containers -fn (mut c Checker) check_elements_ref_containers_initialized(typ ast.Type) bool { +const err_ref_uninitialized = error('arrays of references need to be initialized right away') +const err_interface_uninitialized = error('arrays of interfaces need to be initialized right away') +const err_sumtype_uninitialized = error('arrays of sumtypes need to be initialized right away') + +// check the element, and its children for `ref/interface/sumtype` initialized +fn (mut c Checker) check_elements_initialized(typ ast.Type) ! { if typ == 0 || c.inside_unsafe { - return true + return } if typ.is_any_kind_of_pointer() { - return false + return checker.err_ref_uninitialized } sym := c.table.sym(typ) + if sym.kind == .interface_ { + return checker.err_interface_uninitialized + } else if sym.kind == .sum_type { + return checker.err_sumtype_uninitialized + } + match sym.info { ast.Array { elem_type := sym.info.elem_type - if elem_type.is_any_kind_of_pointer() { - return false - } - return c.check_elements_ref_containers_initialized(elem_type) + return c.check_elements_initialized(elem_type) } ast.ArrayFixed { elem_type := sym.info.elem_type - if elem_type.is_any_kind_of_pointer() && !c.is_builtin_mod { - return false + if !c.is_builtin_mod { + return c.check_elements_initialized(elem_type) } - return c.check_elements_ref_containers_initialized(elem_type) } ast.Map { value_type := sym.info.value_type - if value_type.is_any_kind_of_pointer() && !c.is_builtin_mod { - return false + if !c.is_builtin_mod { + return c.check_elements_initialized(value_type) } - return c.check_elements_ref_containers_initialized(value_type) } ast.Alias { parent_type := sym.info.parent_type - return c.check_elements_ref_containers_initialized(parent_type) + return c.check_elements_initialized(parent_type) } else {} } - return true } diff --git a/vlib/v/checker/tests/array_init_sum_type_without_init_value_and_len_err.out b/vlib/v/checker/tests/array_init_sum_type_without_init_value_and_len_err.out deleted file mode 100644 index 129d9db2d600c3..00000000000000 --- a/vlib/v/checker/tests/array_init_sum_type_without_init_value_and_len_err.out +++ /dev/null @@ -1,14 +0,0 @@ -vlib/v/checker/tests/array_init_sum_type_without_init_value_and_len_err.vv:4:7: error: cannot initialize sum type array without default value - 2 | - 3 | fn main() { - 4 | a := []Foo{len: 10} - | ~~~~~~ - 5 | println(a) - 6 | -vlib/v/checker/tests/array_init_sum_type_without_init_value_and_len_err.vv:7:13: error: cannot initialize sum type array without default value - 5 | println(a) - 6 | - 7 | fixed_a := [10]Foo{} - | ~~~~~~~~~ - 8 | println(fixed_a) - 9 | } diff --git a/vlib/v/checker/tests/array_init_sum_type_without_init_value_and_len_err.vv b/vlib/v/checker/tests/array_init_sum_type_without_init_value_and_len_err.vv deleted file mode 100644 index 2494d5273188ee..00000000000000 --- a/vlib/v/checker/tests/array_init_sum_type_without_init_value_and_len_err.vv +++ /dev/null @@ -1,9 +0,0 @@ -type Foo = int | string - -fn main() { - a := []Foo{len: 10} - println(a) - - fixed_a := [10]Foo{} - println(fixed_a) -} diff --git a/vlib/v/checker/tests/array_init_without_init_value_err.out b/vlib/v/checker/tests/array_init_without_init_value_err.out new file mode 100644 index 00000000000000..980510332b935a --- /dev/null +++ b/vlib/v/checker/tests/array_init_without_init_value_err.out @@ -0,0 +1,70 @@ +vlib/v/checker/tests/array_init_without_init_value_err.vv:5:7: warning: arrays of sumtypes need to be initialized right away, therefore `len:` cannot be used (unless inside `unsafe`, or if you also use `init:`) + 3 | + 4 | fn main_sum_type() { + 5 | a := []Foo{len: 10} + | ~~~~~~ + 6 | println(a) + 7 | fixed_a := [10]Foo{} +vlib/v/checker/tests/array_init_without_init_value_err.vv:7:13: warning: fixed arrays of sumtypes need to be initialized right away (unless inside `unsafe`) + 5 | a := []Foo{len: 10} + 6 | println(a) + 7 | fixed_a := [10]Foo{} + | ~~~~~~~~~ + 8 | println(fixed_a) + 9 | } +vlib/v/checker/tests/array_init_without_init_value_err.vv:20:11: warning: arrays of references need to be initialized right away, therefore `len:` cannot be used (unless inside `unsafe`, or if you also use `init:`) + 18 | // test references uninitialized. + 19 | fn main_ref() { + 20 | println(*[]&int{len: 1}[0]) + | ~~~~~~~ + 21 | println([1]&int{}) + 22 | _ = [][1]&int{len: 1}[0][0] +vlib/v/checker/tests/array_init_without_init_value_err.vv:21:10: warning: fixed arrays of references need to be initialized right away (unless inside `unsafe`) + 19 | fn main_ref() { + 20 | println(*[]&int{len: 1}[0]) + 21 | println([1]&int{}) + | ~~~~~~~~~ + 22 | _ = [][1]&int{len: 1}[0][0] + 23 | _ = []map[int]&int{len: 1} +vlib/v/checker/tests/array_init_without_init_value_err.vv:22:6: warning: arrays of references need to be initialized right away, therefore `len:` cannot be used (unless inside `unsafe`, or if you also use `init:`) + 20 | println(*[]&int{len: 1}[0]) + 21 | println([1]&int{}) + 22 | _ = [][1]&int{len: 1}[0][0] + | ~~~~~~~~~~ + 23 | _ = []map[int]&int{len: 1} + 24 | } +vlib/v/checker/tests/array_init_without_init_value_err.vv:23:6: warning: arrays of references need to be initialized right away, therefore `len:` cannot be used (unless inside `unsafe`, or if you also use `init:`) + 21 | println([1]&int{}) + 22 | _ = [][1]&int{len: 1}[0][0] + 23 | _ = []map[int]&int{len: 1} + | ~~~~~~~~~~~~~~~ + 24 | } + 25 | +vlib/v/checker/tests/array_init_without_init_value_err.vv:40:22: warning: arrays of interfaces need to be initialized right away, therefore `len:` cannot be used (unless inside `unsafe`, or if you also use `init:`) + 38 | + 39 | fn main_interface() { + 40 | mut parsed_lines := []MObject{len: 9} + | ~~~~~~~~~~ + 41 | println(parsed_lines) + 42 | } +vlib/v/checker/tests/array_init_without_init_value_err.vv:12:7: warning: arrays of sumtypes need to be initialized right away, therefore `len:` cannot be used (unless inside `unsafe`, or if you also use `init:`) + 10 | + 11 | fn main_sum_type_2[T]() { + 12 | a := []T{len: 10} + | ~~~~ + 13 | println(a) + 14 | fixed_a := [10]T{} +vlib/v/checker/tests/array_init_without_init_value_err.vv:14:13: warning: fixed arrays of sumtypes need to be initialized right away (unless inside `unsafe`) + 12 | a := []T{len: 10} + 13 | println(a) + 14 | fixed_a := [10]T{} + | ~~~~~~~ + 15 | println(fixed_a) + 16 | } +vlib/v/checker/tests/array_init_without_init_value_err.vv:45:22: warning: arrays of interfaces need to be initialized right away, therefore `len:` cannot be used (unless inside `unsafe`, or if you also use `init:`) + 43 | + 44 | fn main_interface_2[T]() { + 45 | mut parsed_lines := []T{len: 9} + | ~~~~ + 46 | println(parsed_lines) + 47 | } diff --git a/vlib/v/checker/tests/array_init_without_init_value_err.vv b/vlib/v/checker/tests/array_init_without_init_value_err.vv new file mode 100644 index 00000000000000..148ec6d3c5d0b4 --- /dev/null +++ b/vlib/v/checker/tests/array_init_without_init_value_err.vv @@ -0,0 +1,57 @@ +// test sum_types uninitialized. +type Foo = int | string + +fn main_sum_type() { + a := []Foo{len: 10} + println(a) + fixed_a := [10]Foo{} + println(fixed_a) +} + +fn main_sum_type_2[T]() { + a := []T{len: 10} + println(a) + fixed_a := [10]T{} + println(fixed_a) +} + +// test references uninitialized. +fn main_ref() { + println(*[]&int{len: 1}[0]) + println([1]&int{}) + _ = [][1]&int{len: 1}[0][0] + _ = []map[int]&int{len: 1} +} + +// test interfaces uninitialized. +interface MObject { + give_string() string +} + +struct LeStruct { + le_string string +} + +fn (a LeStruct) give_string() string { + return 'V' +} + +fn main_interface() { + mut parsed_lines := []MObject{len: 9} + println(parsed_lines) +} + +fn main_interface_2[T]() { + mut parsed_lines := []T{len: 9} + println(parsed_lines) +} + +fn main() { + main_sum_type() + main_sum_type_2[Foo]() + + main_ref() + + main_interface() + main_interface_2[MObject]() +} diff --git a/vlib/v/checker/tests/array_of_interfaces_with_len_without_init.out b/vlib/v/checker/tests/array_of_interfaces_with_len_without_init.out deleted file mode 100644 index bfd51b03ecb5b4..00000000000000 --- a/vlib/v/checker/tests/array_of_interfaces_with_len_without_init.out +++ /dev/null @@ -1,7 +0,0 @@ -vlib/v/checker/tests/array_of_interfaces_with_len_without_init.vv:14:37: error: cannot instantiate an array of interfaces without also giving a default `init:` value - 12 | - 13 | fn main() { - 14 | mut parsed_lines := []MObject{len: 9} - | ^ - 15 | println(parsed_lines) - 16 | } diff --git a/vlib/v/checker/tests/array_of_interfaces_with_len_without_init.vv b/vlib/v/checker/tests/array_of_interfaces_with_len_without_init.vv deleted file mode 100644 index d9bc2d728ec657..00000000000000 --- a/vlib/v/checker/tests/array_of_interfaces_with_len_without_init.vv +++ /dev/null @@ -1,16 +0,0 @@ -interface MObject { - give_string() string -} - -struct LeStruct { - le_string string -} - -fn (a LeStruct) give_string() string { - return 'V' -} - -fn main() { - mut parsed_lines := []MObject{len: 9} - println(parsed_lines) -} diff --git a/vlib/v/checker/tests/ptr_array_init.out b/vlib/v/checker/tests/ptr_array_init.out deleted file mode 100644 index afc8cbe69c28a6..00000000000000 --- a/vlib/v/checker/tests/ptr_array_init.out +++ /dev/null @@ -1,27 +0,0 @@ -vlib/v/checker/tests/ptr_array_init.vv:2:11: warning: arrays of references need to be initialized right away, therefore `len:` cannot be used (unless inside `unsafe`) - 1 | fn main() { - 2 | println(*[]&int{len: 1}[0]) - | ~~~~~~~ - 3 | println([1]&int{}) - 4 | _ = [][1]&int{len: 1}[0][0] -vlib/v/checker/tests/ptr_array_init.vv:3:10: warning: fixed arrays of references need to be initialized right away (unless inside `unsafe`) - 1 | fn main() { - 2 | println(*[]&int{len: 1}[0]) - 3 | println([1]&int{}) - | ~~~~~~~~~ - 4 | _ = [][1]&int{len: 1}[0][0] - 5 | _ = []map[int]&int{len: 1} -vlib/v/checker/tests/ptr_array_init.vv:4:6: warning: arrays of references need to be initialized right away, therefore `len:` cannot be used (unless inside `unsafe`) - 2 | println(*[]&int{len: 1}[0]) - 3 | println([1]&int{}) - 4 | _ = [][1]&int{len: 1}[0][0] - | ~~~~~~~~~~ - 5 | _ = []map[int]&int{len: 1} - 6 | } -vlib/v/checker/tests/ptr_array_init.vv:5:6: warning: arrays of references need to be initialized right away, therefore `len:` cannot be used (unless inside `unsafe`) - 3 | println([1]&int{}) - 4 | _ = [][1]&int{len: 1}[0][0] - 5 | _ = []map[int]&int{len: 1} - | ~~~~~~~~~~~~~~~ - 6 | } - \ No newline at end of file diff --git a/vlib/v/checker/tests/ptr_array_init.vv b/vlib/v/checker/tests/ptr_array_init.vv deleted file mode 100644 index e0abe1fe723893..00000000000000 --- a/vlib/v/checker/tests/ptr_array_init.vv +++ /dev/null @@ -1,6 +0,0 @@ -fn main() { - println(*[]&int{len: 1}[0]) - println([1]&int{}) - _ = [][1]&int{len: 1}[0][0] - _ = []map[int]&int{len: 1} -} diff --git a/vlib/v/checker/tests/shared_element_lock.out b/vlib/v/checker/tests/shared_element_lock.out index 3867d2af3d2bd2..f1d54cf3cf668c 100644 --- a/vlib/v/checker/tests/shared_element_lock.out +++ b/vlib/v/checker/tests/shared_element_lock.out @@ -1,4 +1,4 @@ -vlib/v/checker/tests/shared_element_lock.vv:43:11: warning: arrays of references need to be initialized right away, therefore `len:` cannot be used (unless inside `unsafe`) +vlib/v/checker/tests/shared_element_lock.vv:43:11: warning: arrays of references need to be initialized right away, therefore `len:` cannot be used (unless inside `unsafe`, or if you also use `init:`) 41 | shared g := Pro{} 42 | g.pers.age = 42 43 | mut h := []shared Pro{len: 3}