mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 21:36:05 +00:00

As per https://eel.is/c++draft/expr.const#17 Fixes #91308 --------- Co-authored-by: Mariya Podchishchaeva <mariya.podchishchaeva@intel.com>
457 lines
12 KiB
C++
457 lines
12 KiB
C++
// RUN: %clang_cc1 -std=c++2a -Wno-unused-value %s -verify
|
|
// RUN: %clang_cc1 -std=c++2b -Wno-unused-value %s -verify
|
|
|
|
consteval int id(int i) { return i; }
|
|
constexpr char id(char c) { return c; }
|
|
|
|
template <typename T>
|
|
constexpr int f(T t) { // expected-note {{declared here}}
|
|
return t + id(t); // expected-note 2{{'f<int>' is an immediate function because its body contains a call to a consteval function 'id' and that call is not a constant expression}}
|
|
}
|
|
|
|
namespace examples {
|
|
|
|
auto a = &f<char>; // ok, f<char> is not an immediate function
|
|
auto b = &f<int>; // expected-error {{cannot take address of immediate function 'f<int>' outside of an immediate invocation}}
|
|
|
|
static_assert(f(3) == 6); // ok
|
|
|
|
template <typename T>
|
|
constexpr int g(T t) { // g<int> is not an immediate function
|
|
return t + id(42); // because id(42) is already a constant
|
|
}
|
|
|
|
template <typename T, typename F>
|
|
constexpr bool is_not(T t, F f) {
|
|
return not f(t);
|
|
}
|
|
|
|
consteval bool is_even(int i) { return i % 2 == 0; }
|
|
|
|
static_assert(is_not(5, is_even));
|
|
|
|
int x = 0; // expected-note {{declared here}}
|
|
|
|
template <typename T>
|
|
constexpr T h(T t = id(x)) { // expected-note {{read of non-const variable 'x' is not allowed in a constant expression}} \
|
|
// expected-note {{'hh<int>' is an immediate function because its body contains a call to a consteval function 'id' and that call is not a constant expression}}
|
|
return t;
|
|
}
|
|
|
|
template <typename T>
|
|
constexpr T hh() { // hh<int> is an immediate function
|
|
[[maybe_unused]] auto x = h<T>();
|
|
return h<T>();
|
|
}
|
|
|
|
int i = hh<int>(); // expected-error {{call to immediate function 'examples::hh<int>' is not a constant expression}} \
|
|
// expected-note {{in call to 'hh<int>()'}}
|
|
|
|
struct A {
|
|
int x;
|
|
int y = id(x);
|
|
};
|
|
|
|
template <typename T>
|
|
constexpr int k(int) {
|
|
return A(42).y;
|
|
}
|
|
|
|
}
|
|
|
|
namespace nested {
|
|
|
|
template <typename T>
|
|
constexpr int fdupe(T t) {
|
|
return id(t);
|
|
}
|
|
|
|
struct a {
|
|
constexpr a(int) { }
|
|
};
|
|
|
|
a aa(fdupe<int>((f<int>(7))));
|
|
|
|
template <typename T>
|
|
constexpr int foo(T t); // expected-note {{declared here}}
|
|
|
|
a bb(f<int>(foo<int>(7))); // expected-error{{call to immediate function 'f<int>' is not a constant expression}} \
|
|
// expected-note{{undefined function 'foo<int>' cannot be used in a constant expression}}
|
|
|
|
}
|
|
|
|
namespace e2{
|
|
template <typename T>
|
|
constexpr int f(T t);
|
|
auto a = &f<char>;
|
|
auto b = &f<int>;
|
|
}
|
|
|
|
namespace forward_declare_constexpr{
|
|
template <typename T>
|
|
constexpr int f(T t);
|
|
|
|
auto a = &f<char>;
|
|
auto b = &f<int>;
|
|
|
|
template <typename T>
|
|
constexpr int f(T t) {
|
|
return id(0);
|
|
}
|
|
}
|
|
|
|
namespace forward_declare_consteval{
|
|
template <typename T>
|
|
constexpr int f(T t);
|
|
|
|
auto a = &f<char>;
|
|
auto b = &f<int>; // expected-error {{immediate function 'f<int>' used before it is defined}} \
|
|
// expected-note {{in instantiation of function template specialization}}
|
|
|
|
template <typename T>
|
|
constexpr int f(T t) { // expected-note {{'f<int>' defined here}}
|
|
return id(t); // expected-note {{'f<int>' is an immediate function because its body contains a call to a consteval function 'id' and that call is not a constant expression}}
|
|
}
|
|
}
|
|
|
|
namespace constructors {
|
|
consteval int f(int) {
|
|
return 0;
|
|
}
|
|
struct S {
|
|
constexpr S(auto i) {
|
|
f(i);
|
|
}
|
|
};
|
|
constexpr void g(auto i) {
|
|
[[maybe_unused]] S s{i};
|
|
}
|
|
void test() {
|
|
g(0);
|
|
}
|
|
}
|
|
|
|
namespace aggregate {
|
|
consteval int f(int);
|
|
struct S{
|
|
int a = 0;
|
|
int b = f(a);
|
|
};
|
|
|
|
constexpr bool test(auto i) {
|
|
S s{i};
|
|
return s.b == 2 *i;
|
|
}
|
|
consteval int f(int i) {
|
|
return 2 * i;
|
|
}
|
|
|
|
void test() {
|
|
static_assert(test(42));
|
|
}
|
|
|
|
}
|
|
|
|
namespace ConstevalConstructor{
|
|
int x = 0; // expected-note {{declared here}}
|
|
struct S {
|
|
consteval S(int) {};
|
|
};
|
|
constexpr int g(auto t) {
|
|
S s(t); // expected-note {{'g<int>' is an immediate function because its body contains a call to a consteval constructor 'S' and that call is not a constant expression}}
|
|
return 0;
|
|
}
|
|
int i = g(x); // expected-error {{call to immediate function 'ConstevalConstructor::g<int>' is not a constant expression}} \
|
|
// expected-note {{read of non-const variable 'x' is not allowed in a constant expression}}
|
|
}
|
|
|
|
|
|
|
|
namespace Aggregate {
|
|
consteval int f(int); // expected-note {{declared here}}
|
|
struct S {
|
|
int x = f(42); // expected-note {{undefined function 'f' cannot be used in a constant expression}} \
|
|
// expected-note {{'immediate<int>' is an immediate function because its body contains a call to a consteval function 'f' and that call is not a constant expression}}
|
|
};
|
|
|
|
constexpr S immediate(auto) {
|
|
return S{};
|
|
}
|
|
|
|
void test_runtime() {
|
|
(void)immediate(0); // expected-error {{call to immediate function 'Aggregate::immediate<int>' is not a constant expression}} \
|
|
// expected-note {{in call to 'immediate<int>(0)'}}
|
|
}
|
|
consteval int f(int i) {
|
|
return i;
|
|
}
|
|
consteval void test() {
|
|
constexpr S s = immediate(0);
|
|
static_assert(s.x == 42);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
namespace GH63742 {
|
|
void side_effect(); // expected-note {{declared here}}
|
|
consteval int f(int x) {
|
|
if (!x) side_effect(); // expected-note {{non-constexpr function 'side_effect' cannot be used in a constant expression}}
|
|
return x;
|
|
}
|
|
struct SS {
|
|
int y = f(1); // Ok
|
|
int x = f(0); // expected-error {{call to consteval function 'GH63742::f' is not a constant expression}} \
|
|
// expected-note {{declared here}} \
|
|
// expected-note {{in call to 'f(0)'}}
|
|
SS();
|
|
};
|
|
SS::SS(){} // expected-note {{in the default initializer of 'x'}}
|
|
|
|
consteval int f2(int x) {
|
|
if (!__builtin_is_constant_evaluated()) side_effect();
|
|
return x;
|
|
}
|
|
struct S2 {
|
|
int x = f2(0);
|
|
constexpr S2();
|
|
};
|
|
|
|
constexpr S2::S2(){}
|
|
S2 s = {};
|
|
constinit S2 s2 = {};
|
|
|
|
struct S3 {
|
|
int x = f2(0);
|
|
S3();
|
|
};
|
|
S3::S3(){}
|
|
|
|
}
|
|
|
|
namespace Defaulted {
|
|
consteval int f(int x);
|
|
struct SS {
|
|
int x = f(0);
|
|
SS() = default;
|
|
};
|
|
}
|
|
|
|
namespace DefaultedUse{
|
|
consteval int f(int x); // expected-note {{declared here}}
|
|
struct SS {
|
|
int a = sizeof(f(0)); // Ok
|
|
int x = f(0); // expected-note {{undefined function 'f' cannot be used in a constant expression}}
|
|
|
|
SS() = default; // expected-note {{'SS' is an immediate constructor because the default initializer of 'x' contains a call to a consteval function 'f' and that call is not a constant expression}}
|
|
};
|
|
|
|
void test() {
|
|
[[maybe_unused]] SS s; // expected-error {{call to immediate function 'DefaultedUse::SS::SS' is not a constant expression}} \
|
|
// expected-note {{in call to 'SS()'}}
|
|
}
|
|
}
|
|
|
|
namespace UserDefinedConstructors {
|
|
consteval int f(int x) {
|
|
return x;
|
|
}
|
|
extern int NonConst; // expected-note 2{{declared here}}
|
|
|
|
struct ConstevalCtr {
|
|
int y;
|
|
int x = f(y);
|
|
consteval ConstevalCtr(int yy)
|
|
: y(f(yy)) {}
|
|
};
|
|
|
|
ConstevalCtr c1(1);
|
|
ConstevalCtr c2(NonConst);
|
|
// expected-error@-1 {{call to consteval function 'UserDefinedConstructors::ConstevalCtr::ConstevalCtr' is not a constant expression}} \
|
|
// expected-note@-1 {{read of non-const variable 'NonConst' is not allowed in a constant expression}}
|
|
|
|
struct ImmediateEscalating {
|
|
int y;
|
|
int x = f(y);
|
|
template<typename T>
|
|
constexpr ImmediateEscalating(T yy) // expected-note {{ImmediateEscalating<int>' is an immediate constructor because the initializer of 'y' contains a call to a consteval function 'f' and that call is not a constant expression}}
|
|
: y(f(yy)) {}
|
|
};
|
|
|
|
ImmediateEscalating c3(1);
|
|
ImmediateEscalating c4(NonConst);
|
|
// expected-error@-1 {{call to immediate function 'UserDefinedConstructors::ImmediateEscalating::ImmediateEscalating<int>' is not a constant expression}} \
|
|
// expected-note@-1 {{read of non-const variable 'NonConst' is not allowed in a constant expression}}
|
|
|
|
|
|
struct NonEscalating {
|
|
int y;
|
|
int x = f(this->y); // expected-error {{call to consteval function 'UserDefinedConstructors::f' is not a constant expression}} \
|
|
// expected-note {{declared here}} \
|
|
// expected-note {{use of 'this' pointer is only allowed within the evaluation of a call to a 'constexpr' member function}}
|
|
constexpr NonEscalating(int yy) : y(yy) {} // expected-note {{in the default initializer of 'x'}}
|
|
};
|
|
NonEscalating s = {1};
|
|
|
|
}
|
|
|
|
namespace AggregateInit {
|
|
|
|
consteval int f(int x) {
|
|
return x;
|
|
}
|
|
|
|
struct S {
|
|
int i;
|
|
int j = f(i);
|
|
};
|
|
|
|
constexpr S test(auto) {
|
|
return {};
|
|
}
|
|
|
|
S s = test(0);
|
|
|
|
}
|
|
|
|
namespace GlobalAggregateInit {
|
|
|
|
consteval int f(int x) {
|
|
return x;
|
|
}
|
|
|
|
struct S {
|
|
int i;
|
|
int j = f(i); // expected-error {{call to consteval function 'GlobalAggregateInit::f' is not a constant expression}} \
|
|
// expected-note {{implicit use of 'this' pointer is only allowed within the evaluation of a call to a 'constexpr' member function}} \
|
|
// expected-note {{declared here}}
|
|
};
|
|
|
|
S s(0); // expected-note {{in the default initializer of 'j'}}
|
|
|
|
}
|
|
|
|
namespace GH65985 {
|
|
consteval int invalid(); // expected-note 2{{declared here}}
|
|
constexpr int escalating(auto) {
|
|
return invalid();
|
|
// expected-note@-1 {{'escalating<int>' is an immediate function because its body contains a call to a consteval function 'invalid' and that call is not a constant expression}}
|
|
// expected-note@-2 2{{undefined function 'invalid' cannot be used in a constant expression}}
|
|
}
|
|
struct S {
|
|
static constexpr int a = escalating(0); // expected-note 2{{in call to}}
|
|
// expected-error@-1 {{call to immediate function 'GH65985::escalating<int>' is not a constant expression}}
|
|
// expected-error@-2 {{constexpr variable 'a' must be initialized by a constant expression}}
|
|
};
|
|
|
|
}
|
|
|
|
namespace GH66324 {
|
|
|
|
consteval int allocate(); // expected-note 2{{declared here}}
|
|
|
|
struct _Vector_base {
|
|
int b = allocate(); // expected-note 2{{undefined function 'allocate' cannot be used in a constant expression}} \
|
|
// expected-error {{call to consteval function 'GH66324::allocate' is not a constant expression}} \
|
|
// expected-note {{declared here}}
|
|
};
|
|
|
|
template <typename>
|
|
struct vector : _Vector_base {
|
|
constexpr vector()
|
|
// expected-note@-1 {{'vector' is an immediate constructor because its body contains a call to a consteval function 'allocate' and that call is not a constant expression}}
|
|
: _Vector_base{} {} // expected-note {{in the default initializer of 'b'}}
|
|
};
|
|
|
|
vector<void> v{};
|
|
// expected-error@-1 {{call to immediate function 'GH66324::vector<void>::vector' is not a constant expression}}
|
|
// expected-note@-2 {{in call to 'vector()'}}
|
|
|
|
}
|
|
|
|
|
|
namespace GH82258 {
|
|
|
|
template <class R, class Pred>
|
|
constexpr auto none_of(R&& r, Pred pred) -> bool { return true; }
|
|
|
|
struct info { int value; };
|
|
consteval auto is_invalid(info i) -> bool { return false; }
|
|
constexpr info types[] = { {1}, {3}, {5}};
|
|
|
|
static_assert(none_of(
|
|
types,
|
|
+[](info i) consteval {
|
|
return is_invalid(i);
|
|
}
|
|
));
|
|
|
|
static_assert(none_of(
|
|
types,
|
|
[]{
|
|
return is_invalid;
|
|
}()
|
|
));
|
|
|
|
}
|
|
|
|
#if __cplusplus >= 202302L
|
|
namespace lvalue_to_rvalue_init_from_heap {
|
|
|
|
struct S {
|
|
int *value;
|
|
constexpr S(int v) : value(new int {v}) {} // expected-note 2 {{heap allocation performed here}}
|
|
constexpr ~S() { delete value; }
|
|
};
|
|
consteval S fn() { return S(5); }
|
|
int fn2() { return 2; } // expected-note {{declared here}}
|
|
|
|
constexpr int a = *fn().value;
|
|
constinit int b = *fn().value;
|
|
const int c = *fn().value;
|
|
int d = *fn().value;
|
|
|
|
constexpr int e = *fn().value + fn2(); // expected-error {{must be initialized by a constant expression}} \
|
|
// expected-error {{call to consteval function 'lvalue_to_rvalue_init_from_heap::fn' is not a constant expression}} \
|
|
// expected-note {{non-constexpr function 'fn2'}} \
|
|
// expected-note {{pointer to heap-allocated object}}
|
|
|
|
int f = *fn().value + fn2(); // expected-error {{call to consteval function 'lvalue_to_rvalue_init_from_heap::fn' is not a constant expression}} \
|
|
// expected-note {{pointer to heap-allocated object}}
|
|
}
|
|
#endif
|
|
|
|
|
|
#if __cplusplus >= 202302L
|
|
|
|
namespace GH91509 {
|
|
|
|
consteval int f(int) { return 0; }
|
|
|
|
template<typename T>
|
|
constexpr int g(int x) {
|
|
if consteval {
|
|
return f(x);
|
|
}
|
|
if !consteval {}
|
|
else {
|
|
return f(x);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int h(int x) {
|
|
return g<void>(x);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
namespace GH91308 {
|
|
constexpr void f(auto) {
|
|
static_assert(false);
|
|
}
|
|
using R1 = decltype(&f<int>);
|
|
}
|