[clang] dbe308c - [clang][ExprConst] Allow non-literal types in C++23 (#100062)

via cfe-commits cfe-commits at lists.llvm.org
Tue Jul 23 23:09:10 PDT 2024


Author: Timm Baeder
Date: 2024-07-24T08:09:06+02:00
New Revision: dbe308c000f3401cbf6bb55f2b8d606fe091dcfe

URL: https://github.com/llvm/llvm-project/commit/dbe308c000f3401cbf6bb55f2b8d606fe091dcfe
DIFF: https://github.com/llvm/llvm-project/commit/dbe308c000f3401cbf6bb55f2b8d606fe091dcfe.diff

LOG: [clang][ExprConst] Allow non-literal types in C++23 (#100062)

Instead of diagnosing non-literal types in C++23, allow them and later
diagnose them differently, e.g. because they have a non-constexpr
constructor, destructor, etc.

For this test:
```c++
struct NonLiteral {
  NonLiteral() {}
};
constexpr int foo() {
  NonLiteral L;
  return 1;
}

// static_assert(foo() == 1);
```
The current diagnostics with c++20/c++23 are:
```console
~/code/llvm-project/build » clang -c array.cpp -std=c++20
array.cpp:91:14: error: variable of non-literal type 'NonLiteral' cannot be defined in a constexpr function before C++23
   91 |   NonLiteral L;
      |              ^
array.cpp:87:8: note: 'NonLiteral' is not literal because it is not an aggregate and has no constexpr constructors other than copy or move constructors
   87 | struct NonLiteral {
      |        ^
1 error generated.
------------------------------------------------------------
~/code/llvm-project/build » clang -c array.cpp -std=c++23
(no output)
```

With the `static_assert` enabled, compiling with `-std=c++23` prints:
```console
array.cpp:95:15: error: static assertion expression is not an integral constant expression
   95 | static_assert(foo() == 1);
      |               ^~~~~~~~~~
array.cpp:91:14: note: non-literal type 'NonLiteral' cannot be used in a constant expression
   91 |   NonLiteral L;
      |              ^
array.cpp:95:15: note: in call to 'foo()'
   95 | static_assert(foo() == 1);
      |               ^~~~~
1 error generated.
```

As mentioned in #60311, this is confusing. The output with c++20
suggests that using c++23 will make the problem go away, but it's
diagnosed the same when running the function.

With this commit, the output instead diagnoses _why_ the non-literal
type can't be used:
```console
array.cpp:95:15: error: static assertion expression is not an integral constant expression
   95 | static_assert(foo() == 1);
      |               ^~~~~~~~~~
array.cpp:91:14: note: non-constexpr constructor 'NonLiteral' cannot be used in a constant expression
   91 |   NonLiteral L;
      |              ^
array.cpp:95:15: note: in call to 'foo()'
   95 | static_assert(foo() == 1);
      |               ^~~~~
array.cpp:88:3: note: declared here
   88 |   NonLiteral() {}
      |   ^
1 error generated.

``` 




Fixes #60311

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/AST/ExprConstant.cpp
    clang/test/CXX/drs/cwg18xx.cpp
    clang/test/SemaCXX/constant-expression-cxx11.cpp
    clang/test/SemaCXX/constant-expression-cxx2b.cpp
    clang/test/SemaCXX/cxx23-invalid-constexpr.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index ac1de0db9ce48..c78e37b48713a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -76,6 +76,7 @@ C++20 Feature Support
 
 C++23 Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
+- Removed the restriction to literal types in constexpr functions in C++23 mode.
 
 C++2c Feature Support
 ^^^^^^^^^^^^^^^^^^^^^

diff  --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index fcb382474ea62..ec19bce283fd5 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2401,6 +2401,10 @@ static bool CheckMemberPointerConstantExpression(EvalInfo &Info,
 /// produce an appropriate diagnostic.
 static bool CheckLiteralType(EvalInfo &Info, const Expr *E,
                              const LValue *This = nullptr) {
+  // The restriction to literal types does not exist in C++23 anymore.
+  if (Info.getLangOpts().CPlusPlus23)
+    return true;
+
   if (!E->isPRValue() || E->getType()->isLiteralType(Info.Ctx))
     return true;
 

diff  --git a/clang/test/CXX/drs/cwg18xx.cpp b/clang/test/CXX/drs/cwg18xx.cpp
index 323e56f9c5278..adfdb738e81c9 100644
--- a/clang/test/CXX/drs/cwg18xx.cpp
+++ b/clang/test/CXX/drs/cwg18xx.cpp
@@ -3,8 +3,8 @@
 // RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx14,cxx98-14,cxx11-17,since-cxx11,since-cxx14 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx14,since-cxx17,cxx11-17,since-cxx11,since-cxx14,cxx17 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx14,since-cxx17,since-cxx20,since-cxx11,since-cxx14 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx14,since-cxx17,since-cxx20,since-cxx11,since-cxx14 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-unknown %s -verify=expected,since-cxx14,since-cxx17,since-cxx20,since-cxx11,since-cxx14 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx14,since-cxx17,since-cxx20,since-cxx23,since-cxx11,since-cxx14 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-unknown %s -verify=expected,since-cxx14,since-cxx17,since-cxx20,since-cxx23,since-cxx11,since-cxx14 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors
 
 #if __cplusplus == 199711L
 #define static_assert(...) __extension__ _Static_assert(__VA_ARGS__)
@@ -480,8 +480,12 @@ namespace cwg1872 { // cwg1872: 9
   static_assert(y == 0);
 #endif
   constexpr int z = A<Z>().f();
-  // since-cxx11-error at -1 {{constexpr variable 'z' must be initialized by a constant expression}}
-  //   since-cxx11-note at -2 {{non-literal type 'A<Z>' cannot be used in a constant expression}}
+  // since-cxx11-error at -1 {{constexpr variable 'z' must be initialized by a constant expression}}a
+#if __cplusplus < 202302L
+  //   since-cxx11-note at -3 {{non-literal type 'A<Z>' cannot be used in a constant expression}}
+#else
+  //   since-cxx23-note at -5 {{cannot construct object of type 'A<cwg1872::Z>' with virtual base class in a constant expression}}
+#endif
 #endif
 }
 

diff  --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp
index efb391ba0922d..6df8a4740d6cc 100644
--- a/clang/test/SemaCXX/constant-expression-cxx11.cpp
+++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -std=c++23 -isystem %S/Inputs -fsyntax-only -verify=expected,cxx20_23,cxx23    -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion
-// RUN: %clang_cc1 -std=c++20 -isystem %S/Inputs -fsyntax-only -verify=expected,cxx11_20,cxx20_23 -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion
-// RUN: %clang_cc1 -std=c++11 -isystem %S/Inputs -fsyntax-only -verify=expected,cxx11_20,cxx11    -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion
+// RUN: %clang_cc1 -std=c++23 -isystem %S/Inputs -fsyntax-only -verify=expected,cxx20_23,cxx23              -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion
+// RUN: %clang_cc1 -std=c++20 -isystem %S/Inputs -fsyntax-only -verify=expected,cxx11_20,cxx20_23,pre-cxx23 -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion
+// RUN: %clang_cc1 -std=c++11 -isystem %S/Inputs -fsyntax-only -verify=expected,cxx11_20,cxx11,pre-cxx23    -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion
 
 namespace StaticAssertFoldTest {
 
@@ -1011,10 +1011,12 @@ constexpr bool b(int n) { return &n; }
 static_assert(b(0), "");
 
 struct NonLiteral {
-  NonLiteral();
+  NonLiteral(); // cxx23-note {{declared here}}
   int f();
 };
-constexpr int k = NonLiteral().f(); // expected-error {{constant expression}} expected-note {{non-literal type 'NonLiteral'}}
+constexpr int k = NonLiteral().f(); // expected-error {{constant expression}} \
+				    // pre-cxx23-note {{non-literal type 'NonLiteral'}} \
+				    // cxx23-note {{non-constexpr constructor 'NonLiteral' cannot be used in a constant expression}}
 
 }
 
@@ -1270,8 +1272,10 @@ static_assert(makeComplexWrap(1,0) != complex(0, 1), "");
 
 namespace PR11595 {
   struct A { constexpr bool operator==(int x) const { return true; } };
-  struct B { B(); A& x; };
-  static_assert(B().x == 3, "");  // expected-error {{constant expression}} expected-note {{non-literal type 'B' cannot be used in a constant expression}}
+  struct B { B(); A& x; }; // cxx23-note {{declared here}}
+  static_assert(B().x == 3, "");  // expected-error {{constant expression}} \
+				  // pre-cxx23-note {{non-literal type 'B' cannot be used in a constant expression}} \
+				  // cxx23-note {{non-constexpr constructor 'B' cannot be used in a constant expression}}
 
   constexpr bool f(int k) { // cxx11_20-error {{constexpr function never produces a constant expression}}
     return B().x == k; // cxx11_20-note {{non-literal type 'B' cannot be used in a constant expression}}

diff  --git a/clang/test/SemaCXX/constant-expression-cxx2b.cpp b/clang/test/SemaCXX/constant-expression-cxx2b.cpp
index 2519839b7ac57..42b3f81cc713d 100644
--- a/clang/test/SemaCXX/constant-expression-cxx2b.cpp
+++ b/clang/test/SemaCXX/constant-expression-cxx2b.cpp
@@ -3,7 +3,7 @@
 
 struct NonLiteral { // cxx2a-note {{'NonLiteral' is not literal}} \
                     // cxx23-note 2{{'NonLiteral' is not literal}}
-  NonLiteral() {}
+  NonLiteral() {} // cxx23-note 2{{declared here}}
 };
 
 struct Constexpr{};
@@ -165,9 +165,9 @@ int test_in_lambdas() {
 
   auto non_literal = [](bool b) constexpr {
     if (!b)
-      NonLiteral n; // cxx23-note {{non-literal type 'NonLiteral' cannot be used in a constant expression}} \
-                    // cxx2a-error {{variable of non-literal type 'NonLiteral' cannot be defined in a constexpr function before C++23}} \
-                    // cxx23-warning {{definition of a variable of non-literal type in a constexpr function is incompatible with C++ standards before C++23}}
+      NonLiteral n; // cxx2a-error {{variable of non-literal type 'NonLiteral' cannot be defined in a constexpr function before C++23}} \
+                    // cxx23-warning {{definition of a variable of non-literal type in a constexpr function is incompatible with C++ standards before C++23}} \
+		    // cxx23-note {{non-constexpr constructor 'NonLiteral' cannot be used in a constant expression}}
     return 0;
   };
 
@@ -217,7 +217,7 @@ int test_lambdas_implicitly_constexpr() {
 
   auto non_literal = [](bool b) { // cxx2a-note 2{{declared here}}
     if (b)
-      NonLiteral n; // cxx23-note {{non-literal type 'NonLiteral' cannot be used in a constant expression}}
+      NonLiteral n; // cxx23-note {{non-constexpr constructor 'NonLiteral' cannot be used in a constant expression}}
     return 0;
   };
 

diff  --git a/clang/test/SemaCXX/cxx23-invalid-constexpr.cpp b/clang/test/SemaCXX/cxx23-invalid-constexpr.cpp
index 4dc16c59d8058..3229a91fbcefb 100644
--- a/clang/test/SemaCXX/cxx23-invalid-constexpr.cpp
+++ b/clang/test/SemaCXX/cxx23-invalid-constexpr.cpp
@@ -25,14 +25,14 @@ constexpr int FT(T N) {
 class NonLiteral { // expected-note {{'NonLiteral' is not literal because it is not an aggregate and has no constexpr constructors}}
 public:
   NonLiteral() {}
-  ~NonLiteral() {}
+  ~NonLiteral() {} // expected-note {{declared here}}
 };
 
 constexpr NonLiteral F1() {
   return NonLiteral{};
 }
 
-constexpr int F2(NonLiteral N) {
+constexpr int F2(NonLiteral N) { // expected-note {{non-constexpr function '~NonLiteral' cannot be used in a constant expression}}
   return 8;
 }
 
@@ -46,7 +46,7 @@ class Derived1 : public NonLiteral {
 
 
 struct X {
-  X();
+  X(); // expected-note 2{{declared here}}
   X(const X&);
   X(X&&);
   X& operator=(X&);
@@ -80,7 +80,7 @@ struct WrapperNonT {
 };
 
 struct NonDefaultMembers {
-  constexpr NonDefaultMembers() {}; // expected-note {{non-literal type 'X' cannot be used in a constant expression}}
+  constexpr NonDefaultMembers() {}; // expected-note 2{{non-constexpr constructor 'X' cannot be used in a constant expression}}
   constexpr NonDefaultMembers(NonDefaultMembers const&) {};
   constexpr NonDefaultMembers(NonDefaultMembers &&) {};
   constexpr NonDefaultMembers& operator=(NonDefaultMembers &other) {this->t = other.t; return *this;}
@@ -109,7 +109,6 @@ void test() {
   F1();
   NonLiteral L;
   constexpr auto D = F2(L); // expected-error {{constexpr variable 'D' must be initialized by a constant expression}}
-                            // expected-note at -1 {{non-literal type 'NonLiteral' cannot be used in a constant expression}}
 
   constexpr auto E = FT(1); // expected-error {{constexpr variable 'E' must be initialized by a constant expression}}
                             // expected-note at -1 {{in call}}
@@ -125,8 +124,8 @@ void test() {
 
   static_assert((NonDefaultMembers(), true),""); // expected-error{{expression is not an integral constant expression}} \
                                                  // expected-note {{in call to}}
-  constexpr bool FFF = (NonDefaultMembers() == NonDefaultMembers()); // expected-error{{must be initialized by a constant expression}} \
-                                                                     // expected-note{{non-literal}}
+  constexpr bool FFF = (NonDefaultMembers() == NonDefaultMembers()); // expected-error {{must be initialized by a constant expression}} \
+								     // expected-note {{in call to 'NonDefaultMembers()'}}
 }
 
 struct A {


        


More information about the cfe-commits mailing list