Skip to content

Commit

Permalink
checker: fix and cleanup uninitialized checks for container elements …
Browse files Browse the repository at this point in the history
…when has len(fix vlang#20272)
  • Loading branch information
shove70 committed Dec 28, 2023
1 parent 02c0f3f commit 17f67d2
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 119 deletions.
72 changes: 32 additions & 40 deletions vlib/v/checker/containers.v
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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))
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}

This file was deleted.

This file was deleted.

70 changes: 70 additions & 0 deletions vlib/v/checker/tests/array_init_without_init_value_err.out
Original file line number Diff line number Diff line change
@@ -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 | }
57 changes: 57 additions & 0 deletions vlib/v/checker/tests/array_init_without_init_value_err.vv
Original file line number Diff line number Diff line change
@@ -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]()
}

This file was deleted.

16 changes: 0 additions & 16 deletions vlib/v/checker/tests/array_of_interfaces_with_len_without_init.vv

This file was deleted.

27 changes: 0 additions & 27 deletions vlib/v/checker/tests/ptr_array_init.out

This file was deleted.

6 changes: 0 additions & 6 deletions vlib/v/checker/tests/ptr_array_init.vv

This file was deleted.

0 comments on commit 17f67d2

Please sign in to comment.