On 2025-04-10, bart <
bc@freeuk.com> wrote:
ATEOTD, to share such a type across two or more translation units, means
each one seeing its own definition of it. Nothing stops you having
somewhat different versions of it, as I had above, so this part has to
be taken on trust. This was part of my broader point that sharing
non-linkage named entities across modules in C was ad hoc.
Nothing stops you because struct types are de-facto non-linkage
entities in the toolchain technology that is in widespread use.
Nothing stops an implementation from recording, into object files, info
that can be used to implement stricter type checking among
translation units at link time.
Here is one possible implementation.
All file-scope struct/union types are compiled in some kind of
struct/union table in the object file.
Those struct/union types are included which have been used as follows,
either directly or via nesting:
- in a parameter type of an external linkage function;
- in the return type of an external linkage function; or
- in the type of an object declared with external linkage.
In the table we have entries, say, consisting of the tag and, say,
tag name and, say, a 32 bit hash of the structure shape, taken over
all the members: their names, types, alignment and everything else
that influences compatibility.
Then, when linking, we merge all of the struct/union tables together.
Whenever we see a tag that has a different 32 bit hash, that's a
conflict, and we report those two files with the different hashes as
conflicting on their definition of the type indicated by the tag name.
The diagnostic dcould be given in tabular form, e.g:
error: 3 incompatible definitions of "struct foo" exist:
1: foo.o foo-helper.o
2: bar.o
3: stack.o queue.o
The rules for which types are included in the tables allow uses
where the types are not depended upon to be compatible.
Two mdoules can have a different "struct foo { ... }" at file scope,
which they each only use locally: neither module passes an
instance of its foo to the other module where it is interpreted
as that module's foo.
These exchanges happen via the arguments of external functions,
via global variables and via function returns. That's why we
suspect those places.
The rules could give rise to false positives, like when the
foo module has helper functions that take "struct foo *" and
are not called from outside of the module, but the helpers have not been
declared static, and another module does the same thing, creating a
clash in the table entries.
That diagnostic would then be misleading; but it would still
indicate that something is not tidy in the program.
-- TXR Programming Language: http://nongnu.org/txrCygnal: Cygwin Native Application Library: http://kylheku.com/cygnalMastodon: @Kazinator@mstdn.ca