[clang] [C23] Implement WG14 N3037 (PR #132939)

2025-03-27 Thread Martin Uecker via cfe-commits

uecker wrote:

BTW: I did not push for this feature to be exposed in earlier language modes 
because GCC will ship with GNU C23 by default with GCC 15.   GCC 15 already saw 
distribution-wide testing in Redhat and Debian and from this experience I do 
not expect any problems related to this feature (so far, I am not aware of any 
issue caused by this feature, which can not be said about some other C23 
changes).  Unrelated testing exercising the new features uncovered a couple of 
bugs in my implementation (as well as other unrelated bugs in GCC), and I would 
expect that this will be true for Clang as well. But I would not expect 
anything critical.  

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


[clang] [C23] Implement WG14 N3037 (PR #132939)

2025-03-27 Thread Martin Uecker via cfe-commits

uecker wrote:

Another comment about attributes: Attributes should not generally affect type 
compatibility (in general, but also relevant for this feature) for two reasons: 
1. Standard attributes are ignorable, and this would then break. 2. 
Compatibility affects aliasing, one one certainly does not want to affect 
aliasing decisions by  adding [[deprecated]] somwhere. 
So only for vendor attributes that affect ABI, this should (and must) be 
considered.

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


[clang] [C23] Implement WG14 N3037 (PR #132939)

2025-03-31 Thread Martin Uecker via cfe-commits

uecker wrote:

> Curiously, GCC doesn't merge the standard attributes either, it seems to do 
> last-one-wins: https://godbolt.org/z/j3W7ej5Kq

I filed a bug for this: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119526

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


[clang] [C23] Implement WG14 N3037 (PR #132939)

2025-04-27 Thread Martin Uecker via cfe-commits

uecker wrote:

I did some initial testing by compiling one of my projects and so far this 
worked fine.

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


[clang] [C23] Implement WG14 N3037 (PR #132939)

2025-05-04 Thread Martin Uecker via cfe-commits

uecker wrote:

So one usability issue I noticed that we also had in GCC is that the warning 
about declaring structs in function prototypes (-Wvisibility) does not make 
sense anymore and becomes annoying. We turned it off in C23 for complete (but 
not for incomplete) structs) without tag. 


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


[clang] Diagnose potential size confusion with VLA params (PR #129772)

2025-03-04 Thread Martin Uecker via cfe-commits


@@ -0,0 +1,68 @@
+// RUN: %clang_cc1 %s -std=c23 -verify=expected,c -fsyntax-only
+// RUN: %clang_cc1 %s -std=c23 -verify=good -fsyntax-only -Wno-vla
+// RUN: %clang_cc1 -x c++ %s -verify -fsyntax-only
+// RUN: %clang_cc1 -DCARET -fsyntax-only -std=c23 
-fno-diagnostics-show-line-numbers -fcaret-diagnostics-max-lines=1 %s 2>&1 | 
FileCheck %s -strict-whitespace
+
+// good-no-diagnostics
+
+int n, m;  // #decl
+int size(int);
+
+void foo(int vla[n], int n); // expected-warning {{variable length array size 
expression refers to declaration from an outer scope}} \
+expected-note {{does not refer to this 
declaration}} \
+expected-note@#decl {{refers to this 
declaration instead}}
+
+void bar(int (*vla)[n], int n); // expected-warning {{variable length array 
size expression refers to declaration from an outer scope}} \
+   expected-note {{does not refer to this 
declaration}} \
+   expected-note@#decl {{refers to this 
declaration instead}}
+
+void baz(int n, int vla[n]); // no diagnostic expected
+
+void quux(int vla[n + 12], int n); // expected-warning {{variable length array 
size expression refers to declaration from an outer scope}} \
+  expected-note {{does not refer to this 
declaration}} \
+  expected-note@#decl {{refers to this 
declaration instead}}
+
+void quibble(int vla[size(n)], int n);  // expected-warning {{variable length 
array size expression refers to declaration from an outer scope}} \
+   expected-note {{does not refer to 
this declaration}} \
+   expected-note@#decl {{refers to 
this declaration instead}}
+
+void quobble(int vla[n + m], int n, int m);  // expected-warning 2 {{variable 
length array size expression refers to declaration from an outer scope}} \
+expected-note 2 {{does not 
refer to this declaration}} \
+expected-note@#decl 2 {{refers 
to this declaration instead}}
+
+// For const int, we still treat the function as having a variably-modified
+// type, but only in C.

uecker wrote:

Note that there is proposal in flight for C that might change this.


