Skip to content

Commit b19ed9c

Browse files
authored
[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.
1 parent 3a228a3 commit b19ed9c

File tree

9 files changed

+58
-9
lines changed

9 files changed

+58
-9
lines changed

clang-tools-extra/clangd/IncludeFixer.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ std::vector<Fix> IncludeFixer::fix(DiagnosticsEngine::Level DiagLevel,
8484
case diag::err_array_incomplete_or_sizeless_type:
8585
case diag::err_array_size_incomplete_type:
8686
case diag::err_asm_incomplete_type:
87-
case diag::err_assoc_type_incomplete:
8887
case diag::err_bad_cast_incomplete:
8988
case diag::err_call_function_incomplete_return:
9089
case diag::err_call_incomplete_argument:

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ C Language Changes
111111

112112
C2y Feature Support
113113
^^^^^^^^^^^^^^^^^^^
114+
- Implement `WG14 N3409 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3409.pdf>`_
115+
which removes UB around use of ``void`` expressions. In practice, this means
116+
that ``_Generic`` selection associations may now have ``void`` type, but it
117+
also removes UB with code like ``(void)(void)1;``.
114118
- Implemented `WG14 N3411 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3411.pdf>`_
115119
which allows a source file to not end with a newline character. This is still
116120
reported as a conforming extension in earlier language modes.

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10425,8 +10425,13 @@ def warn_type_safety_null_pointer_required : Warning<
1042510425
"specified %0 type tag requires a null pointer">, InGroup<TypeSafety>;
1042610426

1042710427
// Generic selections.
10428-
def err_assoc_type_incomplete : Error<
10429-
"type %0 in generic association incomplete">;
10428+
def ext_assoc_type_incomplete : Extension<
10429+
"incomplete type %0 in a '_Generic' association is a C2y extension">,
10430+
InGroup<C2y>;
10431+
def warn_c2y_compat_assoc_type_incomplete : Warning<
10432+
"use of incomplete type %0 in a '_Generic' association is incompatible with "
10433+
"C standards before C2y">,
10434+
InGroup<CPre2yCompat>, DefaultIgnore;
1043010435
def err_assoc_type_nonobject : Error<
1043110436
"type %0 in generic association not an object type">;
1043210437
def err_assoc_type_variably_modified : Error<

clang/lib/Sema/SemaExpr.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1748,9 +1748,14 @@ ExprResult Sema::CreateGenericSelectionExpr(
17481748
//
17491749
// C11 6.5.1.1p2 "The type name in a generic association shall specify a
17501750
// complete object type other than a variably modified type."
1751+
// C2y removed the requirement that an expression form must
1752+
// use a complete type, though it's still as-if the type has undergone
1753+
// lvalue conversion. We support this as an extension in C23 and
1754+
// earlier because GCC does so.
17511755
unsigned D = 0;
17521756
if (ControllingExpr && Types[i]->getType()->isIncompleteType())
1753-
D = diag::err_assoc_type_incomplete;
1757+
D = LangOpts.C2y ? diag::warn_c2y_compat_assoc_type_incomplete
1758+
: diag::ext_assoc_type_incomplete;
17541759
else if (ControllingExpr && !Types[i]->getType()->isObjectType())
17551760
D = diag::err_assoc_type_nonobject;
17561761
else if (Types[i]->getType()->isVariablyModifiedType())

clang/lib/Sema/SemaStmt.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4052,9 +4052,9 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
40524052
Diag(ReturnLoc, D) << CurDecl << isa<CXXDestructorDecl>(CurDecl)
40534053
<< RetValExp->getSourceRange();
40544054
}
4055-
// return (some void expression); is legal in C++.
4055+
// return (some void expression); is legal in C++ and C2y.
40564056
else if (D != diag::ext_return_has_void_expr ||
4057-
!getLangOpts().CPlusPlus) {
4057+
(!getLangOpts().CPlusPlus && !getLangOpts().C2y)) {
40584058
NamedDecl *CurDecl = getCurFunctionOrMethodDecl();
40594059

40604060
int FunctionKind = 0;

clang/test/C/C2y/n3409.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// RUN: %clang_cc1 -verify -std=c2y -pedantic %s
2+
// RUN: %clang_cc1 -verify=pre-c2y -std=c2y -Wpre-c2y-compat %s
3+
// RUN: %clang_cc1 -verify=ext -std=c23 -pedantic %s
4+
// expected-no-diagnostics
5+
6+
/* WG14 N3409: Clang 21
7+
* Slay Some Earthly Demons X
8+
*
9+
* Removes the requirement that an expression with type void cannot be used in
10+
* any way. This was making it UB to use a void expression in a _Generic
11+
* selection expression for no good reason, as well as making it UB to cast a
12+
* void expression to void, etc.
13+
*/
14+
15+
extern void x;
16+
void foo() {
17+
// FIXME: this is technically an extension before C2y and should be diagnosed
18+
// under -pedantic.
19+
(void)(void)1;
20+
// FIXME: same with this.
21+
x;
22+
_Generic(x, void: 1); /* pre-c2y-warning {{use of incomplete type 'void' in a '_Generic' association is incompatible with C standards before C2y}}
23+
ext-warning {{incomplete type 'void' in a '_Generic' association is a C2y extension}}
24+
*/
25+
_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}}
26+
ext-warning {{incomplete type 'typeof (x)' (aka 'void') in a '_Generic' association is a C2y extension}}
27+
*/
28+
(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}}
29+
ext-warning {{passing a type argument as the first operand to '_Generic' is a C2y extension}}
30+
*/
31+
32+
// This is not sufficiently important of an extension to warrant a "not
33+
// compatible with standards before C2y" warning, but it is an extension in
34+
// C23 and earlier.
35+
return x; // ext-warning {{void function 'foo' should not return void expression}}
36+
}

