Skip to content

Commit dbe308c

Browse files
authored
[clang][ExprConst] Allow non-literal types in C++23 (llvm#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 llvm#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 llvm#60311
1 parent 63ae1e9 commit dbe308c

File tree

6 files changed

+35
-23
lines changed

6 files changed

+35
-23
lines changed

clang/docs/ReleaseNotes.rst

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ C++20 Feature Support
7676

7777
C++23 Feature Support
7878
^^^^^^^^^^^^^^^^^^^^^
79+
- Removed the restriction to literal types in constexpr functions in C++23 mode.
7980

8081
C++2c Feature Support
8182
^^^^^^^^^^^^^^^^^^^^^

clang/lib/AST/ExprConstant.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -2401,6 +2401,10 @@ static bool CheckMemberPointerConstantExpression(EvalInfo &Info,
24012401
/// produce an appropriate diagnostic.
24022402
static bool CheckLiteralType(EvalInfo &Info, const Expr *E,
24032403
const LValue *This = nullptr) {
2404+
// The restriction to literal types does not exist in C++23 anymore.
2405+
if (Info.getLangOpts().CPlusPlus23)
2406+
return true;
2407+
24042408
if (!E->isPRValue() || E->getType()->isLiteralType(Info.Ctx))
24052409
return true;
24062410

clang/test/CXX/drs/cwg18xx.cpp

+8-4
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
// 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
44
// 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
55
// 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
6-
// 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
7-
// 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
6+
// 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
7+
// 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
88

99
#if __cplusplus == 199711L
1010
#define static_assert(...) __extension__ _Static_assert(__VA_ARGS__)
@@ -480,8 +480,12 @@ namespace cwg1872 { // cwg1872: 9
480480
static_assert(y == 0);
481481
#endif
482482
constexpr int z = A<Z>().f();
483-
// since-cxx11-error@-1 {{constexpr variable 'z' must be initialized by a constant expression}}
484-
// since-cxx11-note@-2 {{non-literal type 'A<Z>' cannot be used in a constant expression}}
483+
// since-cxx11-error@-1 {{constexpr variable 'z' must be initialized by a constant expression}}a
484+
#if __cplusplus < 202302L
485+
// since-cxx11-note@-3 {{non-literal type 'A<Z>' cannot be used in a constant expression}}
486+
#else
487+
// since-cxx23-note@-5 {{cannot construct object of type 'A<cwg1872::Z>' with virtual base class in a constant expression}}
488+
#endif
485489
#endif
486490
}
487491

clang/test/SemaCXX/constant-expression-cxx11.cpp

+11-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// 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
2-
// 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
3-
// 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
1+
// 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
2+
// 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
3+
// 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
44

55
namespace StaticAssertFoldTest {
66

@@ -1011,10 +1011,12 @@ constexpr bool b(int n) { return &n; }
10111011
static_assert(b(0), "");
10121012

10131013
struct NonLiteral {
1014-
NonLiteral();
1014+
NonLiteral(); // cxx23-note {{declared here}}
10151015
int f();
10161016
};
1017-
constexpr int k = NonLiteral().f(); // expected-error {{constant expression}} expected-note {{non-literal type 'NonLiteral'}}
1017+
constexpr int k = NonLiteral().f(); // expected-error {{constant expression}} \
1018+
// pre-cxx23-note {{non-literal type 'NonLiteral'}} \
1019+
// cxx23-note {{non-constexpr constructor 'NonLiteral' cannot be used in a constant expression}}
10181020

10191021
}
10201022

@@ -1270,8 +1272,10 @@ static_assert(makeComplexWrap(1,0) != complex(0, 1), "");
12701272

12711273
namespace PR11595 {
12721274
struct A { constexpr bool operator==(int x) const { return true; } };
1273-
struct B { B(); A& x; };
1274-
static_assert(B().x == 3, ""); // expected-error {{constant expression}} expected-note {{non-literal type 'B' cannot be used in a constant expression}}
1275+
struct B { B(); A& x; }; // cxx23-note {{declared here}}
1276+
static_assert(B().x == 3, ""); // expected-error {{constant expression}} \
1277+
// pre-cxx23-note {{non-literal type 'B' cannot be used in a constant expression}} \
1278+
// cxx23-note {{non-constexpr constructor 'B' cannot be used in a constant expression}}
12751279

12761280
constexpr bool f(int k) { // cxx11_20-error {{constexpr function never produces a constant expression}}
12771281
return B().x == k; // cxx11_20-note {{non-literal type 'B' cannot be used in a constant expression}}

clang/test/SemaCXX/constant-expression-cxx2b.cpp

+5-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
struct NonLiteral { // cxx2a-note {{'NonLiteral' is not literal}} \
55
// cxx23-note 2{{'NonLiteral' is not literal}}
6-
NonLiteral() {}
6+
NonLiteral() {} // cxx23-note 2{{declared here}}
77
};
88

99
struct Constexpr{};
@@ -165,9 +165,9 @@ int test_in_lambdas() {
165165

166166
auto non_literal = [](bool b) constexpr {
167167
if (!b)
168-
NonLiteral n; // cxx23-note {{non-literal type 'NonLiteral' cannot be used in a constant expression}} \
169-
// cxx2a-error {{variable of non-literal type 'NonLiteral' cannot be defined in a constexpr function before C++23}} \
170-
// cxx23-warning {{definition of a variable of non-literal type in a constexpr function is incompatible with C++ standards before C++23}}
168+
NonLiteral n; // cxx2a-error {{variable of non-literal type 'NonLiteral' cannot be defined in a constexpr function before C++23}} \
169+
// cxx23-warning {{definition of a variable of non-literal type in a constexpr function is incompatible with C++ standards before C++23}} \
170+
// cxx23-note {{non-constexpr constructor 'NonLiteral' cannot be used in a constant expression}}
171171
return 0;
172172
};
173173

@@ -217,7 +217,7 @@ int test_lambdas_implicitly_constexpr() {
217217

218218
auto non_literal = [](bool b) { // cxx2a-note 2{{declared here}}
219219
if (b)
220-
NonLiteral n; // cxx23-note {{non-literal type 'NonLiteral' cannot be used in a constant expression}}
220+
NonLiteral n; // cxx23-note {{non-constexpr constructor 'NonLiteral' cannot be used in a constant expression}}
221221
return 0;
222222
};
223223

clang/test/SemaCXX/cxx23-invalid-constexpr.cpp

+6-7
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ constexpr int FT(T N) {
2525
class NonLiteral { // expected-note {{'NonLiteral' is not literal because it is not an aggregate and has no constexpr constructors}}
2626
public:
2727
NonLiteral() {}
28-
~NonLiteral() {}
28+
~NonLiteral() {} // expected-note {{declared here}}
2929
};
3030

3131
constexpr NonLiteral F1() {
3232
return NonLiteral{};
3333
}
3434

35-
constexpr int F2(NonLiteral N) {
35+
constexpr int F2(NonLiteral N) { // expected-note {{non-constexpr function '~NonLiteral' cannot be used in a constant expression}}
3636
return 8;
3737
}
3838

@@ -46,7 +46,7 @@ class Derived1 : public NonLiteral {
4646

4747

4848
struct X {
49-
X();
49+
X(); // expected-note 2{{declared here}}
5050
X(const X&);
5151
X(X&&);
5252
X& operator=(X&);
@@ -80,7 +80,7 @@ struct WrapperNonT {
8080
};
8181

8282
struct NonDefaultMembers {
83-
constexpr NonDefaultMembers() {}; // expected-note {{non-literal type 'X' cannot be used in a constant expression}}
83+
constexpr NonDefaultMembers() {}; // expected-note 2{{non-constexpr constructor 'X' cannot be used in a constant expression}}
8484
constexpr NonDefaultMembers(NonDefaultMembers const&) {};
8585
constexpr NonDefaultMembers(NonDefaultMembers &&) {};
8686
constexpr NonDefaultMembers& operator=(NonDefaultMembers &other) {this->t = other.t; return *this;}
@@ -109,7 +109,6 @@ void test() {
109109
F1();
110110
NonLiteral L;
111111
constexpr auto D = F2(L); // expected-error {{constexpr variable 'D' must be initialized by a constant expression}}
112-
// expected-note@-1 {{non-literal type 'NonLiteral' cannot be used in a constant expression}}
113112

114113
constexpr auto E = FT(1); // expected-error {{constexpr variable 'E' must be initialized by a constant expression}}
115114
// expected-note@-1 {{in call}}
@@ -125,8 +124,8 @@ void test() {
125124

126125
static_assert((NonDefaultMembers(), true),""); // expected-error{{expression is not an integral constant expression}} \
127126
// expected-note {{in call to}}
128-
constexpr bool FFF = (NonDefaultMembers() == NonDefaultMembers()); // expected-error{{must be initialized by a constant expression}} \
129-
// expected-note{{non-literal}}
127+
constexpr bool FFF = (NonDefaultMembers() == NonDefaultMembers()); // expected-error {{must be initialized by a constant expression}} \
128+
// expected-note {{in call to 'NonDefaultMembers()'}}
130129
}
131130

132131
struct A {

0 commit comments

Comments
 (0)