[C2y] Implement WG14 N3409 (#130299)
This paper removes UB around use of void expressions. Previously, code
like this had undefined behavior:
```
void foo(void) {
(void)(void)1;
extern void x;
x;
}
```
and this is now well-defined in C2y. Functionally, this now means that
it is valid to use `void` as a `_Generic` association.diff --git a/clang-tools-extra/clangd/IncludeFixer.cpp b/clang-tools-extra/clangd/IncludeFixer.cpp
index fadd110..8b74c76 100644
--- a/clang-tools-extra/clangd/IncludeFixer.cpp
+++ b/clang-tools-extra/clangd/IncludeFixer.cpp
@@ -84,7 +84,6 @@
case diag::err_array_incomplete_or_sizeless_type:
case diag::err_array_size_incomplete_type:
case diag::err_asm_incomplete_type:
- case diag::err_assoc_type_incomplete:
case diag::err_bad_cast_incomplete:
case diag::err_call_function_incomplete_return:
case diag::err_call_incomplete_argument:
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index cf363d6..7859871 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -111,6 +111,10 @@
C2y Feature Support
^^^^^^^^^^^^^^^^^^^
+- Implement `WG14 N3409 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3409.pdf>`_
+ which removes UB around use of ``void`` expressions. In practice, this means
+ that ``_Generic`` selection associations may now have ``void`` type, but it
+ also removes UB with code like ``(void)(void)1;``.
- Implemented `WG14 N3411 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3411.pdf>`_
which allows a source file to not end with a newline character. This is still
reported as a conforming extension in earlier language modes.
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1b46920..21be7c3 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10425,8 +10425,13 @@
"specified %0 type tag requires a null pointer">, InGroup<TypeSafety>;
// Generic selections.
-def err_assoc_type_incomplete : Error<
- "type %0 in generic association incomplete">;
+def ext_assoc_type_incomplete : Extension<
+ "incomplete type %0 in a '_Generic' association is a C2y extension">,
+ InGroup<C2y>;
+def warn_c2y_compat_assoc_type_incomplete : Warning<
+ "use of incomplete type %0 in a '_Generic' association is incompatible with "
+ "C standards before C2y">,
+ InGroup<CPre2yCompat>, DefaultIgnore;
def err_assoc_type_nonobject : Error<
"type %0 in generic association not an object type">;
def err_assoc_type_variably_modified : Error<
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index f896cca..de7be6b2 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -1748,9 +1748,14 @@
//
// C11 6.5.1.1p2 "The type name in a generic association shall specify a
// complete object type other than a variably modified type."
+ // C2y removed the requirement that an expression form must
+ // use a complete type, though it's still as-if the type has undergone
+ // lvalue conversion. We support this as an extension in C23 and
+ // earlier because GCC does so.
unsigned D = 0;
if (ControllingExpr && Types[i]->getType()->isIncompleteType())
- D = diag::err_assoc_type_incomplete;
+ D = LangOpts.C2y ? diag::warn_c2y_compat_assoc_type_incomplete
+ : diag::ext_assoc_type_incomplete;
else if (ControllingExpr && !Types[i]->getType()->isObjectType())
D = diag::err_assoc_type_nonobject;
else if (Types[i]->getType()->isVariablyModifiedType())
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 0a193b5..944e330 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -4052,9 +4052,9 @@
Diag(ReturnLoc, D) << CurDecl << isa<CXXDestructorDecl>(CurDecl)
<< RetValExp->getSourceRange();
}
- // return (some void expression); is legal in C++.
+ // return (some void expression); is legal in C++ and C2y.
else if (D != diag::ext_return_has_void_expr ||
- !getLangOpts().CPlusPlus) {
+ (!getLangOpts().CPlusPlus && !getLangOpts().C2y)) {
NamedDecl *CurDecl = getCurFunctionOrMethodDecl();
int FunctionKind = 0;
diff --git a/clang/test/C/C2y/n3409.c b/clang/test/C/C2y/n3409.c
new file mode 100644
index 0000000..01be716
--- /dev/null
+++ b/clang/test/C/C2y/n3409.c
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -verify -std=c2y -pedantic %s
+// RUN: %clang_cc1 -verify=pre-c2y -std=c2y -Wpre-c2y-compat %s
+// RUN: %clang_cc1 -verify=ext -std=c23 -pedantic %s
+// expected-no-diagnostics
+
+/* WG14 N3409: Clang 21
+ * Slay Some Earthly Demons X
+ *
+ * Removes the requirement that an expression with type void cannot be used in
+ * any way. This was making it UB to use a void expression in a _Generic
+ * selection expression for no good reason, as well as making it UB to cast a
+ * void expression to void, etc.
+ */
+
+extern void x;
+void foo() {
+ // FIXME: this is technically an extension before C2y and should be diagnosed
+ // under -pedantic.
+ (void)(void)1;
+ // FIXME: same with this.
+ x;
+ _Generic(x, void: 1); /* pre-c2y-warning {{use of incomplete type 'void' in a '_Generic' association is incompatible with C standards before C2y}}
+ ext-warning {{incomplete type 'void' in a '_Generic' association is a C2y extension}}
+ */
+ _Generic(x, typeof(x): 1); /* pre-c2y-warning {{use of incomplete type 'typeof (x)' (aka 'void') in a '_Generic' association is incompatible with C standards before C2y}}
+ ext-warning {{incomplete type 'typeof (x)' (aka 'void') in a '_Generic' association is a C2y extension}}
+ */
+ (void)_Generic(void, default : 1); /* pre-c2y-warning {{passing a type argument as the first operand to '_Generic' is incompatible with C standards before C2y}}
+ ext-warning {{passing a type argument as the first operand to '_Generic' is a C2y extension}}
+ */
+
+ // This is not sufficiently important of an extension to warrant a "not
+ // compatible with standards before C2y" warning, but it is an extension in
+ // C23 and earlier.
+ return x; // ext-warning {{void function 'foo' should not return void expression}}
+}
diff --git a/clang/test/Sema/generic-selection-type-extension.c b/clang/test/Sema/generic-selection-type-extension.c
index 89ac323..1efd045 100644
--- a/clang/test/Sema/generic-selection-type-extension.c
+++ b/clang/test/Sema/generic-selection-type-extension.c
@@ -37,7 +37,7 @@
// but the expression operand form still rejects them.
static_assert(_Generic(struct incomplete, struct incomplete : 1, default : 0) == 1);
static_assert(_Generic(struct another_incomplete, struct incomplete : 1, default : 0) == 0);
-static_assert(_Generic(1, struct also_incomplete : 1, default : 0) == 0); // expected-error {{type 'struct also_incomplete' in generic association incomplete}}
+static_assert(_Generic(1, struct also_incomplete : 1, default : 0) == 0);
void foo(int);
static_assert(_Generic(__typeof__(foo), void(int) : 1, default : 0) == 1);
diff --git a/clang/test/Sema/generic-selection.c b/clang/test/Sema/generic-selection.c
index 1f17896..8e7b6ed 100644
--- a/clang/test/Sema/generic-selection.c
+++ b/clang/test/Sema/generic-selection.c
@@ -5,7 +5,7 @@
void foo(int n) {
(void) _Generic(0, // ext-warning {{'_Generic' is a C11 extension}}
- struct A: 0, // expected-error {{type 'struct A' in generic association incomplete}}
+ struct A: 0, // ext-warning {{incomplete type 'struct A' in a '_Generic' association is a C2y extension}}
void(): 0, // expected-error {{type 'void ()' in generic association not an object type}}
int[n]: 0); // expected-error {{type 'int[n]' in generic association is a variably modified type}}
diff --git a/clang/test/SemaCXX/generic-selection.cpp b/clang/test/SemaCXX/generic-selection.cpp
index 9e47dde..aa4a4c4 100644
--- a/clang/test/SemaCXX/generic-selection.cpp
+++ b/clang/test/SemaCXX/generic-selection.cpp
@@ -81,10 +81,10 @@
// is an elaborated type specifier followed by the association's value and
// it should work the same as in C.
(void)_Generic(s, struct S : 1);
+ (void)_Generic(s, struct T : 1);
// The rest of these cases test that we still produce a reasonable diagnostic
// when referencing an unknown type or trying to define a type in other ways.
- (void)_Generic(s, struct T : 1); // expected-error {{type 'struct T' in generic association incomplete}}
(void)_Generic(s, struct U { int a; } : 1); // expected-error {{'U' cannot be defined in a type specifier}}
(void)_Generic(s, struct V : S); // expected-error {{'S' does not refer to a value}}
(void)_Generic(s, struct W : S { int b; } : 1); // expected-error {{expected '(' for function-style cast or type construction}}