checker: minor cleanup of struct_init() (#10310)
parent
4089aa38c5
commit
86d70fade7
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue