checker: minor cleanup of struct_init() (#10310)

pull/10341/head
yuyi 2021-06-03 14:48:19 +08:00 committed by GitHub
parent 4089aa38c5
commit 86d70fade7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 54 additions and 54 deletions

View File

@ -670,43 +670,44 @@ fn (mut c Checker) unwrap_generic_struct(struct_type ast.Type, generic_names []s
return struct_type return struct_type
} }
pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type { pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
if struct_init.typ == ast.void_type { if node.typ == ast.void_type {
// Short syntax `({foo: bar})` // Short syntax `({foo: bar})`
if c.expected_type == ast.void_type { if c.expected_type == ast.void_type {
c.error('unexpected short struct syntax', struct_init.pos) c.error('unexpected short struct syntax', node.pos)
return ast.void_type return ast.void_type
} }
sym := c.table.get_type_symbol(c.expected_type) sym := c.table.get_type_symbol(c.expected_type)
if sym.kind == .array { if sym.kind == .array {
struct_init.typ = c.table.value_type(c.expected_type) node.typ = c.table.value_type(c.expected_type)
} else { } else {
struct_init.typ = c.expected_type node.typ = c.expected_type
} }
} }
struct_sym := c.table.get_type_symbol(struct_init.typ) struct_sym := c.table.get_type_symbol(node.typ)
if struct_sym.info is ast.Struct { if struct_sym.info is ast.Struct {
if struct_sym.info.generic_types.len > 0 && struct_sym.info.concrete_types.len == 0 if struct_sym.info.generic_types.len > 0 && struct_sym.info.concrete_types.len == 0
&& c.table.cur_concrete_types.len == 0 { && c.table.cur_concrete_types.len == 0 {
c.error('generic struct init must specify type parameter, e.g. Foo<int>', c.error('generic struct init must specify type parameter, e.g. Foo<int>',
struct_init.pos) node.pos)
} }
} else if struct_sym.info is ast.Alias { } else if struct_sym.info is ast.Alias {
parent_sym := c.table.get_type_symbol(struct_sym.info.parent_type) parent_sym := c.table.get_type_symbol(struct_sym.info.parent_type)
// e.g. ´x := MyMapAlias{}´, should be a cast to alias type ´x := MyMapAlias(map[...]...)´ // e.g. ´x := MyMapAlias{}´, should be a cast to alias type ´x := MyMapAlias(map[...]...)´
if parent_sym.kind == .map { if parent_sym.kind == .map {
alias_str := c.table.type_to_str(struct_init.typ) alias_str := c.table.type_to_str(node.typ)
map_str := c.table.type_to_str(struct_sym.info.parent_type) 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', c.error('direct map alias init is not possible, use `${alias_str}($map_str{})` instead',
struct_init.pos) node.pos)
return ast.void_type return ast.void_type
} }
} }
utyp := c.unwrap_generic_struct(struct_init.typ, c.table.cur_fn.generic_names, c.table.cur_concrete_types) unwrapped_struct_type := c.unwrap_generic_struct(node.typ, c.table.cur_fn.generic_names,
c.ensure_type_exists(utyp, struct_init.pos) or {} c.table.cur_concrete_types)
type_sym := c.table.get_type_symbol(utyp) c.ensure_type_exists(unwrapped_struct_type, node.pos) or {}
type_sym := c.table.get_type_symbol(unwrapped_struct_type)
if !c.inside_unsafe && type_sym.kind == .sum_type { if !c.inside_unsafe && type_sym.kind == .sum_type {
c.note('direct sum type init (`x := SumType{}`) will be removed soon', struct_init.pos) 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{}`, // Make sure the first letter is capital, do not allow e.g. `x := string{}`,
// but `x := T{}` is ok. // but `x := T{}` is ok.
@ -715,43 +716,42 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type {
pos := type_sym.name.last_index('.') or { -1 } pos := type_sym.name.last_index('.') or { -1 }
first_letter := type_sym.name[pos + 1] first_letter := type_sym.name[pos + 1]
if !first_letter.is_capital() { if !first_letter.is_capital() {
c.error('cannot initialize builtin type `$type_sym.name`', struct_init.pos) c.error('cannot initialize builtin type `$type_sym.name`', node.pos)
} }
} }
if type_sym.kind == .sum_type && struct_init.fields.len == 1 { if type_sym.kind == .sum_type && node.fields.len == 1 {
sexpr := struct_init.fields[0].expr.str() sexpr := node.fields[0].expr.str()
c.error('cast to sum type using `${type_sym.name}($sexpr)` not `$type_sym.name{$sexpr}`', c.error('cast to sum type using `${type_sym.name}($sexpr)` not `$type_sym.name{$sexpr}`',
struct_init.pos) node.pos)
} }
if type_sym.kind == .interface_ { if type_sym.kind == .interface_ {
c.error('cannot instantiate interface `$type_sym.name`', struct_init.pos) c.error('cannot instantiate interface `$type_sym.name`', node.pos)
} }
if type_sym.info is ast.Alias { if type_sym.info is ast.Alias {
if type_sym.info.parent_type.is_number() { if type_sym.info.parent_type.is_number() {
c.error('cannot instantiate number type alias `$type_sym.name`', struct_init.pos) c.error('cannot instantiate number type alias `$type_sym.name`', node.pos)
return ast.void_type return ast.void_type
} }
} }
// allow init structs from generic if they're private except the type is from builtin module // 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 if !type_sym.is_public && type_sym.kind != .placeholder && type_sym.language != .c
&& (type_sym.mod != c.mod && !(struct_init.typ.has_flag(.generic) && (type_sym.mod != c.mod && !(node.typ.has_flag(.generic) && type_sym.mod != 'builtin')) {
&& type_sym.mod != 'builtin')) { c.error('type `$type_sym.name` is private', node.pos)
c.error('type `$type_sym.name` is private', struct_init.pos)
} }
if type_sym.kind == .struct_ { if type_sym.kind == .struct_ {
info := type_sym.info as ast.Struct info := type_sym.info as ast.Struct
if info.attrs.len > 0 && info.attrs[0].name == 'noinit' && type_sym.mod != c.mod { 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 ' + c.error('struct `$type_sym.name` is declared with a `[noinit]` attribute, so ' +
'it cannot be initialized with `$type_sym.name{}`', struct_init.pos) 'it cannot be initialized with `$type_sym.name{}`', node.pos)
} }
} }
if type_sym.name.len == 1 && c.table.cur_fn.generic_names.len == 0 { if type_sym.name.len == 1 && c.table.cur_fn.generic_names.len == 0 {
c.error('unknown struct `$type_sym.name`', struct_init.pos) c.error('unknown struct `$type_sym.name`', node.pos)
return 0 return 0
} }
match type_sym.kind { match type_sym.kind {
.placeholder { .placeholder {
c.error('unknown struct: $type_sym.name', struct_init.pos) c.error('unknown struct: $type_sym.name', node.pos)
return ast.void_type return ast.void_type
} }
// string & array are also structs but .kind of string/array // string & array are also structs but .kind of string/array
@ -761,33 +761,33 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type {
info_t := type_sym.info as ast.Alias info_t := type_sym.info as ast.Alias
sym := c.table.get_type_symbol(info_t.parent_type) sym := c.table.get_type_symbol(info_t.parent_type)
if sym.kind == .placeholder { // pending import symbol did not resolve if sym.kind == .placeholder { // pending import symbol did not resolve
c.error('unknown struct: $type_sym.name', struct_init.pos) c.error('unknown struct: $type_sym.name', node.pos)
return ast.void_type return ast.void_type
} }
if sym.kind == .struct_ { if sym.kind == .struct_ {
info = sym.info as ast.Struct info = sym.info as ast.Struct
} else { } else {
c.error('alias type name: $sym.name is not struct type', struct_init.pos) c.error('alias type name: $sym.name is not struct type', node.pos)
} }
} else { } else {
info = type_sym.info as ast.Struct info = type_sym.info as ast.Struct
} }
if struct_init.is_short { if node.is_short {
exp_len := info.fields.len exp_len := info.fields.len
got_len := struct_init.fields.len got_len := node.fields.len
if exp_len != got_len { if exp_len != got_len {
amount := if exp_len < got_len { 'many' } else { 'few' } 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)', c.error('too $amount fields in `$type_sym.name` literal (expecting $exp_len, got $got_len)',
struct_init.pos) node.pos)
} }
} }
mut inited_fields := []string{} mut inited_fields := []string{}
for i, field in struct_init.fields { for i, field in node.fields {
mut info_field := ast.StructField{} mut info_field := ast.StructField{}
mut embed_type := ast.Type(0) mut embed_type := ast.Type(0)
mut is_embed := false mut is_embed := false
mut field_name := '' mut field_name := ''
if struct_init.is_short { if node.is_short {
if i >= info.fields.len { if i >= info.fields.len {
// It doesn't make sense to check for fields that don't exist. // It doesn't make sense to check for fields that don't exist.
// We should just stop here. // We should just stop here.
@ -795,7 +795,7 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type {
} }
info_field = info.fields[i] info_field = info.fields[i]
field_name = info_field.name field_name = info_field.name
struct_init.fields[i].name = field_name node.fields[i].name = field_name
} else { } else {
field_name = field.name field_name = field.name
mut exists := false mut exists := false
@ -841,8 +841,8 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type {
field.pos) field.pos)
} }
} }
struct_init.fields[i].typ = expr_type node.fields[i].typ = expr_type
struct_init.fields[i].expected_type = embed_type node.fields[i].expected_type = embed_type
} else { } else {
inited_fields << field_name inited_fields << field_name
field_type_sym := c.table.get_type_symbol(info_field.typ) field_type_sym := c.table.get_type_symbol(info_field.typ)
@ -873,8 +873,8 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type {
field.pos) field.pos)
} }
} }
struct_init.fields[i].typ = expr_type node.fields[i].typ = expr_type
struct_init.fields[i].expected_type = info_field.typ node.fields[i].expected_type = info_field.typ
} }
if expr_type.is_ptr() && expected_type.is_ptr() { if expr_type.is_ptr() && expected_type.is_ptr() {
if mut field.expr is ast.Ident { if mut field.expr is ast.Ident {
@ -904,23 +904,23 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type {
if field.has_default_expr || field.name in inited_fields { if field.has_default_expr || field.name in inited_fields {
continue continue
} }
if field.typ.is_ptr() && !field.typ.has_flag(.shared_f) if field.typ.is_ptr() && !field.typ.has_flag(.shared_f) && !node.has_update_expr
&& !struct_init.has_update_expr && !c.pref.translated { && !c.pref.translated {
c.error('reference field `${type_sym.name}.$field.name` must be initialized', c.error('reference field `${type_sym.name}.$field.name` must be initialized',
struct_init.pos) node.pos)
} }
// Do not allow empty uninitialized sum types // Do not allow empty uninitialized sum types
/* /*
sym := c.table.get_type_symbol(field.typ) sym := c.table.get_type_symbol(field.typ)
if sym.kind == .sum_type { if sym.kind == .sum_type {
c.warn('sum type field `${type_sym.name}.$field.name` must be initialized', c.warn('sum type field `${type_sym.name}.$field.name` must be initialized',
struct_init.pos) node.pos)
} }
*/ */
// Check for `[required]` struct attr // Check for `[required]` struct attr
if field.attrs.contains('required') && !struct_init.is_short { if field.attrs.contains('required') && !node.is_short {
mut found := false mut found := false
for init_field in struct_init.fields { for init_field in node.fields {
if field.name == init_field.name { if field.name == init_field.name {
found = true found = true
break break
@ -928,37 +928,37 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type {
} }
if !found { if !found {
c.error('field `${type_sym.name}.$field.name` must be initialized', c.error('field `${type_sym.name}.$field.name` must be initialized',
struct_init.pos) node.pos)
} }
} }
} }
} }
else {} else {}
} }
if struct_init.has_update_expr { if node.has_update_expr {
update_type := c.expr(struct_init.update_expr) update_type := c.expr(node.update_expr)
struct_init.update_expr_type = update_type node.update_expr_type = update_type
if c.table.type_kind(update_type) != .struct_ { if c.table.type_kind(update_type) != .struct_ {
s := c.table.type_to_str(update_type) s := c.table.type_to_str(update_type)
c.error('expected struct, found `$s`', struct_init.update_expr.position()) c.error('expected struct, found `$s`', node.update_expr.position())
} else if update_type != struct_init.typ { } else if update_type != node.typ {
from_sym := c.table.get_type_symbol(update_type) from_sym := c.table.get_type_symbol(update_type)
to_sym := c.table.get_type_symbol(struct_init.typ) to_sym := c.table.get_type_symbol(node.typ)
from_info := from_sym.info as ast.Struct from_info := from_sym.info as ast.Struct
to_info := to_sym.info as ast.Struct to_info := to_sym.info as ast.Struct
// TODO this check is too strict // TODO this check is too strict
if !c.check_struct_signature(from_info, to_info) { if !c.check_struct_signature(from_info, to_info) {
c.error('struct `$from_sym.name` is not compatible with struct `$to_sym.name`', c.error('struct `$from_sym.name` is not compatible with struct `$to_sym.name`',
struct_init.update_expr.position()) node.update_expr.position())
} }
} }
if !struct_init.update_expr.is_lvalue() { if !node.update_expr.is_lvalue() {
// cgen will repeat `update_expr` for each field // cgen will repeat `update_expr` for each field
// so enforce an lvalue for efficiency // so enforce an lvalue for efficiency
c.error('expression is not an lvalue', struct_init.update_expr.position()) c.error('expression is not an lvalue', node.update_expr.position())
} }
} }
return utyp return unwrapped_struct_type
} }
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) {