Skip to content

Commit 9d739e5

Browse files
authored
[Clang] Implement CWG2351 void{} (#78060)
Per [CWG2351](https://wg21.link/CWG2351), allow `void{}`, treated the same as `void()`: a prvalue expression of type `void` that performs no initialization. Note that the AST for the expression `T{}` looks like: ``` // using T = int; CXXFunctionalCastExpr 'T':'int' functional cast to T <NoOp> `-InitListExpr 'T':'int' // using T = const int; CXXFunctionalCastExpr 'int' functional cast to T <NoOp> `-InitListExpr 'int' // using T = void; CXXFunctionalCastExpr 'T':'void' functional cast to T <ToVoid> `-InitListExpr 'void' // using T = const void; CXXFunctionalCastExpr 'void' functional cast to T <ToVoid> `-InitListExpr 'void' ``` As for `void()`/`T() [T = const void]`, that looked like `CXXScalarValueInitExpr 'void'` and is unchanged after this. For reference, C++98 [5.2.3p2] says: > The expression `T()`, where `T` is a simple-type-specifier (7.1.5.2) for a non-array complete object type or the (possibly cv-qualified) void type, creates an rvalue of the specified type, whose value is determined by default-initialization (8.5; no initialization is done for the `void()` case). [*Note:* if `T` is a non-class type that is *cv-qualified*, the `cv-qualifiers` are ignored when determining the type of the resulting rvalue (3.10). ] Though it is a bit of a misnomer that, for `T = void`, `CXXScalarValueInitExpr` does not perform value initialization, it would be a breaking change to change the AST node for `void()`, so I simply reworded the doc comment.
1 parent 99741ac commit 9d739e5

File tree

9 files changed

+73
-14
lines changed

9 files changed

+73
-14
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ Resolutions to C++ Defect Reports
152152
- ``nullptr`` is now promoted to ``void*`` when passed to a C-style variadic function.
153153
(`CWG722: Can nullptr be passed to an ellipsis? <https://cplusplus.github.io/CWG/issues/722.html>`_)
154154

155+
- Allow ``void{}`` as a prvalue of type ``void``.
156+
(`CWG2351: void{} <https://cplusplus.github.io/CWG/issues/2351.html>`_).
157+
155158
C Language Changes
156159
------------------
157160

clang/include/clang/AST/ExprCXX.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2176,8 +2176,9 @@ class LambdaExpr final : public Expr,
21762176
const_child_range children() const;
21772177
};
21782178

2179-
/// An expression "T()" which creates a value-initialized rvalue of type
2180-
/// T, which is a non-class type. See (C++98 [5.2.3p2]).
2179+
/// An expression "T()" which creates an rvalue of a non-class type T.
2180+
/// For non-void T, the rvalue is value-initialized.
2181+
/// See (C++98 [5.2.3p2]).
21812182
class CXXScalarValueInitExpr : public Expr {
21822183
friend class ASTStmtReader;
21832184

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1646,12 +1646,23 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo,
16461646
return ExprError(Diag(TyBeginLoc, diag::err_init_for_function_type)
16471647
<< Ty << FullRange);
16481648

1649-
// C++17 [expr.type.conv]p2:
1650-
// If the type is cv void and the initializer is (), the expression is a
1651-
// prvalue of the specified type that performs no initialization.
1652-
if (!Ty->isVoidType() &&
1653-
RequireCompleteType(TyBeginLoc, ElemTy,
1654-
diag::err_invalid_incomplete_type_use, FullRange))
1649+
// C++17 [expr.type.conv]p2, per DR2351:
1650+
// If the type is cv void and the initializer is () or {}, the expression is
1651+
// a prvalue of the specified type that performs no initialization.
1652+
if (Ty->isVoidType()) {
1653+
if (Exprs.empty())
1654+
return new (Context) CXXScalarValueInitExpr(
1655+
Ty.getUnqualifiedType(), TInfo, Kind.getRange().getEnd());
1656+
if (ListInitialization &&
1657+
cast<InitListExpr>(Exprs[0])->getNumInits() == 0) {
1658+
return CXXFunctionalCastExpr::Create(
1659+
Context, Ty.getUnqualifiedType(), VK_PRValue, TInfo, CK_ToVoid,
1660+
Exprs[0], /*Path=*/nullptr, CurFPFeatureOverrides(),
1661+
Exprs[0]->getBeginLoc(), Exprs[0]->getEndLoc());
1662+
}
1663+
} else if (RequireCompleteType(TyBeginLoc, ElemTy,
1664+
diag::err_invalid_incomplete_type_use,
1665+
FullRange))
16551666
return ExprError();
16561667

16571668
// Otherwise, the expression is a prvalue of the specified type whose

clang/lib/Sema/SemaInit.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5490,6 +5490,7 @@ static void TryValueInitialization(Sema &S,
54905490
//
54915491
// To value-initialize an object of type T means:
54925492
QualType T = Entity.getType();
5493+
assert(!T->isVoidType() && "Cannot value-init void");
54935494

54945495
// -- if T is an array type, then each element is value-initialized;
54955496
T = S.Context.getBaseElementType(T);

clang/test/CXX/drs/cwg23xx.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -std=c++98 %s -verify=expected -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s
1+
// RUN: %clang_cc1 -std=c++98 %s -verify=expected,cxx98 -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s
22
// RUN: %clang_cc1 -std=c++11 %s -verify=expected,cxx11-14,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s
33
// RUN: %clang_cc1 -std=c++14 %s -verify=expected,cxx11-14,since-cxx11,since-cxx14 -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s
44
// RUN: %clang_cc1 -std=c++17 %s -verify=expected,since-cxx11,since-cxx14,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s
@@ -213,6 +213,43 @@ namespace cwg2346 { // cwg2346: 11
213213
}
214214
}
215215

216+
namespace cwg2351 { // cwg2351: 20
217+
#if __cplusplus >= 201103L
218+
static_assert((void{}, true), "");
219+
220+
void f() {
221+
return void{};
222+
}
223+
224+
template<typename T>
225+
void g() {
226+
return T{};
227+
}
228+
template void g<void>();
229+
template void g<const void>();
230+
231+
void h() {
232+
return {};
233+
// since-cxx11-error@-1 {{void function 'h' must not return a value}}
234+
}
235+
236+
template<typename T, int... I>
237+
T i() {
238+
return T{I...};
239+
}
240+
template void i<void>();
241+
template const void i<const void>();
242+
243+
static_assert((void({}), true), "");
244+
// since-cxx11-error@-1 {{cannot initialize non-class type 'void' with a parenthesized initializer list}}
245+
#else
246+
int I = (void{}, 0);
247+
// cxx98-error@-1 {{expected ')'}}
248+
// cxx98-note@-2 {{to match this '('}}
249+
// cxx98-error@-3 {{expected expression}}
250+
#endif
251+
}
252+
216253
namespace cwg2352 { // cwg2352: 10
217254
int **p;
218255
const int *const *const &f1() { return p; }

clang/test/SemaCXX/attr-annotate.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ namespace test0 {
4343
template<typename T>
4444
struct B {
4545
[[clang::annotate("test", ((void)T{}, 9))]] void t() {}
46-
// expected-error@-1 {{illegal initializer type 'void'}}
46+
// expected-error@-1 {{cannot create object of function type 'void ()'}}
4747
};
4848
B<int> b;
49-
B<void> b1;
49+
B<void ()> b1;
5050
// expected-note@-1 {{in instantiation of template class}}
5151
}
5252

clang/test/SemaCXX/cxx2a-explicit-bool.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,11 @@ struct D {
7575
template <typename T> struct E {
7676
// expected-note@-1+ {{candidate constructor}}
7777
explicit((T{}, false))
78-
// expected-error@-1 {{illegal initializer type 'void'}}
78+
// expected-error@-1 {{cannot create object of function type 'void ()'}}
7979
E(int);
8080
};
8181

82-
E<void> e = 1;
82+
E<void ()> e = 1;
8383
// expected-error@-1 {{no viable conversion}}
8484
// expected-note@-2 {{in instantiation of}}
8585

clang/test/SemaCXX/sugared-auto.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,12 @@ N t6 = [] { // expected-error {{rvalue of type 'void'}}
112112
return;
113113
}();
114114

115+
N t7 = [] { // expected-error {{rvalue of type 'Virus' (aka 'void')}}
116+
if (true)
117+
return Ebola();
118+
return SARS{};
119+
}();
120+
115121
} // namespace function_multiple_basic
116122

117123
#define TEST_AUTO(X, A, B) \

clang/www/cxx_dr_status.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13921,7 +13921,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
1392113921
<td><a href="https://cplusplus.github.io/CWG/issues/2351.html">2351</a></td>
1392213922
<td>CD5</td>
1392313923
<td><TT>void{}</TT></td>
13924-
<td class="unknown" align="center">Unknown</td>
13924+
<td class="unreleased" align="center">Clang 20</td>
1392513925
</tr>
1392613926
<tr id="2352">
1392713927
<td><a href="https://cplusplus.github.io/CWG/issues/2352.html">2352</a></td>

0 commit comments

Comments
 (0)