clang/test/Sema/generic-selection-type-extension.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ static_assert(_Generic(ci, int : 1, const int : 0) == 1); // expected-warning {{
3737
// but the expression operand form still rejects them.
3838
static_assert(_Generic(struct incomplete, struct incomplete : 1, default : 0) == 1);
3939
static_assert(_Generic(struct another_incomplete, struct incomplete : 1, default : 0) == 0);
40-
static_assert(_Generic(1, struct also_incomplete : 1, default : 0) == 0); // expected-error {{type 'struct also_incomplete' in generic association incomplete}}
40+
static_assert(_Generic(1, struct also_incomplete : 1, default : 0) == 0);
4141

4242
void foo(int);
4343
static_assert(_Generic(__typeof__(foo), void(int) : 1, default : 0) == 1);

clang/test/Sema/generic-selection.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ void g(void);
55

66
void foo(int n) {
77
(void) _Generic(0, // ext-warning {{'_Generic' is a C11 extension}}
8-
struct A: 0, // expected-error {{type 'struct A' in generic association incomplete}}
8+
struct A: 0, // ext-warning {{incomplete type 'struct A' in a '_Generic' association is a C2y extension}}
99
void(): 0, // expected-error {{type 'void ()' in generic association not an object type}}
1010
int[n]: 0); // expected-error {{type 'int[n]' in generic association is a variably modified type}}
1111

clang/test/SemaCXX/generic-selection.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,10 @@ void func(struct S s) {
8181
// is an elaborated type specifier followed by the association's value and
8282
// it should work the same as in C.
8383
(void)_Generic(s, struct S : 1);
84+
(void)_Generic(s, struct T : 1);
8485

8586
// The rest of these cases test that we still produce a reasonable diagnostic
8687
// when referencing an unknown type or trying to define a type in other ways.
87-
(void)_Generic(s, struct T : 1); // expected-error {{type 'struct T' in generic association incomplete}}
8888
(void)_Generic(s, struct U { int a; } : 1); // expected-error {{'U' cannot be defined in a type specifier}}
8989
(void)_Generic(s, struct V : S); // expected-error {{'S' does not refer to a value}}
9090
(void)_Generic(s, struct W : S { int b; } : 1); // expected-error {{expected '(' for function-style cast or type construction}}

0 commit comments

Comments
 (0)