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
159 lines
4.4 KiB
C++
159 lines
4.4 KiB
C++
// RUN: %clang_cc1 -fsyntax-only -verify=expected -std=c++23 %s
|
|
|
|
// This test covers modifications made by P2448R2.
|
|
|
|
// Check that there is no error when a constexpr function that never produces a
|
|
// constant expression, but still an error if such function is called from
|
|
// constexpr context.
|
|
constexpr int F(int N) {
|
|
double D = 2.0 / 0.0; // expected-note {{division by zero}}
|
|
return 1;
|
|
}
|
|
|
|
constexpr int F0(int N) {
|
|
if (N == 0)
|
|
double d2 = 2.0 / 0.0; // expected-note {{division by zero}}
|
|
return 1;
|
|
}
|
|
|
|
template <typename T>
|
|
constexpr int FT(T N) {
|
|
double D = 2.0 / 0.0; // expected-note {{division by zero}}
|
|
return 1;
|
|
}
|
|
|
|
class NonLiteral { // expected-note {{'NonLiteral' is not literal because it is not an aggregate and has no constexpr constructors}}
|
|
public:
|
|
NonLiteral() {}
|
|
~NonLiteral() {} // expected-note {{declared here}}
|
|
};
|
|
|
|
constexpr NonLiteral F1() {
|
|
return NonLiteral{};
|
|
}
|
|
|
|
constexpr int F2(NonLiteral N) { // expected-note {{non-constexpr function '~NonLiteral' cannot be used in a constant expression}}
|
|
return 8;
|
|
}
|
|
|
|
class Derived : public NonLiteral {
|
|
constexpr ~Derived() {};
|
|
};
|
|
|
|
class Derived1 : public NonLiteral {
|
|
constexpr Derived1() : NonLiteral () {}
|
|
};
|
|
|
|
|
|
struct X {
|
|
X(); // expected-note 2{{declared here}}
|
|
X(const X&);
|
|
X(X&&);
|
|
X& operator=(X&);
|
|
X& operator=(X&& other);
|
|
bool operator==(X const&) const;
|
|
};
|
|
|
|
template <typename T>
|
|
struct Wrapper {
|
|
constexpr Wrapper() = default;
|
|
constexpr Wrapper(Wrapper const&) = default;
|
|
constexpr Wrapper(T const& t) : t(t) { }
|
|
constexpr Wrapper(Wrapper &&) = default;
|
|
constexpr X get() const { return t; }
|
|
constexpr bool operator==(Wrapper const&) const = default;
|
|
private:
|
|
T t;
|
|
};
|
|
|
|
struct WrapperNonT {
|
|
constexpr WrapperNonT() = default;
|
|
constexpr WrapperNonT(WrapperNonT const&) = default;
|
|
constexpr WrapperNonT(X const& t) : t(t) { }
|
|
constexpr WrapperNonT(WrapperNonT &&) = default;
|
|
constexpr WrapperNonT& operator=(WrapperNonT &) = default;
|
|
constexpr WrapperNonT& operator=(WrapperNonT&& other) = default;
|
|
constexpr X get() const { return t; }
|
|
constexpr bool operator==(WrapperNonT const&) const = default;
|
|
private:
|
|
X t;
|
|
};
|
|
|
|
struct NonDefaultMembers {
|
|
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;}
|
|
constexpr NonDefaultMembers& operator=(NonDefaultMembers&& other) {this->t = other.t; return *this;}
|
|
constexpr bool operator==(NonDefaultMembers const& other) const {return this->t == other.t;}
|
|
X t;
|
|
};
|
|
|
|
int Glob = 0;
|
|
class C1 {
|
|
public:
|
|
constexpr C1() : D(Glob) {};
|
|
private:
|
|
int D;
|
|
};
|
|
|
|
void test() {
|
|
|
|
constexpr int A = F(3); // expected-error {{constexpr variable 'A' must be initialized by a constant expression}}
|
|
// expected-note@-1 {{in call}}
|
|
F(3);
|
|
constexpr int B = F0(0); // expected-error {{constexpr variable 'B' must be initialized by a constant expression}}
|
|
// expected-note@-1 {{in call}}
|
|
F0(0);
|
|
constexpr auto C = F1(); // expected-error {{constexpr variable cannot have non-literal type 'const NonLiteral'}}
|
|
F1();
|
|
NonLiteral L;
|
|
constexpr auto D = F2(L); // expected-error {{constexpr variable 'D' must be initialized by a constant expression}}
|
|
|
|
constexpr auto E = FT(1); // expected-error {{constexpr variable 'E' must be initialized by a constant expression}}
|
|
// expected-note@-1 {{in call}}
|
|
F2(L);
|
|
|
|
Wrapper<X> x;
|
|
WrapperNonT x1;
|
|
NonDefaultMembers x2;
|
|
|
|
// TODO these produce notes with an invalid source location.
|
|
// static_assert((Wrapper<X>(), true));
|
|
// static_assert((WrapperNonT(), true),"");
|
|
|
|
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 {{in call to 'NonDefaultMembers()'}}
|
|
}
|
|
|
|
struct A {
|
|
A ();
|
|
~A();
|
|
};
|
|
|
|
template <class T>
|
|
struct opt
|
|
{
|
|
union {
|
|
char c;
|
|
T data;
|
|
};
|
|
|
|
constexpr opt() {}
|
|
|
|
constexpr ~opt() {
|
|
if (engaged)
|
|
data.~T();
|
|
}
|
|
|
|
bool engaged = false;
|
|
};
|
|
|
|
consteval void foo() {
|
|
opt<A> a;
|
|
}
|
|
|
|
void bar() { foo(); }
|