[clang] [SemaCXX] Implement CWG2351 `void{}` (PR #78060)

via cfe-commits cfe-commits at lists.llvm.org
Sat Jan 13 10:40:49 PST 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Mital Ashok (MitalAshok)

<details>
<summary>Changes</summary>

As 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 'void' functional cast to T <NoOp>
`-InitListExpr 'void'
// using T = const void;
CXXFunctionalCastExpr 'void' functional cast to T <NoOp>
`-InitListExpr 'void'
```

(Since the `InitListExpr` already has `void` before anything is done, the type doesn't need to be adjusted)

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.

---
Full diff: https://github.com/llvm/llvm-project/pull/78060.diff


5 Files Affected:

- (modified) clang/docs/ReleaseNotes.rst (+2) 
- (modified) clang/include/clang/AST/ExprCXX.h (+3-2) 
- (modified) clang/lib/Sema/SemaExprCXX.cpp (+15-6) 
- (modified) clang/test/CXX/drs/dr23xx.cpp (+31) 
- (modified) clang/www/cxx_dr_status.html (+1-1) 


``````````diff
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3cbce1be159437..00e200ea76e5bc 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -226,6 +226,8 @@ C++2c Feature Support
 
 Resolutions to C++ Defect Reports
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+- Implemented `CWG2351 <https://wg21.link/CWG2351>`_ which allows ``void{}``
+  as a prvalue of type ``void``.
 
 C Language Changes
 ------------------
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 24278016431837..e7b732e4181127 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -2160,8 +2160,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 type T, which is a
+/// non-class type. 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 4ae04358d5df7c..1792ec6bb292d5 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1600,12 +1600,21 @@ 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(Context.VoidTy, TInfo,
+                                                  Kind.getRange().getEnd());
+    if (ListInitialization && cast<InitListExpr>(Exprs[0])->getNumInits() == 0)
+      return CXXFunctionalCastExpr::Create(
+          Context, Context.VoidTy, VK_PRValue, TInfo, CK_NoOp, 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/test/CXX/drs/dr23xx.cpp b/clang/test/CXX/drs/dr23xx.cpp
index d2f4e7652ab568..1bf30a80f743e5 100644
--- a/clang/test/CXX/drs/dr23xx.cpp
+++ b/clang/test/CXX/drs/dr23xx.cpp
@@ -69,6 +69,37 @@ namespace dr2346 { // dr2346: 11
   }
 }
 
+namespace dr2351 { // dr2351: 18
+#if __cplusplus >= 201103L
+  static_assert((void{}, true), "");
+  // since-cxx11-warning at -1 {{left operand of comma operator has no effect}}
+
+  void f() {
+    return void{};
+  }
+
+  template<typename T>
+  void g() {
+    return T{};
+  }
+  template void g<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>();
+
+  static_assert((void({}), true), "");
+  // since-cxx11-error at -1 {{cannot initialize non-class type 'void' with a parenthesized initializer list}}
+#endif
+}
+
 namespace dr2352 { // dr2352: 10
   int **p;
   const int *const *const &f1() { return p; }
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 4a3ed19161f9a5..207f032a5d45e4 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -13914,7 +13914,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 18</td>
   </tr>
   <tr id="2352">
     <td><a href="https://cplusplus.github.io/CWG/issues/2352.html">2352</a></td>

``````````

</details>


https://github.com/llvm/llvm-project/pull/78060


More information about the cfe-commits mailing list