From 64f1ea6fe95342f95ba01438ae6c06a44ec6dd8c Mon Sep 17 00:00:00 2001 From: yuyi Date: Sun, 2 Jan 2022 21:52:19 +0800 Subject: [PATCH] checker: extract containers.v, struct.v, for.v from checker.v (#13012) --- vlib/v/checker/checker.v | 832 ------------------------------------ vlib/v/checker/containers.v | 283 ++++++++++++ vlib/v/checker/for.v | 172 ++++++++ vlib/v/checker/struct.v | 393 +++++++++++++++++ 4 files changed, 848 insertions(+), 832 deletions(-) create mode 100644 vlib/v/checker/containers.v create mode 100644 vlib/v/checker/for.v create mode 100644 vlib/v/checker/struct.v diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 43fd827491..9f4f041ef1 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -514,394 +514,6 @@ pub fn (mut c Checker) expand_iface_embeds(idecl &ast.InterfaceDecl, level int, return ares } -pub fn (mut c Checker) struct_decl(mut node ast.StructDecl) { - if node.language == .v && !c.is_builtin_mod { - c.check_valid_pascal_case(node.name, 'struct name', node.pos) - } - mut struct_sym := c.table.find_type(node.name) or { ast.invalid_type_symbol } - mut has_generic_types := false - if mut struct_sym.info is ast.Struct { - for embed in node.embeds { - if embed.typ.has_flag(.generic) { - has_generic_types = true - } - embed_sym := c.table.sym(embed.typ) - if embed_sym.kind != .struct_ { - c.error('`$embed_sym.name` is not a struct', embed.pos) - } else { - info := embed_sym.info as ast.Struct - if info.is_heap && !embed.typ.is_ptr() { - struct_sym.info.is_heap = true - } - } - } - for attr in node.attrs { - if attr.name == 'typedef' && node.language != .c { - c.error('`typedef` attribute can only be used with C structs', node.pos) - } - } - for i, field in node.fields { - if field.typ == ast.any_type { - c.error('struct field cannot be the `any` type, use generics instead', - field.type_pos) - } - c.ensure_type_exists(field.typ, field.type_pos) or { return } - if field.typ.has_flag(.generic) { - has_generic_types = true - } - if node.language == .v { - c.check_valid_snake_case(field.name, 'field name', field.pos) - } - sym := c.table.sym(field.typ) - for j in 0 .. i { - if field.name == node.fields[j].name { - c.error('field name `$field.name` duplicate', field.pos) - } - } - if sym.kind == .struct_ { - info := sym.info as ast.Struct - if info.is_heap && !field.typ.is_ptr() { - struct_sym.info.is_heap = true - } - } - if field.has_default_expr { - c.expected_type = field.typ - 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_ - && c.type_implements(field_expr_type, field.typ, field.pos) { - if !field_expr_type.is_ptr() && !field_expr_type.is_pointer() - && !c.inside_unsafe { - field_expr_type_sym := c.table.sym(field_expr_type) - if field_expr_type_sym.kind != .interface_ { - c.mark_as_referenced(mut &node.fields[i].default_expr, - true) - } - } - } else { - c.error('incompatible initializer for field `$field.name`: $err.msg', - field.default_expr.position()) - } - } - // Check for unnecessary inits like ` = 0` and ` = ''` - if field.typ.is_ptr() { - continue - } - if field.default_expr is ast.IntegerLiteral { - if field.default_expr.val == '0' { - c.warn('unnecessary default value of `0`: struct fields are zeroed by default', - field.default_expr.pos) - } - } else if field.default_expr is ast.StringLiteral { - if field.default_expr.val == '' { - c.warn("unnecessary default value of '': struct fields are zeroed by default", - field.default_expr.pos) - } - } else if field.default_expr is ast.BoolLiteral { - if field.default_expr.val == false { - c.warn('unnecessary default value `false`: struct fields are zeroed by default', - field.default_expr.pos) - } - } - } - } - if node.generic_types.len == 0 && has_generic_types { - c.error('generic struct declaration must specify the generic type names, e.g. Foo', - node.pos) - } - } -} - -pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { - if node.typ == ast.void_type { - // Short syntax `({foo: bar})` - if c.expected_type == ast.void_type { - c.error('unexpected short struct syntax', node.pos) - return ast.void_type - } - sym := c.table.sym(c.expected_type) - if sym.kind == .array { - node.typ = c.table.value_type(c.expected_type) - } else { - node.typ = c.expected_type - } - } - struct_sym := c.table.sym(node.typ) - if struct_sym.info is ast.Struct { - if struct_sym.info.generic_types.len > 0 && struct_sym.info.concrete_types.len == 0 - && c.table.cur_concrete_types.len == 0 { - c.error('generic struct init must specify type parameter, e.g. Foo', - node.pos) - } - } else if struct_sym.info is ast.Alias { - parent_sym := c.table.sym(struct_sym.info.parent_type) - // e.g. ´x := MyMapAlias{}´, should be a cast to alias type ´x := MyMapAlias(map[...]...)´ - if parent_sym.kind == .map { - alias_str := c.table.type_to_str(node.typ) - map_str := c.table.type_to_str(struct_sym.info.parent_type) - c.error('direct map alias init is not possible, use `${alias_str}($map_str{})` instead', - node.pos) - return ast.void_type - } - } - // register generic struct type when current fn is generic fn - if c.table.cur_fn.generic_names.len > 0 { - c.table.unwrap_generic_type(node.typ, c.table.cur_fn.generic_names, c.table.cur_concrete_types) - } - c.ensure_type_exists(node.typ, node.pos) or {} - type_sym := c.table.sym(node.typ) - if !c.inside_unsafe && type_sym.kind == .sum_type { - c.note('direct sum type init (`x := SumType{}`) will be removed soon', node.pos) - } - // Make sure the first letter is capital, do not allow e.g. `x := string{}`, - // but `x := T{}` is ok. - if !c.is_builtin_mod && !c.inside_unsafe && type_sym.language == .v - && c.table.cur_concrete_types.len == 0 { - pos := type_sym.name.last_index('.') or { -1 } - first_letter := type_sym.name[pos + 1] - if !first_letter.is_capital() { - c.error('cannot initialize builtin type `$type_sym.name`', node.pos) - } - } - if type_sym.kind == .sum_type && node.fields.len == 1 { - sexpr := node.fields[0].expr.str() - c.error('cast to sum type using `${type_sym.name}($sexpr)` not `$type_sym.name{$sexpr}`', - node.pos) - } - if type_sym.kind == .interface_ && type_sym.language != .js { - c.error('cannot instantiate interface `$type_sym.name`', node.pos) - } - if type_sym.info is ast.Alias { - if type_sym.info.parent_type.is_number() { - c.error('cannot instantiate number type alias `$type_sym.name`', node.pos) - return ast.void_type - } - } - // allow init structs from generic if they're private except the type is from builtin module - if !type_sym.is_public && type_sym.kind != .placeholder && type_sym.language != .c - && (type_sym.mod != c.mod && !(node.typ.has_flag(.generic) && type_sym.mod != 'builtin')) { - c.error('type `$type_sym.name` is private', node.pos) - } - if type_sym.kind == .struct_ { - info := type_sym.info as ast.Struct - if info.attrs.len > 0 && info.attrs[0].name == 'noinit' && type_sym.mod != c.mod { - c.error('struct `$type_sym.name` is declared with a `[noinit]` attribute, so ' + - 'it cannot be initialized with `$type_sym.name{}`', node.pos) - } - } - if type_sym.name.len == 1 && c.table.cur_fn.generic_names.len == 0 { - c.error('unknown struct `$type_sym.name`', node.pos) - return 0 - } - match type_sym.kind { - .placeholder { - c.error('unknown struct: $type_sym.name', node.pos) - return ast.void_type - } - // string & array are also structs but .kind of string/array - .struct_, .string, .array, .alias { - mut info := ast.Struct{} - if type_sym.kind == .alias { - info_t := type_sym.info as ast.Alias - sym := c.table.sym(info_t.parent_type) - if sym.kind == .placeholder { // pending import symbol did not resolve - c.error('unknown struct: $type_sym.name', node.pos) - return ast.void_type - } - if sym.kind == .struct_ { - info = sym.info as ast.Struct - } else { - c.error('alias type name: $sym.name is not struct type', node.pos) - } - } else { - info = type_sym.info as ast.Struct - } - if node.is_short { - exp_len := info.fields.len - got_len := node.fields.len - if exp_len != got_len { - amount := if exp_len < got_len { 'many' } else { 'few' } - c.error('too $amount fields in `$type_sym.name` literal (expecting $exp_len, got $got_len)', - node.pos) - } - } - mut inited_fields := []string{} - for i, mut field in node.fields { - mut field_info := ast.StructField{} - mut field_name := '' - if node.is_short { - if i >= info.fields.len { - // It doesn't make sense to check for fields that don't exist. - // We should just stop here. - break - } - field_info = info.fields[i] - field_name = field_info.name - node.fields[i].name = field_name - } else { - field_name = field.name - mut exists := true - field_info = c.table.find_field_with_embeds(type_sym, field_name) or { - exists = false - ast.StructField{} - } - if !exists { - c.error('unknown field `$field.name` in struct literal of type `$type_sym.name`', - field.pos) - continue - } - if field_name in inited_fields { - c.error('duplicate field name in struct literal: `$field_name`', - field.pos) - continue - } - } - mut expr_type := ast.Type(0) - mut expected_type := ast.Type(0) - inited_fields << field_name - field_type_sym := c.table.sym(field_info.typ) - expected_type = field_info.typ - c.expected_type = expected_type - expr_type = c.expr(field.expr) - if !field_info.typ.has_flag(.optional) { - expr_type = c.check_expr_opt_call(field.expr, expr_type) - } - expr_type_sym := c.table.sym(expr_type) - if field_type_sym.kind == .interface_ { - if c.type_implements(expr_type, field_info.typ, field.pos) { - if !expr_type.is_ptr() && !expr_type.is_pointer() - && expr_type_sym.kind != .interface_ && !c.inside_unsafe { - c.mark_as_referenced(mut &field.expr, true) - } - } - } else if expr_type != ast.void_type && expr_type_sym.kind != .placeholder { - c.check_expected(c.unwrap_generic(expr_type), c.unwrap_generic(field_info.typ)) or { - c.error('cannot assign to field `$field_info.name`: $err.msg', - field.pos) - } - } - if field_info.typ.has_flag(.shared_f) { - if !expr_type.has_flag(.shared_f) && expr_type.is_ptr() { - c.error('`shared` field must be initialized with `shared` or value', - field.pos) - } - } else { - if field_info.typ.is_ptr() && !expr_type.is_ptr() && !expr_type.is_pointer() - && !expr_type.is_number() { - c.error('reference field must be initialized with reference', - field.pos) - } - } - node.fields[i].typ = expr_type - node.fields[i].expected_type = field_info.typ - - if field_info.typ.has_flag(.optional) { - c.error('field `$field_info.name` is optional, but initialization of optional fields currently unsupported', - field.pos) - } - if expr_type.is_ptr() && expected_type.is_ptr() { - if mut field.expr is ast.Ident { - if mut field.expr.obj is ast.Var { - mut obj := unsafe { &field.expr.obj } - if c.fn_scope != voidptr(0) { - obj = c.fn_scope.find_var(obj.name) or { obj } - } - if obj.is_stack_obj && !c.inside_unsafe { - sym := c.table.sym(obj.typ.set_nr_muls(0)) - if !sym.is_heap() && !c.pref.translated { - suggestion := if sym.kind == .struct_ { - 'declaring `$sym.name` as `[heap]`' - } else { - 'wrapping the `$sym.name` object in a `struct` declared as `[heap]`' - } - c.error('`$field.expr.name` cannot be assigned outside `unsafe` blocks as it might refer to an object stored on stack. Consider ${suggestion}.', - field.expr.pos) - } - } - } - } - } - } - // Check uninitialized refs/sum types - for field in info.fields { - if field.has_default_expr || field.name in inited_fields { - continue - } - if field.typ.is_ptr() && !field.typ.has_flag(.shared_f) && !node.has_update_expr - && !c.pref.translated { - c.error('reference field `${type_sym.name}.$field.name` must be initialized', - node.pos) - } - // Do not allow empty uninitialized interfaces - sym := c.table.sym(field.typ) - mut has_noinit := false - for attr in field.attrs { - if attr.name == 'noinit' { - has_noinit = true - break - } - } - if sym.kind == .interface_ && (!has_noinit && sym.language != .js) { - // TODO: should be an error instead, but first `ui` needs updating. - c.note('interface field `${type_sym.name}.$field.name` must be initialized', - node.pos) - } - // Do not allow empty uninitialized sum types - /* - sym := c.table.sym(field.typ) - if sym.kind == .sum_type { - c.warn('sum type field `${type_sym.name}.$field.name` must be initialized', - node.pos) - } - */ - // Check for `[required]` struct attr - if field.attrs.contains('required') && !node.is_short && !node.has_update_expr { - mut found := false - for init_field in node.fields { - if field.name == init_field.name { - found = true - break - } - } - if !found { - c.error('field `${type_sym.name}.$field.name` must be initialized', - node.pos) - } - } - } - } - else {} - } - if node.has_update_expr { - update_type := c.expr(node.update_expr) - node.update_expr_type = update_type - if c.table.type_kind(update_type) != .struct_ { - s := c.table.type_to_str(update_type) - c.error('expected struct, found `$s`', node.update_expr.position()) - } else if update_type != node.typ { - from_sym := c.table.sym(update_type) - to_sym := c.table.sym(node.typ) - from_info := from_sym.info as ast.Struct - to_info := to_sym.info as ast.Struct - // TODO this check is too strict - if !c.check_struct_signature(from_info, to_info) { - c.error('struct `$from_sym.name` is not compatible with struct `$to_sym.name`', - node.update_expr.position()) - } - } - if !node.update_expr.is_lvalue() { - // cgen will repeat `update_expr` for each field - // so enforce an lvalue for efficiency - c.error('expression is not an lvalue', node.update_expr.position()) - } - } - return node.typ -} - fn (mut c Checker) check_div_mod_by_zero(expr ast.Expr, op_kind token.Kind) { match mut expr { ast.FloatLiteral { @@ -2120,194 +1732,6 @@ pub fn (mut c Checker) enum_decl(mut node ast.EnumDecl) { } } -fn (mut c Checker) check_array_init_para_type(para string, expr ast.Expr, pos token.Position) { - 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) 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)) - c.check_expected(default_typ, node.elem_type) or { - c.error(err.msg, default_expr.position()) - } - } - 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.position()) - } - } - 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)) - 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.position()) - } - 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 = c.table.mktyp(typ.deref()) - } else { - elem_type = c.table.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.position()) - } - } - } - 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('expecting `int` for fixed size', node.pos) - } - } - if fixed_size <= 0 { - c.error('fixed size cannot be zero or negative (fixed_size: $fixed_size)', - init_expr.position()) - } - 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 -} - [inline] fn (mut c Checker) check_loop_label(label string, pos token.Position) { if label.len == 0 { @@ -2521,173 +1945,6 @@ fn (mut c Checker) branch_stmt(node ast.BranchStmt) { } } -fn (mut c Checker) for_c_stmt(node ast.ForCStmt) { - c.in_for_count++ - prev_loop_label := c.loop_label - if node.has_init { - c.stmt(node.init) - } - c.expr(node.cond) - if node.has_inc { - c.stmt(node.inc) - } - c.check_loop_label(node.label, node.pos) - c.stmts(node.stmts) - c.loop_label = prev_loop_label - c.in_for_count-- -} - -fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) { - c.in_for_count++ - prev_loop_label := c.loop_label - typ := c.expr(node.cond) - typ_idx := typ.idx() - if node.key_var.len > 0 && node.key_var != '_' { - c.check_valid_snake_case(node.key_var, 'variable name', node.pos) - } - if node.val_var.len > 0 && node.val_var != '_' { - c.check_valid_snake_case(node.val_var, 'variable name', node.pos) - } - if node.is_range { - high_type := c.expr(node.high) - high_type_idx := high_type.idx() - if typ_idx in ast.integer_type_idxs && high_type_idx !in ast.integer_type_idxs { - c.error('range types do not match', node.cond.position()) - } else if typ_idx in ast.float_type_idxs || high_type_idx in ast.float_type_idxs { - c.error('range type can not be float', node.cond.position()) - } else if typ_idx == ast.bool_type_idx || high_type_idx == ast.bool_type_idx { - c.error('range type can not be bool', node.cond.position()) - } else if typ_idx == ast.string_type_idx || high_type_idx == ast.string_type_idx { - c.error('range type can not be string', node.cond.position()) - } - if high_type in [ast.int_type, ast.int_literal_type] { - node.val_type = typ - } else { - node.val_type = high_type - } - node.high_type = high_type - node.scope.update_var_type(node.val_var, node.val_type) - } else { - sym := c.table.final_sym(typ) - if sym.kind == .struct_ { - // iterators - next_fn := sym.find_method_with_generic_parent('next') or { - c.error('a struct must have a `next()` method to be an iterator', node.cond.position()) - return - } - if !next_fn.return_type.has_flag(.optional) { - c.error('iterator method `next()` must return an optional', node.cond.position()) - } - // the receiver - if next_fn.params.len != 1 { - c.error('iterator method `next()` must have 0 parameters', node.cond.position()) - } - mut val_type := next_fn.return_type.clear_flag(.optional) - if node.val_is_mut { - val_type = val_type.ref() - } - node.cond_type = typ - node.kind = sym.kind - node.val_type = val_type - node.scope.update_var_type(node.val_var, val_type) - } else if sym.kind == .string && node.val_is_mut { - c.error('string type is immutable, it cannot be changed', node.pos) - } else { - if sym.kind == .map && !(node.key_var.len > 0 && node.val_var.len > 0) { - c.error( - 'declare a key and a value variable when ranging a map: `for key, val in map {`\n' + - 'use `_` if you do not need the variable', node.pos) - } - if node.key_var.len > 0 { - key_type := match sym.kind { - .map { sym.map_info().key_type } - else { ast.int_type } - } - node.key_type = key_type - node.scope.update_var_type(node.key_var, key_type) - } - mut value_type := c.table.value_type(typ) - if value_type == ast.void_type || typ.has_flag(.optional) { - if typ != ast.void_type { - c.error('for in: cannot index `${c.table.type_to_str(typ)}`', node.cond.position()) - } - } - if node.val_is_mut { - value_type = value_type.ref() - match node.cond { - ast.Ident { - if node.cond.obj is ast.Var { - obj := node.cond.obj as ast.Var - if !obj.is_mut { - c.error('`$obj.name` is immutable, it cannot be changed', - node.cond.pos) - } - } - } - ast.ArrayInit { - c.error('array literal is immutable, it cannot be changed', node.cond.pos) - } - ast.MapInit { - c.error('map literal is immutable, it cannot be changed', node.cond.pos) - } - ast.SelectorExpr { - root_ident := node.cond.root_ident() or { node.cond.expr as ast.Ident } - if !(root_ident.obj as ast.Var).is_mut { - c.error('field `$node.cond.field_name` is immutable, it cannot be changed', - node.cond.pos) - } - } - else {} - } - } - node.cond_type = typ - node.kind = sym.kind - node.val_type = value_type - node.scope.update_var_type(node.val_var, value_type) - } - } - c.check_loop_label(node.label, node.pos) - c.stmts(node.stmts) - c.loop_label = prev_loop_label - c.in_for_count-- -} - -fn (mut c Checker) for_stmt(mut node ast.ForStmt) { - c.in_for_count++ - prev_loop_label := c.loop_label - c.expected_type = ast.bool_type - typ := c.expr(node.cond) - if !node.is_inf && typ.idx() != ast.bool_type_idx && !c.pref.translated { - c.error('non-bool used as for condition', node.pos) - } - if node.cond is ast.InfixExpr { - infix := node.cond - if infix.op == .key_is { - if infix.left in [ast.Ident, ast.SelectorExpr] && infix.right is ast.TypeNode { - is_variable := if mut infix.left is ast.Ident { - infix.left.kind == .variable - } else { - true - } - left_type := c.expr(infix.left) - left_sym := c.table.sym(left_type) - if is_variable { - if left_sym.kind in [.sum_type, .interface_] { - c.smartcast(infix.left, infix.left_type, infix.right.typ, mut - node.scope) - } - } - } - } - } - // TODO: update loop var type - // how does this work currenly? - c.check_loop_label(node.label, node.pos) - c.stmts(node.stmts) - c.loop_label = prev_loop_label - c.in_for_count-- -} - fn (mut c Checker) global_decl(mut node ast.GlobalDecl) { for mut field in node.fields { c.check_valid_snake_case(field.name, 'global name', field.pos) @@ -4518,95 +3775,6 @@ pub fn (mut c Checker) check_dup_keys(node &ast.MapInit, i int) { } } -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 - 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 = c.table.mktyp(c.expr(node.keys[0])) - if node.keys[0].is_auto_deref_var() { - key0_type = key0_type.deref() - } - val0_type = c.table.mktyp(c.expr(node.vals[0])) - if node.vals[0].is_auto_deref_var() { - val0_type = val0_type.deref() - } - } - 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) - if !c.check_types(key_type, key0_type) || (i == 0 && key_type.is_number() - && key0_type.is_number() && key0_type != c.table.mktyp(key_type)) { - msg := c.expected_msg(key_type, key0_type) - c.error('invalid map key: $msg', key.position()) - same_key_type = false - } - if !c.check_types(val_type, val0_type) || (i == 0 && val_type.is_number() - && val0_type.is_number() && val0_type != c.table.mktyp(val_type)) { - msg := c.expected_msg(val_type, val0_type) - c.error('invalid map value: $msg', val.position()) - } - } - 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 -} - // call this *before* calling error or warn pub fn (mut c Checker) add_error_detail(s string) { c.error_details << s diff --git a/vlib/v/checker/containers.v b/vlib/v/checker/containers.v new file mode 100644 index 0000000000..9ad4e84c39 --- /dev/null +++ b/vlib/v/checker/containers.v @@ -0,0 +1,283 @@ +// Copyright (c) 2019-2021 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)) + c.check_expected(default_typ, node.elem_type) or { + c.error(err.msg, default_expr.position()) + } + } + 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.position()) + } + } + 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)) + 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.position()) + } + 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 = c.table.mktyp(typ.deref()) + } else { + elem_type = c.table.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.position()) + } + } + } + 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('expecting `int` for fixed size', node.pos) + } + } + if fixed_size <= 0 { + c.error('fixed size cannot be zero or negative (fixed_size: $fixed_size)', + init_expr.position()) + } + 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.Position) { + 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 + 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 = c.table.mktyp(c.expr(node.keys[0])) + if node.keys[0].is_auto_deref_var() { + key0_type = key0_type.deref() + } + val0_type = c.table.mktyp(c.expr(node.vals[0])) + if node.vals[0].is_auto_deref_var() { + val0_type = val0_type.deref() + } + } + 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) + if !c.check_types(key_type, key0_type) || (i == 0 && key_type.is_number() + && key0_type.is_number() && key0_type != c.table.mktyp(key_type)) { + msg := c.expected_msg(key_type, key0_type) + c.error('invalid map key: $msg', key.position()) + same_key_type = false + } + if !c.check_types(val_type, val0_type) || (i == 0 && val_type.is_number() + && val0_type.is_number() && val0_type != c.table.mktyp(val_type)) { + msg := c.expected_msg(val_type, val0_type) + c.error('invalid map value: $msg', val.position()) + } + } + 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 +} diff --git a/vlib/v/checker/for.v b/vlib/v/checker/for.v new file mode 100644 index 0000000000..293e077218 --- /dev/null +++ b/vlib/v/checker/for.v @@ -0,0 +1,172 @@ +// Copyright (c) 2019-2021 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 + +fn (mut c Checker) for_c_stmt(node ast.ForCStmt) { + c.in_for_count++ + prev_loop_label := c.loop_label + if node.has_init { + c.stmt(node.init) + } + c.expr(node.cond) + if node.has_inc { + c.stmt(node.inc) + } + c.check_loop_label(node.label, node.pos) + c.stmts(node.stmts) + c.loop_label = prev_loop_label + c.in_for_count-- +} + +fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) { + c.in_for_count++ + prev_loop_label := c.loop_label + typ := c.expr(node.cond) + typ_idx := typ.idx() + if node.key_var.len > 0 && node.key_var != '_' { + c.check_valid_snake_case(node.key_var, 'variable name', node.pos) + } + if node.val_var.len > 0 && node.val_var != '_' { + c.check_valid_snake_case(node.val_var, 'variable name', node.pos) + } + if node.is_range { + high_type := c.expr(node.high) + high_type_idx := high_type.idx() + if typ_idx in ast.integer_type_idxs && high_type_idx !in ast.integer_type_idxs { + c.error('range types do not match', node.cond.position()) + } else if typ_idx in ast.float_type_idxs || high_type_idx in ast.float_type_idxs { + c.error('range type can not be float', node.cond.position()) + } else if typ_idx == ast.bool_type_idx || high_type_idx == ast.bool_type_idx { + c.error('range type can not be bool', node.cond.position()) + } else if typ_idx == ast.string_type_idx || high_type_idx == ast.string_type_idx { + c.error('range type can not be string', node.cond.position()) + } + if high_type in [ast.int_type, ast.int_literal_type] { + node.val_type = typ + } else { + node.val_type = high_type + } + node.high_type = high_type + node.scope.update_var_type(node.val_var, node.val_type) + } else { + sym := c.table.final_sym(typ) + if sym.kind == .struct_ { + // iterators + next_fn := sym.find_method_with_generic_parent('next') or { + c.error('a struct must have a `next()` method to be an iterator', node.cond.position()) + return + } + if !next_fn.return_type.has_flag(.optional) { + c.error('iterator method `next()` must return an optional', node.cond.position()) + } + // the receiver + if next_fn.params.len != 1 { + c.error('iterator method `next()` must have 0 parameters', node.cond.position()) + } + mut val_type := next_fn.return_type.clear_flag(.optional) + if node.val_is_mut { + val_type = val_type.ref() + } + node.cond_type = typ + node.kind = sym.kind + node.val_type = val_type + node.scope.update_var_type(node.val_var, val_type) + } else if sym.kind == .string && node.val_is_mut { + c.error('string type is immutable, it cannot be changed', node.pos) + } else { + if sym.kind == .map && !(node.key_var.len > 0 && node.val_var.len > 0) { + c.error( + 'declare a key and a value variable when ranging a map: `for key, val in map {`\n' + + 'use `_` if you do not need the variable', node.pos) + } + if node.key_var.len > 0 { + key_type := match sym.kind { + .map { sym.map_info().key_type } + else { ast.int_type } + } + node.key_type = key_type + node.scope.update_var_type(node.key_var, key_type) + } + mut value_type := c.table.value_type(typ) + if value_type == ast.void_type || typ.has_flag(.optional) { + if typ != ast.void_type { + c.error('for in: cannot index `${c.table.type_to_str(typ)}`', node.cond.position()) + } + } + if node.val_is_mut { + value_type = value_type.ref() + match node.cond { + ast.Ident { + if node.cond.obj is ast.Var { + obj := node.cond.obj as ast.Var + if !obj.is_mut { + c.error('`$obj.name` is immutable, it cannot be changed', + node.cond.pos) + } + } + } + ast.ArrayInit { + c.error('array literal is immutable, it cannot be changed', node.cond.pos) + } + ast.MapInit { + c.error('map literal is immutable, it cannot be changed', node.cond.pos) + } + ast.SelectorExpr { + root_ident := node.cond.root_ident() or { node.cond.expr as ast.Ident } + if !(root_ident.obj as ast.Var).is_mut { + c.error('field `$node.cond.field_name` is immutable, it cannot be changed', + node.cond.pos) + } + } + else {} + } + } + node.cond_type = typ + node.kind = sym.kind + node.val_type = value_type + node.scope.update_var_type(node.val_var, value_type) + } + } + c.check_loop_label(node.label, node.pos) + c.stmts(node.stmts) + c.loop_label = prev_loop_label + c.in_for_count-- +} + +fn (mut c Checker) for_stmt(mut node ast.ForStmt) { + c.in_for_count++ + prev_loop_label := c.loop_label + c.expected_type = ast.bool_type + typ := c.expr(node.cond) + if !node.is_inf && typ.idx() != ast.bool_type_idx && !c.pref.translated { + c.error('non-bool used as for condition', node.pos) + } + if node.cond is ast.InfixExpr { + infix := node.cond + if infix.op == .key_is { + if infix.left in [ast.Ident, ast.SelectorExpr] && infix.right is ast.TypeNode { + is_variable := if mut infix.left is ast.Ident { + infix.left.kind == .variable + } else { + true + } + left_type := c.expr(infix.left) + left_sym := c.table.sym(left_type) + if is_variable { + if left_sym.kind in [.sum_type, .interface_] { + c.smartcast(infix.left, infix.left_type, infix.right.typ, mut + node.scope) + } + } + } + } + } + // TODO: update loop var type + // how does this work currenly? + c.check_loop_label(node.label, node.pos) + c.stmts(node.stmts) + c.loop_label = prev_loop_label + c.in_for_count-- +} diff --git a/vlib/v/checker/struct.v b/vlib/v/checker/struct.v new file mode 100644 index 0000000000..5c2c4e6dd5 --- /dev/null +++ b/vlib/v/checker/struct.v @@ -0,0 +1,393 @@ +// Copyright (c) 2019-2021 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 + +pub fn (mut c Checker) struct_decl(mut node ast.StructDecl) { + if node.language == .v && !c.is_builtin_mod { + c.check_valid_pascal_case(node.name, 'struct name', node.pos) + } + mut struct_sym := c.table.find_type(node.name) or { ast.invalid_type_symbol } + mut has_generic_types := false + if mut struct_sym.info is ast.Struct { + for embed in node.embeds { + if embed.typ.has_flag(.generic) { + has_generic_types = true + } + embed_sym := c.table.sym(embed.typ) + if embed_sym.kind != .struct_ { + c.error('`$embed_sym.name` is not a struct', embed.pos) + } else { + info := embed_sym.info as ast.Struct + if info.is_heap && !embed.typ.is_ptr() { + struct_sym.info.is_heap = true + } + } + } + for attr in node.attrs { + if attr.name == 'typedef' && node.language != .c { + c.error('`typedef` attribute can only be used with C structs', node.pos) + } + } + for i, field in node.fields { + if field.typ == ast.any_type { + c.error('struct field cannot be the `any` type, use generics instead', + field.type_pos) + } + c.ensure_type_exists(field.typ, field.type_pos) or { return } + if field.typ.has_flag(.generic) { + has_generic_types = true + } + if node.language == .v { + c.check_valid_snake_case(field.name, 'field name', field.pos) + } + sym := c.table.sym(field.typ) + for j in 0 .. i { + if field.name == node.fields[j].name { + c.error('field name `$field.name` duplicate', field.pos) + } + } + if sym.kind == .struct_ { + info := sym.info as ast.Struct + if info.is_heap && !field.typ.is_ptr() { + struct_sym.info.is_heap = true + } + } + if field.has_default_expr { + c.expected_type = field.typ + 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_ + && c.type_implements(field_expr_type, field.typ, field.pos) { + if !field_expr_type.is_ptr() && !field_expr_type.is_pointer() + && !c.inside_unsafe { + field_expr_type_sym := c.table.sym(field_expr_type) + if field_expr_type_sym.kind != .interface_ { + c.mark_as_referenced(mut &node.fields[i].default_expr, + true) + } + } + } else { + c.error('incompatible initializer for field `$field.name`: $err.msg', + field.default_expr.position()) + } + } + // Check for unnecessary inits like ` = 0` and ` = ''` + if field.typ.is_ptr() { + continue + } + if field.default_expr is ast.IntegerLiteral { + if field.default_expr.val == '0' { + c.warn('unnecessary default value of `0`: struct fields are zeroed by default', + field.default_expr.pos) + } + } else if field.default_expr is ast.StringLiteral { + if field.default_expr.val == '' { + c.warn("unnecessary default value of '': struct fields are zeroed by default", + field.default_expr.pos) + } + } else if field.default_expr is ast.BoolLiteral { + if field.default_expr.val == false { + c.warn('unnecessary default value `false`: struct fields are zeroed by default', + field.default_expr.pos) + } + } + } + } + if node.generic_types.len == 0 && has_generic_types { + c.error('generic struct declaration must specify the generic type names, e.g. Foo', + node.pos) + } + } +} + +pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { + if node.typ == ast.void_type { + // Short syntax `({foo: bar})` + if c.expected_type == ast.void_type { + c.error('unexpected short struct syntax', node.pos) + return ast.void_type + } + sym := c.table.sym(c.expected_type) + if sym.kind == .array { + node.typ = c.table.value_type(c.expected_type) + } else { + node.typ = c.expected_type + } + } + struct_sym := c.table.sym(node.typ) + if struct_sym.info is ast.Struct { + if struct_sym.info.generic_types.len > 0 && struct_sym.info.concrete_types.len == 0 + && c.table.cur_concrete_types.len == 0 { + c.error('generic struct init must specify type parameter, e.g. Foo', + node.pos) + } + } else if struct_sym.info is ast.Alias { + parent_sym := c.table.sym(struct_sym.info.parent_type) + // e.g. ´x := MyMapAlias{}´, should be a cast to alias type ´x := MyMapAlias(map[...]...)´ + if parent_sym.kind == .map { + alias_str := c.table.type_to_str(node.typ) + map_str := c.table.type_to_str(struct_sym.info.parent_type) + c.error('direct map alias init is not possible, use `${alias_str}($map_str{})` instead', + node.pos) + return ast.void_type + } + } + // register generic struct type when current fn is generic fn + if c.table.cur_fn.generic_names.len > 0 { + c.table.unwrap_generic_type(node.typ, c.table.cur_fn.generic_names, c.table.cur_concrete_types) + } + c.ensure_type_exists(node.typ, node.pos) or {} + type_sym := c.table.sym(node.typ) + if !c.inside_unsafe && type_sym.kind == .sum_type { + c.note('direct sum type init (`x := SumType{}`) will be removed soon', node.pos) + } + // Make sure the first letter is capital, do not allow e.g. `x := string{}`, + // but `x := T{}` is ok. + if !c.is_builtin_mod && !c.inside_unsafe && type_sym.language == .v + && c.table.cur_concrete_types.len == 0 { + pos := type_sym.name.last_index('.') or { -1 } + first_letter := type_sym.name[pos + 1] + if !first_letter.is_capital() { + c.error('cannot initialize builtin type `$type_sym.name`', node.pos) + } + } + if type_sym.kind == .sum_type && node.fields.len == 1 { + sexpr := node.fields[0].expr.str() + c.error('cast to sum type using `${type_sym.name}($sexpr)` not `$type_sym.name{$sexpr}`', + node.pos) + } + if type_sym.kind == .interface_ && type_sym.language != .js { + c.error('cannot instantiate interface `$type_sym.name`', node.pos) + } + if type_sym.info is ast.Alias { + if type_sym.info.parent_type.is_number() { + c.error('cannot instantiate number type alias `$type_sym.name`', node.pos) + return ast.void_type + } + } + // allow init structs from generic if they're private except the type is from builtin module + if !type_sym.is_public && type_sym.kind != .placeholder && type_sym.language != .c + && (type_sym.mod != c.mod && !(node.typ.has_flag(.generic) && type_sym.mod != 'builtin')) { + c.error('type `$type_sym.name` is private', node.pos) + } + if type_sym.kind == .struct_ { + info := type_sym.info as ast.Struct + if info.attrs.len > 0 && info.attrs[0].name == 'noinit' && type_sym.mod != c.mod { + c.error('struct `$type_sym.name` is declared with a `[noinit]` attribute, so ' + + 'it cannot be initialized with `$type_sym.name{}`', node.pos) + } + } + if type_sym.name.len == 1 && c.table.cur_fn.generic_names.len == 0 { + c.error('unknown struct `$type_sym.name`', node.pos) + return 0 + } + match type_sym.kind { + .placeholder { + c.error('unknown struct: $type_sym.name', node.pos) + return ast.void_type + } + // string & array are also structs but .kind of string/array + .struct_, .string, .array, .alias { + mut info := ast.Struct{} + if type_sym.kind == .alias { + info_t := type_sym.info as ast.Alias + sym := c.table.sym(info_t.parent_type) + if sym.kind == .placeholder { // pending import symbol did not resolve + c.error('unknown struct: $type_sym.name', node.pos) + return ast.void_type + } + if sym.kind == .struct_ { + info = sym.info as ast.Struct + } else { + c.error('alias type name: $sym.name is not struct type', node.pos) + } + } else { + info = type_sym.info as ast.Struct + } + if node.is_short { + exp_len := info.fields.len + got_len := node.fields.len + if exp_len != got_len { + amount := if exp_len < got_len { 'many' } else { 'few' } + c.error('too $amount fields in `$type_sym.name` literal (expecting $exp_len, got $got_len)', + node.pos) + } + } + mut inited_fields := []string{} + for i, mut field in node.fields { + mut field_info := ast.StructField{} + mut field_name := '' + if node.is_short { + if i >= info.fields.len { + // It doesn't make sense to check for fields that don't exist. + // We should just stop here. + break + } + field_info = info.fields[i] + field_name = field_info.name + node.fields[i].name = field_name + } else { + field_name = field.name + mut exists := true + field_info = c.table.find_field_with_embeds(type_sym, field_name) or { + exists = false + ast.StructField{} + } + if !exists { + c.error('unknown field `$field.name` in struct literal of type `$type_sym.name`', + field.pos) + continue + } + if field_name in inited_fields { + c.error('duplicate field name in struct literal: `$field_name`', + field.pos) + continue + } + } + mut expr_type := ast.Type(0) + mut expected_type := ast.Type(0) + inited_fields << field_name + field_type_sym := c.table.sym(field_info.typ) + expected_type = field_info.typ + c.expected_type = expected_type + expr_type = c.expr(field.expr) + if !field_info.typ.has_flag(.optional) { + expr_type = c.check_expr_opt_call(field.expr, expr_type) + } + expr_type_sym := c.table.sym(expr_type) + if field_type_sym.kind == .interface_ { + if c.type_implements(expr_type, field_info.typ, field.pos) { + if !expr_type.is_ptr() && !expr_type.is_pointer() + && expr_type_sym.kind != .interface_ && !c.inside_unsafe { + c.mark_as_referenced(mut &field.expr, true) + } + } + } else if expr_type != ast.void_type && expr_type_sym.kind != .placeholder { + c.check_expected(c.unwrap_generic(expr_type), c.unwrap_generic(field_info.typ)) or { + c.error('cannot assign to field `$field_info.name`: $err.msg', + field.pos) + } + } + if field_info.typ.has_flag(.shared_f) { + if !expr_type.has_flag(.shared_f) && expr_type.is_ptr() { + c.error('`shared` field must be initialized with `shared` or value', + field.pos) + } + } else { + if field_info.typ.is_ptr() && !expr_type.is_ptr() && !expr_type.is_pointer() + && !expr_type.is_number() { + c.error('reference field must be initialized with reference', + field.pos) + } + } + node.fields[i].typ = expr_type + node.fields[i].expected_type = field_info.typ + + if field_info.typ.has_flag(.optional) { + c.error('field `$field_info.name` is optional, but initialization of optional fields currently unsupported', + field.pos) + } + if expr_type.is_ptr() && expected_type.is_ptr() { + if mut field.expr is ast.Ident { + if mut field.expr.obj is ast.Var { + mut obj := unsafe { &field.expr.obj } + if c.fn_scope != voidptr(0) { + obj = c.fn_scope.find_var(obj.name) or { obj } + } + if obj.is_stack_obj && !c.inside_unsafe { + sym := c.table.sym(obj.typ.set_nr_muls(0)) + if !sym.is_heap() && !c.pref.translated { + suggestion := if sym.kind == .struct_ { + 'declaring `$sym.name` as `[heap]`' + } else { + 'wrapping the `$sym.name` object in a `struct` declared as `[heap]`' + } + c.error('`$field.expr.name` cannot be assigned outside `unsafe` blocks as it might refer to an object stored on stack. Consider ${suggestion}.', + field.expr.pos) + } + } + } + } + } + } + // Check uninitialized refs/sum types + for field in info.fields { + if field.has_default_expr || field.name in inited_fields { + continue + } + if field.typ.is_ptr() && !field.typ.has_flag(.shared_f) && !node.has_update_expr + && !c.pref.translated { + c.error('reference field `${type_sym.name}.$field.name` must be initialized', + node.pos) + } + // Do not allow empty uninitialized interfaces + sym := c.table.sym(field.typ) + mut has_noinit := false + for attr in field.attrs { + if attr.name == 'noinit' { + has_noinit = true + break + } + } + if sym.kind == .interface_ && (!has_noinit && sym.language != .js) { + // TODO: should be an error instead, but first `ui` needs updating. + c.note('interface field `${type_sym.name}.$field.name` must be initialized', + node.pos) + } + // Do not allow empty uninitialized sum types + /* + sym := c.table.sym(field.typ) + if sym.kind == .sum_type { + c.warn('sum type field `${type_sym.name}.$field.name` must be initialized', + node.pos) + } + */ + // Check for `[required]` struct attr + if field.attrs.contains('required') && !node.is_short && !node.has_update_expr { + mut found := false + for init_field in node.fields { + if field.name == init_field.name { + found = true + break + } + } + if !found { + c.error('field `${type_sym.name}.$field.name` must be initialized', + node.pos) + } + } + } + } + else {} + } + if node.has_update_expr { + update_type := c.expr(node.update_expr) + node.update_expr_type = update_type + if c.table.type_kind(update_type) != .struct_ { + s := c.table.type_to_str(update_type) + c.error('expected struct, found `$s`', node.update_expr.position()) + } else if update_type != node.typ { + from_sym := c.table.sym(update_type) + to_sym := c.table.sym(node.typ) + from_info := from_sym.info as ast.Struct + to_info := to_sym.info as ast.Struct + // TODO this check is too strict + if !c.check_struct_signature(from_info, to_info) { + c.error('struct `$from_sym.name` is not compatible with struct `$to_sym.name`', + node.update_expr.position()) + } + } + if !node.update_expr.is_lvalue() { + // cgen will repeat `update_expr` for each field + // so enforce an lvalue for efficiency + c.error('expression is not an lvalue', node.update_expr.position()) + } + } + return node.typ +}