checker: extract containers.v, struct.v, for.v from checker.v (#13012)
							parent
							
								
									41078bc438
								
							
						
					
					
						commit
						64f1ea6fe9
					
				|  | @ -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<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) { | ||||
| 	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 | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
|  | @ -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-- | ||||
| } | ||||
|  | @ -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 | ||||
| } | ||||
		Loading…
	
		Reference in New Issue