diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index bcde3cbd6..d637af4bc 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -36,7 +36,7 @@ static bool sema_analyse_bitstruct(SemaContext *context, Decl *decl, bool *erase static bool sema_analyse_union_members(SemaContext *context, Decl *decl); static bool sema_analyse_struct_members(SemaContext *context, Decl *decl); static inline bool sema_analyse_struct_member(SemaContext *context, Decl *parent, Decl *decl, bool *erase_decl); -static inline bool sema_check_struct_holes(SemaContext *context, Decl *decl, Decl *member, Type *member_type); +static inline bool sema_check_struct_holes(SemaContext *context, Decl *decl, Decl *member); static inline bool sema_analyse_bitstruct_member(SemaContext *context, Decl *parent, Decl *member, unsigned index, bool allow_overlap, bool *erase_decl); static inline bool sema_analyse_doc_header(SemaContext *context, AstId doc, Decl **params, Decl **extra_params, bool *pure_ref); @@ -260,9 +260,10 @@ static inline bool sema_analyse_struct_member(SemaContext *context, Decl *parent } case DECL_STRUCT: case DECL_UNION: - // Extend the nopadding attributes to substructs. + // Extend the nopadding attributes to nested structs. if (parent->attr_nopadding) decl->attr_nopadding = true; if (parent->attr_compact) decl->attr_compact = true; + FALLTHROUGH; case DECL_BITSTRUCT: decl->is_export = is_export; if (!sema_analyse_decl(context, decl)) return false; @@ -272,9 +273,9 @@ static inline bool sema_analyse_struct_member(SemaContext *context, Decl *parent } } -static inline bool sema_check_struct_holes(SemaContext *context, Decl *decl, Decl *member, Type *member_type) +static inline bool sema_check_struct_holes(SemaContext *context, Decl *decl, Decl *member) { - member_type = type_flatten(member_type); + Type* member_type = type_flatten(member->type); if (!type_is_union_or_strukt(member_type)) return true; assert(decl_is_struct_type(member_type->decl)); if (!member_type->decl->strukt.padded_decl_id) return true; @@ -282,8 +283,16 @@ static inline bool sema_check_struct_holes(SemaContext *context, Decl *decl, Dec if (decl->attr_compact) { SEMA_ERROR(member, "%s has padding and can't be used as the type of '%s', because members of a `@compact` type must all have zero padding.", type_quoted_error_string(member_type), member->name); - SEMA_NOTE(declptr(member_type->decl->strukt.padded_decl_id), "The first padded field in %s is here.", - type_quoted_error_string(member_type)); + Decl* padded_decl = declptr(member_type->decl->strukt.padded_decl_id); + if (decl_is_struct_type(padded_decl)) + { + SEMA_NOTE(padded_decl, "The first padding in %s would be added to the end of this type.", + type_quoted_error_string(member_type)); + } + else + { + SEMA_NOTE(padded_decl, "The first padded field in %s is here.", type_quoted_error_string(member_type)); + } return false; } return true; @@ -337,8 +346,7 @@ static bool sema_analyse_union_members(SemaContext *context, Decl *decl) AlignSize member_alignment; if (!sema_set_abi_alignment(context, member->type, &member_alignment)) return false; - Type *member_type = type_flatten(member->type); - if (!sema_check_struct_holes(context, decl, member, member_type)) return false; + if (!sema_check_struct_holes(context, decl, member)) return false; ByteSize member_size = type_size(member->type); assert(member_size <= MAX_TYPE_SIZE); @@ -595,11 +603,11 @@ static bool sema_analyse_struct_members(SemaContext *context, Decl *decl) if (!decl->strukt.padded_decl_id) decl->strukt.padded_decl_id = declid(member); if (decl->attr_nopadding || member->attr_nopadding) { - RETURN_SEMA_ERROR(member, "%d bytes of padding would be added to align this member.", align_offset - offset); + RETURN_SEMA_ERROR(member, "%d bytes of padding would be added to align this member which is not allowed with `@nopadding` and `@compact`.", align_offset - offset); } } - if (!sema_check_struct_holes(context, decl, member, member_type)) return false; + if (!sema_check_struct_holes(context, decl, member)) return false; offset = align_offset; member->offset = offset; @@ -641,9 +649,9 @@ static bool sema_analyse_struct_members(SemaContext *context, Decl *decl) { Decl *first_member = struct_members[0]; Type *type = type_flatten(first_member->type); - if (type->type_kind == TYPE_STRUCT && !type->decl->attr_nopadding) + if (type_is_union_or_strukt(type) && !type->decl->attr_nopadding) { - RETURN_SEMA_ERROR(first_member, "Inlined struct requires @nopadding attribute."); + RETURN_SEMA_ERROR(first_member, "An inlined struct or union type also requires the `@nopadding` attribute."); } } diff --git a/test/test_suite/struct/struct_nopadding_compact.c3t b/test/test_suite/struct/struct_nopadding_compact.c3t new file mode 100644 index 000000000..e96d58244 --- /dev/null +++ b/test/test_suite/struct/struct_nopadding_compact.c3t @@ -0,0 +1,272 @@ +module struct2; + +struct Foo1 +{ + bool a @nopadding; + int b; + int c @nopadding; +} + +union Foo1_Union +{ + bool a @nopadding; + int b; + int c @nopadding; +} + +struct Foo2 @nopadding +{ + bool a; + bool b; + bool c; +} + +union Foo2_Union @nopadding +{ + bool a; + bool b; + bool c; +} + +struct Foo3 +{ + bool a; + int b @nopadding; // #error: 3 bytes of padding would be added + int c @nopadding; +} + +struct Foo4 @nopadding +{ + bool a; + int b; // #error: 3 bytes of padding would be added + int c; +} + +struct Foo5 @nopadding // #error: 3 bytes of padding would be added +{ + int a; + bool b; +} + +struct Foo5_B @align(16) @nopadding // #error: 8 bytes of padding would be added +{ + int a; + int b; +} + +struct Foo6 @nopadding +{ + int a; + struct // #error: 3 bytes of padding would be added + { + int b; + bool c; + } +} + +struct Foo7 @nopadding +{ + int a; + struct nested + { + bool b; + int c; // #error: 3 bytes of padding would be added + } +} + +struct Foo7_A @nopadding +{ + int a; + struct + { + bool b; + union // #error: 7 bytes of padding would be added + { + int c; + long d; + } + } +} + +struct Foo7_B @nopadding +{ + int a; + char[4] _pad; + struct + { + long b; + union + { + int c; + long d; + struct // #error: 3 bytes of padding would be added + { + int e; + bool f; + } + } + } +} + +struct Foo7_C @nopadding +{ + int a; + char[4] _pad; + struct + { + int b @align(8); + union // #error: 4 bytes of padding would be added + { + int c; + long d; + struct + { + bool f; + } + } + } +} + +struct Foo10 @nopadding +{ + inline Foo2 a; +} + +struct Foo11 @nopadding +{ + inline Foo1 a; // #error: An inlined struct or union type also requires the `@nopadding` attribute +} + +struct Foo11_A @nopadding +{ + inline Foo1_Union a; // #error: An inlined struct or union type also requires the `@nopadding` attribute +} + +union Foo12 @nopadding +{ + int b; + bool a; + long c; + struct + { + bool d; + int e; // #error: 3 bytes of padding would be added + } +} + +union Foo12_A @nopadding +{ + int b; + bool a; + long c; +} + +struct Foo13 @nopadding +{ + inline Foo12_A a; + short b @align(32); // #error: 24 bytes of padding would be added +} + +struct Foo14 @compact // #error: 2 bytes of padding would be added to the end +{ + Foo10 a @align(4); + char[1] _pad; + short b; +} + +struct Foo1_Compact +{ + bool a @compact; // #error: '@compact' is not a valid member attribute + int b; + int c @compact; +} + +struct Foo2_Compact @compact +{ + bool a; + bool b; + bool c; +} + +struct Foo4_Compact @compact +{ + bool a; + int b; // #error: 3 bytes of padding would be added + int c; +} + +struct Foo5_Compact @compact // #error: 3 bytes of padding would be added +{ + int a; + bool b; +} + +struct Foo6_Compact @compact +{ + int a; + struct nested // #error: 3 bytes of padding would be added + { + int a; + bool b; + } +} + +struct Foo7_Compact @compact +{ + int a; + struct nested + { + bool b; + int a; // #error: 3 bytes of padding would be added + } +} + +struct Foo8 @compact +{ + Foo8_A a; // #error: 'Foo8_A' has padding +} + +struct Foo8_A +{ + long a; + Foo8_B b; +} + +struct Foo8_B +{ + int a; + char[4] _pad; + struct + { + long b; + union + { + int c; + long d; + struct + { + int e; + Foo8_C f; + } + } + } +} + +struct Foo8_C +{ + bool a; + int b; +} + +struct Foo9 @compact +{ + Foo10 a; + char[13] _pad; + Foo9_A b; // #error: 'Foo9_A' has padding +} + +struct Foo9_A @align(16) +{ + int a; + int b; +}