checker: cleanup and simplify struct processing p2, extend test (#21025)

This commit is contained in:
Turiiya 2024-03-15 08:08:46 +01:00 committed by GitHub
parent 6a42f79ddc
commit cb595293d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 59 additions and 89 deletions

View File

@ -18,17 +18,14 @@ fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
c.check_valid_pascal_case(node.name, 'struct name', node.pos) c.check_valid_pascal_case(node.name, 'struct name', node.pos)
} }
for embed in node.embeds { for embed in node.embeds {
if embed.typ.has_flag(.generic) {
has_generic_types = true
}
embed_sym := c.table.sym(embed.typ) embed_sym := c.table.sym(embed.typ)
if embed_sym.kind != .struct_ { if embed_sym.kind != .struct_ {
c.error('`${embed_sym.name}` is not a struct', embed.pos) c.error('`${embed_sym.name}` is not a struct', embed.pos)
} else { } else if (embed_sym.info as ast.Struct).is_heap && !embed.typ.is_ptr() {
info := embed_sym.info as ast.Struct struct_sym.info.is_heap = true
if info.is_heap && !embed.typ.is_ptr() { }
struct_sym.info.is_heap = true if embed.typ.has_flag(.generic) {
} has_generic_types = true
} }
// Ensure each generic type of the embed was declared in the struct's definition // Ensure each generic type of the embed was declared in the struct's definition
if node.generic_types.len > 0 && embed.typ.has_flag(.generic) { if node.generic_types.len > 0 && embed.typ.has_flag(.generic) {
@ -90,12 +87,10 @@ fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
if !c.is_builtin_mod && node.language == .v { if !c.is_builtin_mod && node.language == .v {
if !(c.file.is_translated || c.pref.translated) { if !(c.file.is_translated || c.pref.translated) {
sym := c.table.sym(field.typ) sym := c.table.sym(field.typ)
if sym.kind == .function { if sym.kind == .function && !field.typ.has_flag(.option)
if !field.typ.has_flag(.option) && !field.has_default_expr && !field.has_default_expr && !field.attrs.contains('required') {
&& field.attrs.all(it.name != 'required') { error_msg := 'uninitialized `fn` struct fields are not allowed, since they can result in segfaults; use `?fn` or `[required]` or initialize the field with `=` (if you absolutely want to have unsafe function pointers, use `= unsafe { nil }`)'
error_msg := 'uninitialized `fn` struct fields are not allowed, since they can result in segfaults; use `?fn` or `[required]` or initialize the field with `=` (if you absolutely want to have unsafe function pointers, use `= unsafe { nil }`)' c.note(error_msg, field.pos)
c.note(error_msg, field.pos)
}
} }
} }
} }
@ -317,18 +312,15 @@ fn minify_sort_fn(a &ast.StructField, b &ast.StructField) int {
// TODO: support enums with custom field values as well // TODO: support enums with custom field values as well
if a_sym.info is ast.Enum { if a_sym.info is ast.Enum {
if !a_sym.info.is_flag && !a_sym.info.uses_exprs { if !a_sym.info.is_flag && !a_sym.info.uses_exprs {
if b_sym.kind == .enum_ { return if b_sym.info is ast.Enum {
a_nr_vals := (a_sym.info as ast.Enum).vals.len match true {
b_nr_vals := (b_sym.info as ast.Enum).vals.len a_sym.info.vals.len > b_sym.info.vals.len { -1 }
return if a_nr_vals > b_nr_vals { a_sym.info.vals.len < b_sym.info.vals.len { 1 }
-1 else { 0 }
} else if a_nr_vals < b_nr_vals {
1
} else {
0
} }
} else {
1
} }
return 1
} }
} else if b_sym.info is ast.Enum { } else if b_sym.info is ast.Enum {
if !b_sym.info.is_flag && !b_sym.info.uses_exprs { if !b_sym.info.is_flag && !b_sym.info.uses_exprs {
@ -338,16 +330,12 @@ fn minify_sort_fn(a &ast.StructField, b &ast.StructField) int {
a_size, a_align := t.type_size(a.typ) a_size, a_align := t.type_size(a.typ)
b_size, b_align := t.type_size(b.typ) b_size, b_align := t.type_size(b.typ)
return if a_align > b_align { return match true {
-1 a_align > b_align { -1 }
} else if a_align < b_align { a_align < b_align { 1 }
1 a_size > b_size { -1 }
} else if a_size > b_size { a_size < b_size { 1 }
-1 else { 0 }
} else if a_size < b_size {
1
} else {
0
} }
} }
@ -454,8 +442,8 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini
&& c.table.cur_concrete_types.len == 0 { && c.table.cur_concrete_types.len == 0 {
pos := type_sym.name.index_u8_last(`.`) pos := type_sym.name.index_u8_last(`.`)
first_letter := type_sym.name[pos + 1] first_letter := type_sym.name[pos + 1]
if !first_letter.is_capital() if !first_letter.is_capital() && (type_sym.kind != .struct_
&& (type_sym.kind != .struct_ || !(type_sym.info as ast.Struct).is_anon) || !(type_sym.info is ast.Struct && type_sym.info.is_anon))
&& type_sym.kind != .placeholder { && type_sym.kind != .placeholder {
c.error('cannot initialize builtin type `${type_sym.name}`', node.pos) c.error('cannot initialize builtin type `${type_sym.name}`', node.pos)
} }
@ -643,8 +631,7 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini
init_field.expr.pos()) init_field.expr.pos())
} }
if exp_type_sym.kind == .array && got_type_sym.kind == .array { if exp_type_sym.kind == .array && got_type_sym.kind == .array {
if init_field.expr is ast.IndexExpr if init_field.expr is ast.IndexExpr && init_field.expr.left is ast.Ident
&& (init_field.expr as ast.IndexExpr).left is ast.Ident
&& ((init_field.expr as ast.IndexExpr).left.is_mut() && ((init_field.expr as ast.IndexExpr).left.is_mut()
|| field_info.is_mut) && init_field.expr.index is ast.RangeExpr || field_info.is_mut) && init_field.expr.index is ast.RangeExpr
&& !c.inside_unsafe { && !c.inside_unsafe {
@ -667,11 +654,10 @@ or use an explicit `unsafe{ a[..] }`, if you do not want a copy of the slice.',
} }
} }
if exp_type_sym.kind == .interface_ { if exp_type_sym.kind == .interface_ {
if c.type_implements(got_type, exp_type, init_field.pos) { if got_type_sym.kind != .interface_ && !c.inside_unsafe
if !c.inside_unsafe && got_type_sym.kind != .interface_ && !got_type.is_any_kind_of_pointer()
&& !got_type.is_any_kind_of_pointer() { && c.type_implements(got_type, exp_type, init_field.pos) {
c.mark_as_referenced(mut &init_field.expr, true) c.mark_as_referenced(mut &init_field.expr, true)
}
} }
} else if got_type != ast.void_type && got_type_sym.kind != .placeholder } else if got_type != ast.void_type && got_type_sym.kind != .placeholder
&& !exp_type.has_flag(.generic) { && !exp_type.has_flag(.generic) {
@ -686,17 +672,13 @@ or use an explicit `unsafe{ a[..] }`, if you do not want a copy of the slice.',
init_field.pos) init_field.pos)
} }
} else { } else {
is_unsafe_0 := init_field.expr is ast.UnsafeExpr if !c.inside_unsafe && type_sym.language == .v && !(c.file.is_translated
&& (init_field.expr as ast.UnsafeExpr).expr.str() == '0' || c.pref.translated) && exp_type.is_ptr()
if exp_type.is_ptr() && !is_unsafe_0 && !got_type.is_any_kind_of_pointer() && !got_type.is_any_kind_of_pointer() && !exp_type.has_flag(.option)
&& !exp_type.has_flag(.option) { && !(init_field.expr is ast.UnsafeExpr && init_field.expr.expr.str() == '0') {
if init_field.expr.str() == '0' { if init_field.expr.str() == '0' {
if !c.inside_unsafe && type_sym.language == .v { c.note('assigning `0` to a reference field is only allowed in `unsafe` blocks',
if !(c.file.is_translated || c.pref.translated) { init_field.pos)
c.note('assigning `0` to a reference field is only allowed in `unsafe` blocks',
init_field.pos)
}
}
} else { } else {
c.error('reference field must be initialized with reference', c.error('reference field must be initialized with reference',
init_field.pos) init_field.pos)
@ -713,27 +695,19 @@ or use an explicit `unsafe{ a[..] }`, if you do not want a copy of the slice.',
node.init_fields[i].typ = got_type node.init_fields[i].typ = got_type
node.init_fields[i].expected_type = exp_type node.init_fields[i].expected_type = exp_type
if got_type.is_ptr() && exp_type.is_ptr() { if got_type.is_ptr() && exp_type.is_ptr() && mut init_field.expr is ast.Ident {
if mut init_field.expr is ast.Ident { c.fail_if_stack_struct_action_outside_unsafe(mut init_field.expr,
c.fail_if_stack_struct_action_outside_unsafe(mut init_field.expr, 'assigned')
'assigned')
}
} }
if field_info.typ in ast.unsigned_integer_type_idxs { if field_info.typ in ast.unsigned_integer_type_idxs
if mut init_field.expr is ast.IntegerLiteral { && mut init_field.expr is ast.IntegerLiteral
if init_field.expr.val[0] == `-` { && (init_field.expr as ast.IntegerLiteral).val[0] == `-` {
c.error('cannot assign negative value to unsigned integer type', c.error('cannot assign negative value to unsigned integer type', init_field.expr.pos)
init_field.expr.pos)
}
}
} }
if exp_type_sym.kind == .struct_ && !(exp_type_sym.info as ast.Struct).is_anon if exp_type_sym.info is ast.Struct && !exp_type_sym.info.is_anon
&& mut init_field.expr is ast.StructInit { && mut init_field.expr is ast.StructInit && init_field.expr.is_anon {
if init_field.expr.is_anon { c.error('cannot assign anonymous `struct` to a typed `struct`', init_field.expr.pos)
c.error('cannot assign anonymous `struct` to a typed `struct`',
init_field.expr.pos)
}
} }
// all the fields of initialized embedded struct are ignored, they are considered initialized // all the fields of initialized embedded struct are ignored, they are considered initialized
@ -840,22 +814,13 @@ or use an explicit `unsafe{ a[..] }`, if you do not want a copy of the slice.',
} }
*/ */
// Check for `[required]` struct attr // Check for `[required]` struct attr
if !node.no_keys && !node.has_update_expr && field.attrs.contains('required') { if !node.no_keys && !node.has_update_expr && field.attrs.contains('required')
mut found := false && node.init_fields.all(it.name != field.name) {
for init_field in node.init_fields { c.error('field `${type_sym.name}.${field.name}` must be initialized',
if field.name == init_field.name { node.pos)
found = true
break
}
}
if !found {
c.error('field `${type_sym.name}.${field.name}` must be initialized',
node.pos)
}
} }
if !node.has_update_expr && !field.has_default_expr && field.name !in inited_fields if !node.has_update_expr && !field.has_default_expr && !field.typ.is_ptr()
&& !field.typ.is_ptr() && !field.typ.has_flag(.option) && !field.typ.has_flag(.option) && c.table.final_sym(field.typ).kind == .struct_ {
&& c.table.final_sym(field.typ).kind == .struct_ {
mut zero_struct_init := ast.StructInit{ mut zero_struct_init := ast.StructInit{
pos: node.pos pos: node.pos
typ: field.typ typ: field.typ
@ -945,7 +910,7 @@ fn (mut c Checker) check_ref_fields_initialized(struct_sym &ast.TypeSymbol, mut
if sym.language == .c && sym.info.is_typedef { if sym.language == .c && sym.info.is_typedef {
continue continue
} }
if field.name.is_capital() && sym.language == .v { if field.name.len > 0 && field.name[0].is_capital() && sym.language == .v {
// an embedded struct field // an embedded struct field
continue continue
} }
@ -988,7 +953,7 @@ fn (mut c Checker) check_ref_fields_initialized_note(struct_sym &ast.TypeSymbol,
if sym.language == .c && sym.info.is_typedef { if sym.language == .c && sym.info.is_typedef {
continue continue
} }
if field.name.is_capital() && sym.language == .v { if field.name.len > 0 && field.name[0].is_capital() && sym.language == .v {
// an embedded struct field // an embedded struct field
continue continue
} }

View File

@ -11,4 +11,4 @@ vlib/v/checker/tests/struct_ref_fields_init_0_err.vv:8:3: notice: assigning `0`
8 | foo: 0 8 | foo: 0
| ~~~~~~ | ~~~~~~
9 | } 9 | }
10 | } 10 |

View File

@ -7,4 +7,9 @@ fn main() {
_ = Foo{ _ = Foo{
foo: 0 foo: 0
} }
_ = Foo{unsafe { 0 }}
_ = Foo{
foo: unsafe { 0 }
}
} }