v/vlib/v/checker/containers.v

290 lines
9.1 KiB
V

// Copyright (c) 2019-2022 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
module checker
import v.ast
import v.token
pub fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type {
mut elem_type := ast.void_type
// []string - was set in parser
if node.typ != ast.void_type {
if node.exprs.len == 0 {
if node.has_cap {
c.check_array_init_para_type('cap', node.cap_expr, node.pos)
}
if node.has_len {
c.check_array_init_para_type('len', node.len_expr, node.pos)
}
}
if node.has_default {
default_expr := node.default_expr
default_typ := c.check_expr_opt_call(default_expr, c.expr(default_expr))
node.default_type = default_typ
c.check_expected(default_typ, node.elem_type) or {
c.error(err.msg(), default_expr.pos())
}
}
if node.has_len {
if node.has_len && !node.has_default {
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())
}
}
c.ensure_sumtype_array_has_default_value(node)
}
c.ensure_type_exists(node.elem_type, node.elem_type_pos) or {}
if node.typ.has_flag(.generic) && c.table.cur_fn.generic_names.len == 0 {
c.error('generic struct cannot use in non-generic function', 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) or {}
}
// a = []
if node.exprs.len == 0 {
// a := fn_returing_opt_array() or { [] }
if c.expected_type == ast.void_type && c.expected_or_type != ast.void_type {
c.expected_type = c.expected_or_type
}
mut type_sym := c.table.sym(c.expected_type)
if type_sym.kind != .array || type_sym.array_info().elem_type == ast.void_type {
c.error('array_init: no type specified (maybe: `[]Type{}` instead of `[]`)',
node.pos)
return ast.void_type
}
// TODO: seperate errors once bug is fixed with `x := if expr { ... } else { ... }`
// if c.expected_type == ast.void_type {
// c.error('array_init: use `[]Type{}` instead of `[]`', node.pos)
// return ast.void_type
// }
array_info := type_sym.array_info()
node.elem_type = array_info.elem_type
// clear optional flag incase of: `fn opt_arr ?[]int { return [] }`
return c.expected_type.clear_flag(.optional)
}
// [1,2,3]
if node.exprs.len > 0 && node.elem_type == ast.void_type {
mut expected_value_type := ast.void_type
mut expecting_interface_array := false
if c.expected_type != 0 {
expected_value_type = c.table.value_type(c.expected_type)
if c.table.sym(expected_value_type).kind == .interface_ {
// Array of interfaces? (`[dog, cat]`) Save the interface type (`Animal`)
expecting_interface_array = true
}
}
// expecting_interface_array := c.expected_type != 0 &&
// c.table.sym(c.table.value_type(c.expected_type)).kind == .interface_
//
// if expecting_interface_array {
// println('ex $c.expected_type')
// }
for i, mut expr in node.exprs {
typ := c.check_expr_opt_call(expr, c.expr(expr))
if typ == ast.void_type {
c.error('invalid void array element type', expr.pos())
}
node.expr_types << typ
// The first element's type
if expecting_interface_array {
if i == 0 {
elem_type = expected_value_type
c.expected_type = elem_type
c.type_implements(typ, elem_type, expr.pos())
}
if !typ.is_ptr() && !typ.is_pointer() && !c.inside_unsafe {
typ_sym := c.table.sym(typ)
if typ_sym.kind != .interface_ {
c.mark_as_referenced(mut &expr, true)
}
}
continue
}
// The first element's type
if i == 0 {
if expr.is_auto_deref_var() {
elem_type = ast.mktyp(typ.deref())
} else {
elem_type = ast.mktyp(typ)
}
c.expected_type = elem_type
continue
}
if expr !is ast.TypeNode {
c.check_expected(typ, elem_type) or {
c.error('invalid array element: $err.msg()', expr.pos())
}
}
}
if node.is_fixed {
idx := c.table.find_or_register_array_fixed(elem_type, node.exprs.len, ast.empty_expr())
if elem_type.has_flag(.generic) {
node.typ = ast.new_type(idx).set_flag(.generic)
} else {
node.typ = ast.new_type(idx)
}
} else {
idx := c.table.find_or_register_array(elem_type)
if elem_type.has_flag(.generic) {
node.typ = ast.new_type(idx).set_flag(.generic)
} else {
node.typ = ast.new_type(idx)
}
}
node.elem_type = elem_type
} else if node.is_fixed && node.exprs.len == 1 && node.elem_type != ast.void_type {
// [50]byte
mut fixed_size := i64(0)
init_expr := node.exprs[0]
c.expr(init_expr)
match init_expr {
ast.IntegerLiteral {
fixed_size = init_expr.val.int()
}
ast.Ident {
if init_expr.obj is ast.ConstField {
if comptime_value := c.eval_comptime_const_expr(init_expr.obj.expr,
0)
{
fixed_size = comptime_value.i64() or { fixed_size }
}
} else {
c.error('non-constant array bound `$init_expr.name`', init_expr.pos)
}
}
ast.InfixExpr {
if comptime_value := c.eval_comptime_const_expr(init_expr, 0) {
fixed_size = comptime_value.i64() or { fixed_size }
}
}
else {
c.error('fixed array size cannot use non-constant value', init_expr.pos())
}
}
if fixed_size <= 0 {
c.error('fixed size cannot be zero or negative (fixed_size: $fixed_size)',
init_expr.pos())
}
idx := c.table.find_or_register_array_fixed(node.elem_type, int(fixed_size), init_expr)
if node.elem_type.has_flag(.generic) {
node.typ = ast.new_type(idx).set_flag(.generic)
} else {
node.typ = ast.new_type(idx)
}
if node.has_default {
c.expr(node.default_expr)
}
}
return node.typ
}
fn (mut c Checker) check_array_init_para_type(para string, expr ast.Expr, pos token.Pos) {
sym := c.table.sym(c.expr(expr))
if sym.kind !in [.int, .int_literal] {
c.error('array $para needs to be an int', pos)
}
}
pub 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_default {
c.error('cannot initialize sum type array without default value', node.pos)
}
}
pub 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 {
sym := c.table.sym(c.expected_type)
if sym.kind == .map {
info := sym.map_info()
node.typ = c.expected_type.clear_flag(.optional)
node.key_type = info.key_type
node.value_type = info.value_type
return node.typ
} else {
if sym.kind == .struct_ {
c.error('`{}` can not be used for initialising empty structs any more. Use `${c.table.type_to_str(c.expected_type)}{}` instead.',
node.pos)
} else {
c.error('invalid empty map initialisation syntax, use e.g. map[string]int{} instead',
node.pos)
}
return ast.void_type
}
}
// `x := map[string]string` - set in parser
if node.typ != 0 {
info := c.table.sym(node.typ).map_info()
c.ensure_type_exists(info.key_type, node.pos) or {}
c.ensure_type_exists(info.value_type, node.pos) or {}
node.key_type = info.key_type
node.value_type = info.value_type
return node.typ
}
if node.keys.len > 0 && node.vals.len > 0 {
mut key0_type := ast.void_type
mut val0_type := ast.void_type
use_expected_type := c.expected_type != ast.void_type && !c.inside_const
&& c.table.sym(c.expected_type).kind == .map
if use_expected_type {
sym := c.table.sym(c.expected_type)
info := sym.map_info()
key0_type = c.unwrap_generic(info.key_type)
val0_type = c.unwrap_generic(info.value_type)
} else {
// `{'age': 20}`
key0_type = ast.mktyp(c.expr(node.keys[0]))
if node.keys[0].is_auto_deref_var() {
key0_type = key0_type.deref()
}
val0_type = ast.mktyp(c.expr(node.vals[0]))
if node.vals[0].is_auto_deref_var() {
val0_type = val0_type.deref()
}
node.val_types << val0_type
}
mut same_key_type := true
for i, key in node.keys {
if i == 0 && !use_expected_type {
continue
}
val := node.vals[i]
c.expected_type = key0_type
key_type := c.expr(key)
c.expected_type = val0_type
val_type := c.expr(val)
node.val_types << val_type
if !c.check_types(key_type, key0_type) || (i == 0 && key_type.is_number()
&& key0_type.is_number() && key0_type != ast.mktyp(key_type)) {
msg := c.expected_msg(key_type, key0_type)
c.error('invalid map key: $msg', key.pos())
same_key_type = false
}
if !c.check_types(val_type, val0_type) || (i == 0 && val_type.is_number()
&& val0_type.is_number() && val0_type != ast.mktyp(val_type)) {
msg := c.expected_msg(val_type, val0_type)
c.error('invalid map value: $msg', val.pos())
}
}
if same_key_type {
for i in 1 .. node.keys.len {
c.check_dup_keys(node, i)
}
}
key0_type = c.unwrap_generic(key0_type)
val0_type = c.unwrap_generic(val0_type)
mut map_type := ast.new_type(c.table.find_or_register_map(key0_type, val0_type))
node.typ = map_type
node.key_type = key0_type
node.value_type = val0_type
return map_type
}
return node.typ
}