[clang] 9d739e5 - [Clang] Implement CWG2351 `void{}` (#78060)

via cfe-commits cfe-commits at lists.llvm.org
Wed Aug 21 01:09:11 PDT 2024


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 at -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 at -1 {{cannot initialize non-class type 'void' with a parenthesized initializer list}}
+#else
+  int I = (void{}, 0);
+  // cxx98-error at -1 {{expected ')'}}
+  //   cxx98-note at -2 {{to match this '('}}
+  // cxx98-error at -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 at -1 {{illegal initializer type 'void'}}
+    // expected-error at -1 {{cannot create object of function type 'void ()'}}
   };
   B<int> b;
-  B<void> b1;
+  B<void ()> b1;
 // expected-note at -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 at -1+ {{candidate constructor}}
   explicit((T{}, false))
-  // expected-error at -1 {{illegal initializer type 'void'}}
+  // expected-error at -1 {{cannot create object of function type 'void ()'}}
   E(int);
 };
 
-E<void> e = 1;
+E<void ()> e = 1;
 // expected-error at -1 {{no viable conversion}}
 // expected-note at -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>


        


More information about the cfe-commits mailing list