Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions regression/goto-gcc/ms_extensions_anonymous_member/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// -fms-extensions: a *tagged* struct/union used as an unnamed (anonymous)
// member. Its members are injected into the enclosing struct and it
// contributes its size. Used pervasively in the Linux kernel (e.g.
// struct __filename_head embedded in struct filename, whose static_asserts
// require the size to be exact). The _Static_asserts here are checked at
// conversion time, so this fails to compile unless the anonymous tagged
// member is laid out correctly. Member types are deliberately fixed-width
// (no pointers, no long) so the expected sizes are identical on 32-bit and
// 64-bit targets.
struct head
{
int name;
int refcnt;
int aname;
};

struct filename
{
struct head; // anonymous tagged-struct member
char iname[20];
};

_Static_assert(sizeof(struct head) == 12, "head size");
_Static_assert(sizeof(struct filename) == 32, "filename size");
_Static_assert(
__builtin_offsetof(struct filename, iname) == 12,
"iname offset");

// a tagged union as an anonymous member, too
struct u_outer
{
union inner
{
int i;
char c[4];
};
int tail;
};
_Static_assert(sizeof(struct u_outer) == 8, "union-anon size");

int main(void)
{
struct filename f;
f.refcnt = 7; // inner field reachable through the anonymous member
f.name = 0;

struct u_outer u;
u.i = 3; // inner union field reachable

return f.refcnt + u.i;
}
17 changes: 17 additions & 0 deletions regression/goto-gcc/ms_extensions_anonymous_member/test.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
CORE
main.c
-fms-extensions -c -o main.gb
^EXIT=0$
^SIGNAL=0$
--
^warning: ignoring
^CONVERSION ERROR$
--
With -fms-extensions, goto-cc must accept a *tagged* struct/union as an
anonymous member: it contributes its size and injects its members. The
_Static_asserts (checked at conversion) require exact layout, and the
field accesses require member injection. This is the pattern used by the
Linux kernel's struct filename (struct __filename_head). Member sizes are
fixed-width so the layout asserts hold on both 32-bit and 64-bit targets.
Without -fms-extensions the member is ignored (standard GCC), which is
covered by regression/ansi-c/anonymous_member*.
52 changes: 26 additions & 26 deletions src/ansi-c/c_typecheck_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -986,32 +986,32 @@ void c_typecheck_baset::typecheck_compound_body(
}
else
{
// GCC and Clang ignore anything other than an untagged struct or
// union; we could print a warning, but there isn't any ambiguity in
// semantics here. Printing a warning could elevate this to an error
// when compiling code with goto-cc with -Werror.
// Note that our type checking always creates a struct_tag/union_tag
// type, but only named struct/union types have an ID_tag member.
if(
new_component.type().id() == ID_struct_tag &&
follow_tag(to_struct_tag_type(new_component.type()))
.find(ID_tag)
.is_nil())
{
// ok, anonymous struct
}
else if(
new_component.type().id() == ID_union_tag &&
follow_tag(to_union_tag_type(new_component.type()))
.find(ID_tag)
.is_nil())
{
// ok, anonymous union
}
else
{
continue;
}
// C11 allows an *untagged* struct/union as an anonymous member.
// -fms-extensions (and MSVC) additionally allow a *tagged*
// struct/union as an anonymous member, injecting its members
// and contributing its size -- used throughout the Linux kernel
// (e.g. `struct __filename_head;` embedded in struct filename).
// Without -fms-extensions, GCC/Clang ignore such a member (it
// contributes no size); we preserve that. A non-struct/union
// unnamed member (e.g. a bare `int;`) is always ignored.
const bool is_struct_tag =
new_component.type().id() == ID_struct_tag;
const bool is_union_tag = new_component.type().id() == ID_union_tag;

bool is_untagged = false;
if(is_struct_tag)
is_untagged = follow_tag(to_struct_tag_type(new_component.type()))
.find(ID_tag)
.is_nil();
else if(is_union_tag)
is_untagged = follow_tag(to_union_tag_type(new_component.type()))
.find(ID_tag)
.is_nil();

if(!is_struct_tag && !is_union_tag)
continue; // not a struct/union: ignore
if(!is_untagged && !config.ansi_c.ms_extensions)
continue; // tagged anonymous member needs -fms-extensions
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/goto-cc/gcc_mode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,14 @@ int gcc_modet::doit()
if(cmdline.isset("-fsingle-precision-constant"))
config.ansi_c.single_precision_constant=true;

// -fms-extensions enables MS/GCC extensions such as anonymous members
// that are a *tagged* struct/union type (used throughout the Linux
// kernel, e.g. struct __filename_head embedded in struct filename).
// Note: goto_cc_cmdlinet stores long options without the leading '-',
// so we query "fms-extensions" (cf. the "fshort-double" check below).
if(cmdline.isset("fms-extensions"))
config.ansi_c.ms_extensions = true;
Comment on lines +587 to +593
Comment on lines +592 to +593

// -fshort-double makes double the same as float
if(cmdline.isset("fshort-double"))
config.ansi_c.double_width=config.ansi_c.single_width;
Expand Down
1 change: 1 addition & 0 deletions src/util/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,7 @@ bool configt::set(const cmdlinet &cmdline)
cpp.cpp_standard=cppt::default_cpp_standard();

ansi_c.single_precision_constant=false;
ansi_c.ms_extensions = false;
ansi_c.for_has_scope=true; // C99 or later
ansi_c.ts_18661_3_Floatn_types=false;
ansi_c.__float128_is_keyword = false;
Expand Down
2 changes: 2 additions & 0 deletions src/util/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ class configt
bool bf16_type; // __bf16 (Clang >= 15, GCC >= 13)
bool fp16_type; // __fp16 (GCC >= 4.5 on ARM, Clang >= 6)
bool single_precision_constant;
bool ms_extensions = false; // -fms-extensions: MS/GCC anonymous tagged
// struct/union members, etc.
enum class c_standardt
{
C89,
Expand Down
Loading