jyknight wrote: I now see there's two different parts of the problem to worry about:
First case: A redeclaration in a _different_ scope. This always defines a new distinct type. This was valid before C23, and is still valid regardless of whether the type is compatible. (In contrast with function decls, which must be globally compatible across the entire TU in all scopes). This is the case I was thinking about earlier. This part really feels pretty-much straight-forward. E.g. The following example defines two different `struct Foo` types. The second does not inherit any semantics from the former, and the code is valid before and after C23. ``` struct Foo { [[deprecated("hello")]] int x; }; int test2() { struct Foo { int x; }; struct Foo f = {0}; return f.x; // no deprecation } ``` Newly in C23, the inner `struct Foo` is compatible with the outer `Foo`. As such, you can now implicitly cast between pointers to the two types, use them to read/write the same memory, etc. The expected semantics seems to me to be very clear here: we should determine whether it's a compatible type according to the standard's rules, and we shouldn't inherit any attributes from the other scope. For determining compatibility of nonstandard attributes, we can do similarly to how we handle functions: explicitly check a handful of properties, such as calling convention, and assume compatibility if not otherwise specified. For aggregates, we can also validate that the field offsets are the same, which will handle most of the questionable cases. But then there's the other case... Where you have a redefinition of a struct in the _same scope_. This was formerly invalid, and is now valid only when compatible. In this case, both definitions define "the same type", so it seems reasonable to expect some sort of decl merging semantics. But the standard doesn't appear to say anything at all about what to do. However, we do have some prior art -- we're not inventing the whole idea of decl-merging from scratch right now. So maybe we can follow that prior art? For example, top-level attributes on a struct's definition can be inherited from a declaration in the same scope. It seems plausible that the same would be true for redefinition, and then maybe we'd also extend that same declaration-attribute-merging to fields within the struct. Except, while investigating, I discovered that the pre-C23 behavior is inconsistent between Clang and GCC, potentially resulting in ABI differences. E.g. ``` struct __attribute__((packed)) Foo; struct Foo { char a; int b; }; _Static_assert(sizeof(struct Foo) == 8); // with GCC, because packed on the decl was ignored _Static_assert(sizeof(struct Foo) == 5); // with Clang, because packed on decl was inherited by the definition ``` Both GCC and Clang do inherit deprecation from the decl. E.g.: ``` struct [[deprecated("hello")]] Foo; struct Foo* test1; // Both GCC and Clang say: deprecated: hello struct Foo { int x; }; struct Foo* test2; // Both GCC and Clang say: deprecated: hello ``` Given that, I'd definitely expect that under the new C23 semantics, _redefining_ the struct should obviously also preserve the deprecated attribute. Except...apparently it doesn't in GCC's implementation. Gah. Adding on to the previous test, ``` struct Foo { int x; }; struct Foo* test3; // GCC: no deprecation warning. ``` So...yeah... https://github.com/llvm/llvm-project/pull/132939 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits