Skip to content

zero size structs with extern (C) attribute #3093

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
17 changes: 10 additions & 7 deletions spec/struct.dd
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,9 @@ $(H2 $(LNAME2 struct_layout, Struct Layout))
the first field and the start of the object.
)

$(P Structs with no fields of non-zero size (aka $(I Empty Structs)) have a size of one byte.)
$(P A struct with no fields of non-zero size is an $(I Empty Struct).
An Empty Struct with a $(DDSUBLINK spec/attribute, linkage, Linkage Attribute) of `extern (C)` has a size of zero.
Otherwise, its size is one byte.)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong. I think you wanted to say that empty extern (C) struct size is the same as with the underlying C compiler. Also you might want to add what happens if the C compiler simply disallows empty structs?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C11 6.7.2.1-8 says "If the struct-declaration-list does not contain any named members, either directly or via an anonymous structure or anonymous union, the behavior is undefined."

Undefined behavior is not disallowed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we encounter a C compiler with different behavior, we'll adjust the spec.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I recall, the problem being that gcc and clang don't agree with each other on the same platform. So you could choose to imitate gcc on Linux, but then you're abi incompatible with clang on Linux - which someone might instead choose to use.

But that's all by the by.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Undefined behavior is not disallowed.

The point is, undefined behaviour allows refusal to compile. A reasonable C compiler could emit an error when encountering an empty struct.

If we encounter a C compiler with different behavior, we'll adjust the spec.

Better, but why not do it right away?


$(P Non-static $(RELATIVE_LINK2 nested, function-nested D structs), which access the context of
their enclosing scope, have an extra field.
Expand All @@ -179,20 +181,22 @@ $(H2 $(LNAME2 struct_layout, Struct Layout))
$(OL
$(LI The default layout of the fields of a struct is an exact
match with the $(I associated C compiler).)
$(LI g++ and clang++ differ in how empty structs are handled. Both return `1` from `sizeof`,
$(LI g++ and clang++ differ in how Empty Structs are handled. Both return `1` from `sizeof`,
however, clang++ does not push them onto the parameter stack while g++ does. This is a
binary incompatibility between g++ and clang++.
dmd follows clang++ behavior for OSX and FreeBSD, and g++ behavior for Linux and other
Posix platforms.
)
$(LI clang and gcc both return `0` from `sizeof` for empty structs. Using `extern "C++"`
in clang++ and g++ does not cause them to conform to the behavior of their respective C compilers.)
$(LI C compilers normally evaluate `sizeof` applied to Empty Structs as `0`, although it
is undefined behavior according to the C11 6.7.2.1-8 Standard.
C++ compilers normally evaluate the size as `1`, even if `extern "C"` is applied.
)
))

$(UNDEFINED_BEHAVIOR
$(OL
$(LI The padding data can be accessed, but its contents are undefined.)
$(LI Do not pass or return structs with no fields of non-zero size to `extern (C)` functions.
$(LI Do not pass or return Empty Structs to `extern (C)` functions.
According to C11 6.7.2.1p8 this is undefined behavior.)
))

Expand All @@ -202,8 +206,7 @@ $(H2 $(LNAME2 struct_layout, Struct Layout))
attributes to describe an exact match. Using a $(DDSUBLINK spec/version, static-assert, Static Assert)
to ensure the result is as expected.)
$(LI Although the contents of the padding are often zero, do not rely on that.)
$(LI Avoid using empty structs when interfacing with C and C++ code.)
$(LI Avoid using empty structs as parameters or arguments to variadic functions.)
$(LI Avoid using Empty Structs. Add a one byte dummy field to those structs.)
))

$(H2 $(LNAME2 POD, Plain Old Data))
Expand Down