llvm-project/clang/test/SemaCXX/cxx2a-consteval.cpp
Aaron Ballman ca844ab01c Fix template instantiation of UDLs
Previously, we would instantiate the UDL by marking the function as
referenced and potentially binding to a temporary; this skipped
transforming the call when the UDL was dependent on a template
parameter.

Now, we defer all the work to instantiating the call expression for the
UDL. This ensures that constant evaluation occurs at compile time
rather than deferring until runtime.

Fixes Issue 54578.
2022-03-28 14:46:53 -04:00

748 lines
22 KiB
C++

// RUN: %clang_cc1 -std=c++2a -emit-llvm-only -Wno-unused-value %s -verify
typedef __SIZE_TYPE__ size_t;
namespace basic_sema {
consteval int f1(int i) {
return i;
}
consteval constexpr int f2(int i) {
//expected-error@-1 {{cannot combine}}
return i;
}
constexpr auto l_eval = [](int i) consteval {
// expected-note@-1+ {{declared here}}
return i;
};
constexpr consteval int f3(int i) {
//expected-error@-1 {{cannot combine}}
return i;
}
struct A {
consteval int f1(int i) const {
// expected-note@-1 {{declared here}}
return i;
}
consteval A(int i);
consteval A() = default;
consteval ~A() = default; // expected-error {{destructor cannot be declared consteval}}
};
consteval struct B {}; // expected-error {{struct cannot be marked consteval}}
consteval typedef B b; // expected-error {{typedef cannot be consteval}}
consteval int redecl() {return 0;} // expected-note {{previous declaration is here}}
constexpr int redecl() {return 0;} // expected-error {{constexpr declaration of 'redecl' follows consteval declaration}}
consteval int i = 0; // expected-error {{consteval can only be used in function declarations}}
consteval int; // expected-error {{consteval can only be used in function declarations}}
consteval int f1() {} // expected-error {{no return statement in consteval function}}
struct C {
C() {}
~C() {}
};
struct D {
C c;
consteval D() = default; // expected-error {{cannot be consteval}}
consteval ~D() = default; // expected-error {{destructor cannot be declared consteval}}
};
struct E : C {
consteval ~E() {} // expected-error {{cannot be declared consteval}}
};
}
consteval int main() { // expected-error {{'main' is not allowed to be declared consteval}}
return 0;
}
consteval int f_eval(int i) {
// expected-note@-1+ {{declared here}}
return i;
}
namespace taking_address {
using func_type = int(int);
func_type* p1 = (&f_eval);
// expected-error@-1 {{take address}}
func_type* p7 = __builtin_addressof(f_eval);
// expected-error@-1 {{take address}}
auto p = f_eval;
// expected-error@-1 {{take address}}
auto m1 = &basic_sema::A::f1;
// expected-error@-1 {{take address}}
auto l1 = &decltype(basic_sema::l_eval)::operator();
// expected-error@-1 {{take address}}
consteval int f(int i) {
// expected-note@-1+ {{declared here}}
return i;
}
auto ptr = &f;
// expected-error@-1 {{take address}}
auto f1() {
return &f;
// expected-error@-1 {{take address}}
}
}
namespace invalid_function {
struct A {
consteval void *operator new(size_t count);
// expected-error@-1 {{'operator new' cannot be declared consteval}}
consteval void *operator new[](size_t count);
// expected-error@-1 {{'operator new[]' cannot be declared consteval}}
consteval void operator delete(void* ptr);
// expected-error@-1 {{'operator delete' cannot be declared consteval}}
consteval void operator delete[](void* ptr);
// expected-error@-1 {{'operator delete[]' cannot be declared consteval}}
consteval ~A() {}
// expected-error@-1 {{destructor cannot be declared consteval}}
};
}
namespace nested {
consteval int f() {
return 0;
}
consteval int f1(...) {
return 1;
}
enum E {};
using T = int(&)();
consteval auto operator+ (E, int(*a)()) {
return 0;
}
void d() {
auto i = f1(E() + &f);
}
auto l0 = [](auto) consteval {
return 0;
};
int i0 = l0(&f1);
int i1 = f1(l0(4));
int i2 = f1(&f1, &f1, &f1, &f1, &f1, &f1, &f1);
int i3 = f1(f1(f1(&f1, &f1), f1(&f1, &f1), f1(f1(&f1, &f1), &f1)));
}
namespace user_defined_literal {
consteval int operator"" _test(unsigned long long i) {
// expected-note@-1+ {{declared here}}
return 0;
}
int i = 0_test;
auto ptr = &operator"" _test;
// expected-error@-1 {{take address}}
consteval auto operator"" _test1(unsigned long long i) {
return &f_eval;
}
auto i1 = 0_test1; // expected-error {{is not a constant expression}}
// expected-note@-1 {{is not a constant expression}}
}
namespace return_address {
consteval int f() {
// expected-note@-1 {{declared here}}
return 0;
}
consteval int(*ret1(int i))() {
return &f;
}
auto ptr = ret1(0);
// expected-error@-1 {{is not a constant expression}}
// expected-note@-2 {{pointer to a consteval}}
struct A {
consteval int f(int) {
// expected-note@-1+ {{declared here}}
return 0;
}
};
using mem_ptr_type = int (A::*)(int);
template<mem_ptr_type ptr>
struct C {};
C<&A::f> c;
// expected-error@-1 {{is not a constant expression}}
// expected-note@-2 {{pointer to a consteval}}
consteval mem_ptr_type ret2() {
return &A::f;
}
C<ret2()> c1;
// expected-error@-1 {{is not a constant expression}}
// expected-note@-2 {{pointer to a consteval}}
}
namespace context {
int g_i;
// expected-note@-1 {{declared here}}
consteval int f(int) {
return 0;
}
constexpr int c_i = 0;
int t1 = f(g_i);
// expected-error@-1 {{is not a constant expression}}
// expected-note@-2 {{read of non-const variable}}
int t3 = f(c_i);
constexpr int f_c(int i) {
// expected-note@-1 {{declared here}}
int t = f(i);
// expected-error@-1 {{is not a constant expression}}
// expected-note@-2 {{function parameter}}
return f(0);
}
consteval int f_eval(int i) {
return f(i);
}
auto l0 = [](int i) consteval {
return f(i);
};
auto l1 = [](int i) constexpr {
// expected-note@-1 {{declared here}}
int t = f(i);
// expected-error@-1 {{is not a constant expression}}
// expected-note@-2 {{function parameter}}
return f(0);
};
}
namespace std {
template <typename T> struct remove_reference { using type = T; };
template <typename T> struct remove_reference<T &> { using type = T; };
template <typename T> struct remove_reference<T &&> { using type = T; };
template <typename T>
constexpr typename std::remove_reference<T>::type&& move(T &&t) noexcept {
return static_cast<typename std::remove_reference<T>::type &&>(t);
}
}
namespace temporaries {
struct A {
consteval int ret_i() const { return 0; }
consteval A ret_a() const { return A{}; }
constexpr ~A() { }
};
consteval int by_value_a(A a) { return a.ret_i(); }
consteval int const_a_ref(const A &a) {
return a.ret_i();
}
consteval int rvalue_ref(const A &&a) {
return a.ret_i();
}
consteval const A &to_lvalue_ref(const A &&a) {
return a;
}
void test() {
constexpr A a {};
{ int k = A().ret_i(); }
{ A k = A().ret_a(); }
{ A k = to_lvalue_ref(A()); }// expected-error {{is not a constant expression}}
// expected-note@-1 {{is not a constant expression}} expected-note@-1 {{temporary created here}}
{ A k = to_lvalue_ref(A().ret_a()); } // expected-error {{is not a constant expression}}
// expected-note@-1 {{is not a constant expression}} expected-note@-1 {{temporary created here}}
{ int k = A().ret_a().ret_i(); }
{ int k = by_value_a(A()); }
{ int k = const_a_ref(A()); }
{ int k = const_a_ref(a); }
{ int k = rvalue_ref(A()); }
{ int k = rvalue_ref(std::move(a)); }
{ int k = const_a_ref(A().ret_a()); }
{ int k = const_a_ref(to_lvalue_ref(A().ret_a())); }
{ int k = const_a_ref(to_lvalue_ref(std::move(a))); }
{ int k = by_value_a(A().ret_a()); }
{ int k = by_value_a(to_lvalue_ref(std::move(a))); }
{ int k = (A().ret_a(), A().ret_i()); }
{ int k = (const_a_ref(A().ret_a()), A().ret_i()); }//
}
}
namespace alloc {
consteval int f() {
int *A = new int(0);
// expected-note@-1+ {{allocation performed here was not deallocated}}
return *A;
}
int i1 = f(); // expected-error {{is not a constant expression}}
struct A {
int* p = new int(42);
// expected-note@-1+ {{heap allocation performed here}}
consteval int ret_i() const { return p ? *p : 0; }
consteval A ret_a() const { return A{}; }
constexpr ~A() { delete p; }
};
consteval int by_value_a(A a) { return a.ret_i(); }
consteval int const_a_ref(const A &a) {
return a.ret_i();
}
consteval int rvalue_ref(const A &&a) {
return a.ret_i();
}
consteval const A &to_lvalue_ref(const A &&a) {
return a;
}
void test() {
constexpr A a{ nullptr };
{ int k = A().ret_i(); }
{ A k = A().ret_a(); } // expected-error {{is not a constant expression}}
// expected-note@-1 {{is not a constant expression}}
{ A k = to_lvalue_ref(A()); } // expected-error {{is not a constant expression}}
// expected-note@-1 {{is not a constant expression}} expected-note@-1 {{temporary created here}}
{ A k = to_lvalue_ref(A().ret_a()); }
// expected-error@-1 {{'alloc::A::ret_a' is not a constant expression}}
// expected-note@-2 {{heap-allocated object is not a constant expression}}
// expected-error@-3 {{'alloc::to_lvalue_ref' is not a constant expression}}
// expected-note@-4 {{reference to temporary is not a constant expression}}
// expected-note@-5 {{temporary created here}}
{ int k = A().ret_a().ret_i(); }
// expected-error@-1 {{'alloc::A::ret_a' is not a constant expression}}
// expected-note@-2 {{heap-allocated object is not a constant expression}}
{ int k = by_value_a(A()); }
{ int k = const_a_ref(A()); }
{ int k = const_a_ref(a); }
{ int k = rvalue_ref(A()); }
{ int k = rvalue_ref(std::move(a)); }
{ int k = const_a_ref(A().ret_a()); }
// expected-error@-1 {{'alloc::A::ret_a' is not a constant expression}}
// expected-note@-2 {{is not a constant expression}}
{ int k = const_a_ref(to_lvalue_ref(A().ret_a())); }
// expected-error@-1 {{'alloc::A::ret_a' is not a constant expression}}
// expected-note@-2 {{is not a constant expression}}
{ int k = const_a_ref(to_lvalue_ref(std::move(a))); }
{ int k = by_value_a(A().ret_a()); }
{ int k = by_value_a(to_lvalue_ref(static_cast<const A&&>(a))); }
{ int k = (A().ret_a(), A().ret_i()); }// expected-error {{is not a constant expression}}
// expected-note@-1 {{is not a constant expression}}
{ int k = (const_a_ref(A().ret_a()), A().ret_i()); }
// expected-error@-1 {{'alloc::A::ret_a' is not a constant expression}}
// expected-note@-2 {{is not a constant expression}}
}
}
namespace self_referencing {
struct S {
S* ptr = nullptr;
constexpr S(int i) : ptr(this) {
if (this == ptr && i)
ptr = nullptr;
}
constexpr ~S() {}
};
consteval S f(int i) {
return S(i);
}
void test() {
S s(1);
s = f(1);
s = f(0); // expected-error {{is not a constant expression}}
// expected-note@-1 {{is not a constant expression}} expected-note@-1 {{temporary created here}}
}
struct S1 {
S1* ptr = nullptr;
consteval S1(int i) : ptr(this) {
if (this == ptr && i)
ptr = nullptr;
}
constexpr ~S1() {}
};
void test1() {
S1 s(1);
s = S1(1);
s = S1(0); // expected-error {{is not a constant expression}}
// expected-note@-1 {{is not a constant expression}} expected-note@-1 {{temporary created here}}
}
}
namespace ctor {
consteval int f_eval() { // expected-note+ {{declared here}}
return 0;
}
namespace std {
struct strong_ordering {
int n;
static const strong_ordering less, equal, greater;
};
constexpr strong_ordering strong_ordering::less = {-1};
constexpr strong_ordering strong_ordering::equal = {0};
constexpr strong_ordering strong_ordering::greater = {1};
constexpr bool operator!=(strong_ordering, int);
}
namespace override {
struct A {
virtual consteval void f(); // expected-note {{overridden}}
virtual void g(); // expected-note {{overridden}}
};
struct B : A {
consteval void f();
void g();
};
struct C : A {
void f(); // expected-error {{non-consteval function 'f' cannot override a consteval function}}
consteval void g(); // expected-error {{consteval function 'g' cannot override a non-consteval function}}
};
namespace implicit_equals_1 {
struct Y;
struct X {
std::strong_ordering operator<=>(const X&) const;
constexpr bool operator==(const X&) const;
virtual consteval bool operator==(const Y&) const; // expected-note {{here}}
};
struct Y : X {
std::strong_ordering operator<=>(const Y&) const = default;
// expected-error@-1 {{non-consteval function 'operator==' cannot override a consteval function}}
};
}
namespace implicit_equals_2 {
struct Y;
struct X {
constexpr std::strong_ordering operator<=>(const X&) const;
constexpr bool operator==(const X&) const;
virtual bool operator==(const Y&) const; // expected-note {{here}}
};
struct Y : X {
consteval std::strong_ordering operator<=>(const Y&) const = default;
// expected-error@-1 {{consteval function 'operator==' cannot override a non-consteval function}}
};
}
}
namespace operator_rewrite {
struct A {
friend consteval int operator<=>(const A&, const A&) { return 0; }
};
const bool k = A() < A();
static_assert(!k);
A a;
bool k2 = A() < a; // OK, does not access 'a'.
struct B {
friend consteval int operator<=>(const B &l, const B &r) { return r.n - l.n; } // expected-note {{read of }}
int n;
};
static_assert(B() >= B());
B b; // expected-note {{here}}
bool k3 = B() < b; // expected-error-re {{call to consteval function '{{.*}}::operator<=>' is not a constant expression}} expected-note {{in call}}
}
struct A {
int(*ptr)();
consteval A(int(*p)() = nullptr) : ptr(p) {}
};
struct B {
int(*ptr)();
B() : ptr(nullptr) {}
consteval B(int(*p)(), int) : ptr(p) {}
};
void test() {
{ A a; }
{ A a(&f_eval); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ B b(nullptr, 0); }
{ B b(&f_eval, 0); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ A a{}; }
{ A a{&f_eval}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ B b{nullptr, 0}; }
{ B b{&f_eval, 0}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ A a = A(); }
{ A a = A(&f_eval); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ B b = B(nullptr, 0); }
{ B b = B(&f_eval, 0); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ A a = A{}; }
{ A a = A{&f_eval}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ B b = B{nullptr, 0}; }
{ B b = B{&f_eval, 0}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ A a; a = A(); }
{ A a; a = A(&f_eval); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ B b; b = B(nullptr, 0); }
{ B b; b = B(&f_eval, 0); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ A a; a = A{}; }
{ A a; a = A{&f_eval}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ B b; b = B{nullptr, 0}; }
{ B b; b = B{&f_eval, 0}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ A* a; a = new A(); }
{ A* a; a = new A(&f_eval); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ B* b; b = new B(nullptr, 0); }
{ B* b; b = new B(&f_eval, 0); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ A* a; a = new A{}; }
{ A* a; a = new A{&f_eval}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ B* b; b = new B{nullptr, 0}; }
{ B* b; b = new B{&f_eval, 0}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
}
}
namespace copy_ctor {
consteval int f_eval() { // expected-note+ {{declared here}}
return 0;
}
struct Copy {
int(*ptr)();
constexpr Copy(int(*p)() = nullptr) : ptr(p) {}
consteval Copy(const Copy&) = default;
};
constexpr const Copy &to_lvalue_ref(const Copy &&a) {
return a;
}
void test() {
constexpr const Copy C;
// there is no the copy constructor call when its argument is a prvalue because of garanteed copy elision.
// so we need to test with both prvalue and xvalues.
{ Copy c(C); }
{ Copy c((Copy(&f_eval))); }// expected-error {{cannot take address of consteval}}
{ Copy c(std::move(C)); }
{ Copy c(std::move(Copy(&f_eval))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ Copy c(to_lvalue_ref((Copy(&f_eval)))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ Copy c(to_lvalue_ref(std::move(C))); }
{ Copy c(to_lvalue_ref(std::move(Copy(&f_eval)))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ Copy c = Copy(C); }
{ Copy c = Copy(Copy(&f_eval)); }// expected-error {{cannot take address of consteval}}
{ Copy c = Copy(std::move(C)); }
{ Copy c = Copy(std::move(Copy(&f_eval))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ Copy c = Copy(to_lvalue_ref(Copy(&f_eval))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ Copy c = Copy(to_lvalue_ref(std::move(C))); }
{ Copy c = Copy(to_lvalue_ref(std::move(Copy(&f_eval)))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ Copy c; c = Copy(C); }
{ Copy c; c = Copy(Copy(&f_eval)); }// expected-error {{cannot take address of consteval}}
{ Copy c; c = Copy(std::move(C)); }
{ Copy c; c = Copy(std::move(Copy(&f_eval))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ Copy c; c = Copy(to_lvalue_ref(Copy(&f_eval))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ Copy c; c = Copy(to_lvalue_ref(std::move(C))); }
{ Copy c; c = Copy(to_lvalue_ref(std::move(Copy(&f_eval)))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ Copy* c; c = new Copy(C); }
{ Copy* c; c = new Copy(Copy(&f_eval)); }// expected-error {{cannot take address of consteval}}
{ Copy* c; c = new Copy(std::move(C)); }
{ Copy* c; c = new Copy(std::move(Copy(&f_eval))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ Copy* c; c = new Copy(to_lvalue_ref(Copy(&f_eval))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
{ Copy* c; c = new Copy(to_lvalue_ref(std::move(C))); }
{ Copy* c; c = new Copy(to_lvalue_ref(std::move(Copy(&f_eval)))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
}
} // namespace special_ctor
namespace unevaluated {
template <typename T, typename U> struct is_same { static const bool value = false; };
template <typename T> struct is_same<T, T> { static const bool value = true; };
long f(); // expected-note {{declared here}}
auto consteval g(auto a) {
return a;
}
auto e = g(f()); // expected-error {{is not a constant expression}}
// expected-note@-1 {{non-constexpr function 'f' cannot be used in a constant expression}}
using T = decltype(g(f()));
static_assert(is_same<long, T>::value);
} // namespace unevaluated
namespace value_dependent {
consteval int foo(int x) {
return x;
}
template <int X> constexpr int bar() {
// Previously this call was rejected as value-dependent constant expressions
// can't be immediately evaluated. Now we show that we don't immediately
// evaluate them until they are instantiated.
return foo(X);
}
template <typename T> constexpr int baz() {
constexpr int t = sizeof(T);
// Previously this call was rejected as `t` is value-dependent and its value
// is unknown until the function is instantiated. Now we show that we don't
// reject such calls.
return foo(t);
}
static_assert(bar<15>() == 15);
static_assert(baz<int>() == sizeof(int));
} // namespace value_dependent
namespace PR50779 {
struct derp {
int b = 0;
};
constexpr derp d;
struct test {
consteval int operator[](int i) const { return {}; }
consteval const derp * operator->() const { return &d; }
consteval int f() const { return 12; } // expected-note 2{{declared here}}
};
constexpr test a;
// We previously rejected both of these overloaded operators as taking the
// address of a consteval function outside of an immediate context, but we
// accepted direct calls to the overloaded operator. Now we show that we accept
// both forms.
constexpr int s = a.operator[](1);
constexpr int t = a[1];
constexpr int u = a.operator->()->b;
constexpr int v = a->b;
// FIXME: I believe this case should work, but we currently reject.
constexpr int w = (a.*&test::f)(); // expected-error {{cannot take address of consteval function 'f' outside of an immediate invocation}}
constexpr int x = a.f();
// Show that we reject when not in an immediate context.
int w2 = (a.*&test::f)(); // expected-error {{cannot take address of consteval function 'f' outside of an immediate invocation}}
}
namespace PR48235 {
consteval int d() {
return 1;
}
struct A {
consteval int a() const { return 1; }
void b() {
this->a() + d(); // expected-error {{call to consteval function 'PR48235::A::a' is not a constant expression}} \
// expected-note {{use of 'this' pointer is only allowed within the evaluation of a call to a 'constexpr' member function}}
}
void c() {
a() + d(); // expected-error {{call to consteval function 'PR48235::A::a' is not a constant expression}} \
// expected-note {{use of 'this' pointer is only allowed within the evaluation of a call to a 'constexpr' member function}}
}
};
} // PR48235
namespace NamespaceScopeConsteval {
struct S {
int Val; // expected-note {{subobject declared here}}
consteval S() {}
};
S s1; // expected-error {{call to consteval function 'NamespaceScopeConsteval::S::S' is not a constant expression}} \
expected-note {{subobject of type 'int' is not initialized}}
template <typename Ty>
struct T {
Ty Val; // expected-note {{subobject declared here}}
consteval T() {}
};
T<int> t; // expected-error {{call to consteval function 'NamespaceScopeConsteval::T<int>::T' is not a constant expression}} \
expected-note {{subobject of type 'int' is not initialized}}
} // namespace NamespaceScopeConsteval
namespace Issue54578 {
// We expect the user-defined literal to be resovled entirely at compile time
// despite being instantiated through a template.
inline consteval unsigned char operator""_UC(const unsigned long long n) {
return static_cast<unsigned char>(n);
}
inline constexpr char f1(const auto octet) {
return 4_UC;
}
template <typename Ty>
inline constexpr char f2(const Ty octet) {
return 4_UC;
}
void test() {
static_assert(f1('a') == 4);
static_assert(f2('a') == 4);
constexpr int c = f1('a') + f2('a');
static_assert(c == 8);
}
}