checker: extract containers.v, struct.v, for.v from checker.v (#13012)

pull/13251/head
yuyi 2022-01-02 21:52:19 +08:00 committed by GitHub
parent 41078bc438
commit 64f1ea6fe9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 848 additions and 832 deletions

View File

@ -514,394 +514,6 @@ pub fn (mut c Checker) expand_iface_embeds(idecl &ast.InterfaceDecl, level int,
return ares 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<T>',
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<int>',
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) { fn (mut c Checker) check_div_mod_by_zero(expr ast.Expr, op_kind token.Kind) {
match mut expr { match mut expr {
ast.FloatLiteral { 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] [inline]
fn (mut c Checker) check_loop_label(label string, pos token.Position) { fn (mut c Checker) check_loop_label(label string, pos token.Position) {
if label.len == 0 { 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) { fn (mut c Checker) global_decl(mut node ast.GlobalDecl) {
for mut field in node.fields { for mut field in node.fields {
c.check_valid_snake_case(field.name, 'global name', field.pos) 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 // call this *before* calling error or warn
pub fn (mut c Checker) add_error_detail(s string) { pub fn (mut c Checker) add_error_detail(s string) {
c.error_details << s c.error_details << s

View File

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

View File

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

View File

@ -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<T>',
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<int>',
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
}