https://github.com/llvm/llvm-project/pull/129772
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Diagnose potential size confusion with VLA params (PR #129772)

2025-03-04 Thread Martin Uecker via cfe-commits

uecker wrote:

And a final comment. While the main motivating use case for you may  be VLAs in 
parameter declarations (although as heavy user of those, I never felt the need 
for such a warning), it would seem the warning would be  relevant in cases 
where the arrays are not actually VLAs.  In general, it seems a warning about 
shadowing a visible identifier somewhere and just having it used with the old 
definition. So I wonder whether it should be renamed?

https://github.com/llvm/llvm-project/pull/129772
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [C23] Implement WG14 N3037 (PR #132939)

2025-03-29 Thread Martin Uecker via cfe-commits

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


[clang] [C23] Implement WG14 N3037 (PR #132939)

2025-03-27 Thread Martin Uecker via cfe-commits

uecker wrote:

> It seems to me that we could simplify the implementation by being a bit more 
> literal in the interpretation of the requirements: look only at the type, 
> alignment, name, and bitfield-width of members. Just don't bother checking 
> other stuff
> 
> I'd hope it can look more like a simple loop over the fields of the two 
> record types (similar to mergeFunctionTypes's checks of the functions' param 
> types), rather than starting with the very-complex 
> ASTStructuralEquivalence.cpp, and adding even more complexity to validate 
> attributes.

What I do in GCC is mostly look at those things required in the standard and I 
make sure that all field offsets are the same (e.g. if some attribute would 
change how fields are layed out, this would catch it).  This is a simple loop. 
GCC already had a generic mechanism to compare attributes on types which 
decides whether an attribute makes a type incompatible (.e.g. affects ABI such 
as "packed"), or requires a warning, or whether it is just fine. 

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


[clang] [C23] Implement WG14 N3037 (PR #132939)

2025-03-27 Thread Martin Uecker via cfe-commits


@@ -450,6 +453,116 @@ class StmtComparer {
 };
 } // namespace
 
+static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
+ const Attr *Attr1, const Attr *Attr2) {
+  // Two attributes are structurally equivalent if they are the same kind
+  // of attribute, spelled with the same spelling kind, and have the same
+  // arguments. This means that [[noreturn]] and __attribute__((noreturn)) are
+  // not structurally equivalent, nor are [[nodiscard("foo")]] and
+  // [[nodiscard("bar")]].
+  if (Attr1->getKind() != Attr2->getKind())
+return false;
+
+  if (Attr1->getSyntax() != Attr2->getSyntax())
+return false;
+
+  if (Attr1->getSpellingListIndex() != Attr2->getSpellingListIndex())
+return false;
+
+  auto GetAttrName = [](const Attr *A) {
+if (const IdentifierInfo *II = A->getAttrName())
+  return II->getName();
+return StringRef{};
+  };
+
+  if (GetAttrName(Attr1) != GetAttrName(Attr2))
+return false;
+
+  // FIXME: check the attribute arguments. Attr does not track the arguments on
+  // the base class, which makes this awkward. We may want to tablegen a
+  // comparison function for attributes? In the meantime, we're doing this the
+  // cheap way by pretty printing the attributes and checking they produce

uecker wrote:

> > The standard defines when things are compatible and it explicitly does not 
> > require attributes to match for types to be compatible.
> 
> Anything the standard doesn't talk about is UB by omission, and the standard 
> makes no mention of attributes. There is nothing explicit about this 
> situation.

I don't think this applies here.  We say that types are compatible under 
conditions A B and C. That we do not mention D does not mean there is UB. We 
also do not mention E, F and G or a billion other things. For me, an omission 
is when something is not precisely specified and you can reasonable interpret 
it either way. This is not the case here. But this only applies to standard 
attributes, vendor attributes can do whatever they want.
> 
> I want to dispense with this topic because I feel very strongly we move 
> forward with the current approach. To make this more explicit as to why:
> 
> 0. The goal of this feature is that two types which were compatible 
> across TU boundaries are now compatible if defined within the same TU. So 
> this is about naming and object layout, in general. Attributes impact these 
> kinds of things (packed, transparent_union, type_visibility, preferred_name, 
> randomize_layout, etc), and attribute arguments do as well (btf_decl_tag, 
> objc_bridge_related, uuid, etc)
> 
> 1. The ideal situation is we have individual attributes specify whether 
> they do or do not make two types [in]compatible, then the equivalence checker 
> can generically check this rather than requiring special logic that's trivial 
> for us to forget when adding new attributes.
> 
> 2. I'm not implementing the ideal solution because that's a significant 
> undertaking, attributes are an edge case, and I only have so much bandwidth. 
> I have a FIXME in the patch that makes this clear.
> 
> 3. Since we're not getting the ideal solution today, we can either be 
> safe by default (require attributes to be the same) or we can be unsafe by 
> default (ignore attributes). I'm adamant we go with safe by default until the 
> ideal solution can be implemented.

Sounds reasonable, but I would exempt standard attributes, because I believe 
this is what the standard requires.




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


[clang] [C23] Implement WG14 N3037 (PR #132939)

2025-03-27 Thread Martin Uecker via cfe-commits


@@ -450,6 +453,116 @@ class StmtComparer {
 };
 } // namespace
 
+static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
+ const Attr *Attr1, const Attr *Attr2) {
+  // Two attributes are structurally equivalent if they are the same kind
+  // of attribute, spelled with the same spelling kind, and have the same
+  // arguments. This means that [[noreturn]] and __attribute__((noreturn)) are
+  // not structurally equivalent, nor are [[nodiscard("foo")]] and
+  // [[nodiscard("bar")]].
+  if (Attr1->getKind() != Attr2->getKind())
+return false;
+
+  if (Attr1->getSyntax() != Attr2->getSyntax())
+return false;
+
+  if (Attr1->getSpellingListIndex() != Attr2->getSpellingListIndex())
+return false;
+
+  auto GetAttrName = [](const Attr *A) {
+if (const IdentifierInfo *II = A->getAttrName())
+  return II->getName();
+return StringRef{};
+  };
+
+  if (GetAttrName(Attr1) != GetAttrName(Attr2))
+return false;
+
+  // FIXME: check the attribute arguments. Attr does not track the arguments on
+  // the base class, which makes this awkward. We may want to tablegen a
+  // comparison function for attributes? In the meantime, we're doing this the
+  // cheap way by pretty printing the attributes and checking they produce

uecker wrote:

I guess then everything is UB because we never mention whether the time of day 
plays a role in type compatibility.

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


[clang] [C23] Implement WG14 N3037 (PR #132939)

2025-04-04 Thread Martin Uecker via cfe-commits


@@ -450,6 +453,116 @@ class StmtComparer {
 };
 } // namespace
 
+static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
+ const Attr *Attr1, const Attr *Attr2) {
+  // Two attributes are structurally equivalent if they are the same kind
+  // of attribute, spelled with the same spelling kind, and have the same
+  // arguments. This means that [[noreturn]] and __attribute__((noreturn)) are
+  // not structurally equivalent, nor are [[nodiscard("foo")]] and
+  // [[nodiscard("bar")]].
+  if (Attr1->getKind() != Attr2->getKind())
+return false;
+
+  if (Attr1->getSyntax() != Attr2->getSyntax())
+return false;
+
+  if (Attr1->getSpellingListIndex() != Attr2->getSpellingListIndex())
+return false;
+
+  auto GetAttrName = [](const Attr *A) {
+if (const IdentifierInfo *II = A->getAttrName())
+  return II->getName();
+return StringRef{};
+  };
+
+  if (GetAttrName(Attr1) != GetAttrName(Attr2))
+return false;
+
+  // FIXME: check the attribute arguments. Attr does not track the arguments on
+  // the base class, which makes this awkward. We may want to tablegen a
+  // comparison function for attributes? In the meantime, we're doing this the
+  // cheap way by pretty printing the attributes and checking they produce

uecker wrote:

The standard defines when things are compatible and it explicitly does not 
require attributes to match for types to be compatible (in general, this has 
not much to do with this proposal except that this general rule now also 
applies here).  In general, this proposal does not raise any new questions  
here (but extends the scope of such questions) and the answer to the question 
should be the same as elsewhere (e.g. as in typedef redefinitions, 
compatibility of function types). I would recommend to warn on redefinitions 
that add or remove standard attributes. For compatibility those should *not* 
matter.  For vendor attributes this up to you, of course and some attributes 
will certainly make types incompatible (e.g. "packed", here one can take a look 
at what you do for LTO because these things should already matter there).  For 
vendors attributes, the safe choice is to reject redefinitions. For 
compatibility, one has make a choice for each case, but it should be mostly 
obvious (does it change the representation?) 

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


[clang] [C23] Implement WG14 N3037 (PR #132939)

2025-04-04 Thread Martin Uecker via cfe-commits

uecker wrote:

> 
> Once that is settled, then go back to work on redeclaration/definition 
> support. Possibly it'd be good to pause and ask the standards body to clarify 
> how it's supposed to work first. While I think my proposed semantics above 
> make sense, the standard doesn't actually _specify_ that, one way or the 
> other. And it should.

For  standard attributes, the standard explicitly makes it clear that 
redeclarations w/ and w/o attributes are ok and can be mixed. This is also what 
GCC implements. I do not think there is anything unclear in the standard. Each 
attribute has specific wording that clarifies this.


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