Add tests and improvements for @nopadding and @compact.

This commit is contained in:
Christian Buttner
2024-07-16 13:17:57 +02:00
committed by Christoffer Lerno
parent a4a1a42842
commit 8285720180
2 changed files with 292 additions and 12 deletions

View File

@@ -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.");
}
}

View File

@@ -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;
}