Files
clang-p2996/clang/test/SemaCXX/cxx23-invalid-constexpr.cpp
Timm Baeder dbe308c000 [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
2024-07-24 08:09:06 +02:00

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(); }