checker: fix and cleanup uninitialized checks for array initialisers with len: (fix #20272) (#20279)

This commit is contained in:
shove 2023-12-29 05:07:02 +08:00 committed by GitHub
parent 5b96d8d179
commit 2dce525d90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 160 additions and 120 deletions

View File

@ -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
}

View File

@ -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 | }

View File

@ -1,9 +0,0 @@
type Foo = int | string
fn main() {
a := []Foo{len: 10}
println(a)
fixed_a := [10]Foo{}
println(fixed_a)
}

View File

@ -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 | }

View File

@ -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]()
}

View File

@ -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 | }

View File

@ -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)
}

View File

@ -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 | }

View File

@ -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}
}

View File

@ -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}