Author: Mital Ashok Date: 2024-08-21T10:09:08+02:00 New Revision: 9d739e54f4506bf9bd220c5d65f710b86a39f6d5
URL: https://github.com/llvm/llvm-project/commit/9d739e54f4506bf9bd220c5d65f710b86a39f6d5 DIFF: https://github.com/llvm/llvm-project/commit/9d739e54f4506bf9bd220c5d65f710b86a39f6d5.diff LOG: [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. Added: Modified: clang/docs/ReleaseNotes.rst clang/include/clang/AST/ExprCXX.h clang/lib/Sema/SemaExprCXX.cpp clang/lib/Sema/SemaInit.cpp clang/test/CXX/drs/cwg23xx.cpp clang/test/SemaCXX/attr-annotate.cpp clang/test/SemaCXX/cxx2a-explicit-bool.cpp clang/test/SemaCXX/sugared-auto.cpp clang/www/cxx_dr_status.html Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 1df3f0e7e75ca3..127b9541d5c5d8 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -152,6 +152,9 @@ Resolutions to C++ Defect Reports - ``nullptr`` is now promoted to ``void*`` when passed to a C-style variadic function. (`CWG722: Can nullptr be passed to an ellipsis? <https://cplusplus.github.io/CWG/issues/722.html>`_) +- Allow ``void{}`` as a prvalue of type ``void``. + (`CWG2351: void{} <https://cplusplus.github.io/CWG/issues/2351.html>`_). + C Language Changes ------------------ diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 847a6ea408e98e..975bcdac5069b9 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -2176,8 +2176,9 @@ class LambdaExpr final : public Expr, const_child_range children() const; }; -/// An expression "T()" which creates a value-initialized rvalue of type -/// T, which is a non-class type. See (C++98 [5.2.3p2]). +/// An expression "T()" which creates an rvalue of a non-class type T. +/// For non-void T, the rvalue is value-initialized. +/// See (C++98 [5.2.3p2]). class CXXScalarValueInitExpr : public Expr { friend class ASTStmtReader; diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 5356bcf172f752..746c67ff1e979f 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1646,12 +1646,23 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo, return ExprError(Diag(TyBeginLoc, diag::err_init_for_function_type) << Ty << FullRange); - // C++17 [expr.type.conv]p2: - // If the type is cv void and the initializer is (), the expression is a - // prvalue of the specified type that performs no initialization. - if (!Ty->isVoidType() && - RequireCompleteType(TyBeginLoc, ElemTy, - diag::err_invalid_incomplete_type_use, FullRange)) + // C++17 [expr.type.conv]p2, per DR2351: + // If the type is cv void and the initializer is () or {}, the expression is + // a prvalue of the specified type that performs no initialization. + if (Ty->isVoidType()) { + if (Exprs.empty()) + return new (Context) CXXScalarValueInitExpr( + Ty.getUnqualifiedType(), TInfo, Kind.getRange().getEnd()); + if (ListInitialization && + cast<InitListExpr>(Exprs[0])->getNumInits() == 0) { + return CXXFunctionalCastExpr::Create( + Context, Ty.getUnqualifiedType(), VK_PRValue, TInfo, CK_ToVoid, + Exprs[0], /*Path=*/nullptr, CurFPFeatureOverrides(), + Exprs[0]->getBeginLoc(), Exprs[0]->getEndLoc()); + } + } else if (RequireCompleteType(TyBeginLoc, ElemTy, + diag::err_invalid_incomplete_type_use, + FullRange)) return ExprError(); // Otherwise, the expression is a prvalue of the specified type whose diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 2666e60c0dd67c..dcfe3bc80c87ac 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -5490,6 +5490,7 @@ static void TryValueInitialization(Sema &S, // // To value-initialize an object of type T means: QualType T = Entity.getType(); + assert(!T->isVoidType() && "Cannot value-init void"); // -- if T is an array type, then each element is value-initialized; T = S.Context.getBaseElementType(T); diff --git a/clang/test/CXX/drs/cwg23xx.cpp b/clang/test/CXX/drs/cwg23xx.cpp index 77fd6a337436e3..7f57d237526bc5 100644 --- a/clang/test/CXX/drs/cwg23xx.cpp +++ b/clang/test/CXX/drs/cwg23xx.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++98 %s -verify=expected -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s +// RUN: %clang_cc1 -std=c++98 %s -verify=expected,cxx98 -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s // RUN: %clang_cc1 -std=c++11 %s -verify=expected,cxx11-14,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s // RUN: %clang_cc1 -std=c++14 %s -verify=expected,cxx11-14,since-cxx11,since-cxx14 -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s // 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 } } +namespace cwg2351 { // cwg2351: 20 +#if __cplusplus >= 201103L + static_assert((void{}, true), ""); + + void f() { + return void{}; + } + + template<typename T> + void g() { + return T{}; + } + template void g<void>(); + template void g<const void>(); + + void h() { + return {}; + // since-cxx11-error@-1 {{void function 'h' must not return a value}} + } + + template<typename T, int... I> + T i() { + return T{I...}; + } + template void i<void>(); + template const void i<const void>(); + + static_assert((void({}), true), ""); + // since-cxx11-error@-1 {{cannot initialize non-class type 'void' with a parenthesized initializer list}} +#else + int I = (void{}, 0); + // cxx98-error@-1 {{expected ')'}} + // cxx98-note@-2 {{to match this '('}} + // cxx98-error@-3 {{expected expression}} +#endif +} + namespace cwg2352 { // cwg2352: 10 int **p; const int *const *const &f1() { return p; } diff --git a/clang/test/SemaCXX/attr-annotate.cpp b/clang/test/SemaCXX/attr-annotate.cpp index 3854f72bbcac1c..846ef4119f1d7c 100644 --- a/clang/test/SemaCXX/attr-annotate.cpp +++ b/clang/test/SemaCXX/attr-annotate.cpp @@ -43,10 +43,10 @@ namespace test0 { template<typename T> struct B { [[clang::annotate("test", ((void)T{}, 9))]] void t() {} - // expected-error@-1 {{illegal initializer type 'void'}} + // expected-error@-1 {{cannot create object of function type 'void ()'}} }; B<int> b; - B<void> b1; + B<void ()> b1; // expected-note@-1 {{in instantiation of template class}} } diff --git a/clang/test/SemaCXX/cxx2a-explicit-bool.cpp b/clang/test/SemaCXX/cxx2a-explicit-bool.cpp index 03799c52654a5f..c106de1e5efd09 100644 --- a/clang/test/SemaCXX/cxx2a-explicit-bool.cpp +++ b/clang/test/SemaCXX/cxx2a-explicit-bool.cpp @@ -75,11 +75,11 @@ struct D { template <typename T> struct E { // expected-note@-1+ {{candidate constructor}} explicit((T{}, false)) - // expected-error@-1 {{illegal initializer type 'void'}} + // expected-error@-1 {{cannot create object of function type 'void ()'}} E(int); }; -E<void> e = 1; +E<void ()> e = 1; // expected-error@-1 {{no viable conversion}} // expected-note@-2 {{in instantiation of}} diff --git a/clang/test/SemaCXX/sugared-auto.cpp b/clang/test/SemaCXX/sugared-auto.cpp index 5fdfb09689b667..b5bb4f0f85a775 100644 --- a/clang/test/SemaCXX/sugared-auto.cpp +++ b/clang/test/SemaCXX/sugared-auto.cpp @@ -112,6 +112,12 @@ N t6 = [] { // expected-error {{rvalue of type 'void'}} return; }(); +N t7 = [] { // expected-error {{rvalue of type 'Virus' (aka 'void')}} + if (true) + return Ebola(); + return SARS{}; +}(); + } // namespace function_multiple_basic #define TEST_AUTO(X, A, B) \ diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index e6c955a5c0e255..a8d2d813d0f536 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -13921,7 +13921,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td><a href="https://cplusplus.github.io/CWG/issues/2351.html">2351</a></td> <td>CD5</td> <td><TT>void{}</TT></td> - <td class="unknown" align="center">Unknown</td> + <td class="unreleased" align="center">Clang 20</td> </tr> <tr id="2352"> <td><a href="https://cplusplus.github.io/CWG/issues/2352.html">2352</a></td> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits