uecker 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...
I think this is a bug in my implementation for GCC. Here, the wording in the standard for the attributes makes this clear that an entity can be redeclared with and without the attribute, but it is considered "marked" after the first declaration that has it. 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