On 4/10/25 20:40, James Kuyper wrote:
...
"Two declarations of structure, union, or enumerated types which ... use
different tags declare distinct types. Each declaration of a structure,
union, or enumerated type which does not include a tag declares a
distinct type." (6.7.2.3p9).
...
I get the impression that the reason why the rules for compatible
struct, union, and enumerated types were defined the way they are might
not be obvious.
In C, within a single translation unit, there are three ways to define a
struct, union, or enumeration exactly once, and then use that type in
multiple locations:
1. Define the type with a tag, and then use that tag anywhere within its
scope to refer to the definition.
2. Define the type with a typedef, and then use that typedef anywhere
withing it's scope to refer to the definition.
3. Define the type in the "type-specifier" of a declaration with
multiple declarators. All of the declarators will share the definition
of that type.
Because these methods for sharing a type definition exist, the type
compatibility rules were designed on the assumption that anybody who
defines two distinct types must have intended them to be distinct, even
if the definitions of those types are identical. It would therefore be a
logical error to use one of those types in a context where the other was
intended. even though the machine code to read and write those types
would be identical. That's the reason such types are not considered
compatible.
However, none of those techniques would be sufficient to allow use of
such a type to communicate between different translation units. Types
defined in two different translation units are necessarily distinct
types, even if defined identically. That's why there's a special rule
that allows distinct types in different translation units to be
compatible with each other, but only if their definitions meet certain
requirements. They must be essentially identical, except that member
types are allowed to be compatible, rather than exactly the same - which
is necessary because, for user-defined types, they can't be the same in
different translation units. The most significant difference allowed by
this rule is that in one definition, a member's type can be an
enumerated type, while in the other definition, the same name identifies
member with the underlying type of that enumerated type. I don't think
that was a desired consequence, just a side-effect of the way the rule
had to be worded.