llvm/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp

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

namespace GH94935 {

consteval void f(int) {}
consteval void undef(int); // expected-note {{declared here}}

template<typename T>
struct G {
    void g() {
        GH94935::f(T::fn());
        GH94935::f(T::undef2());  // expected-error {{call to consteval function 'GH94935::f' is not a constant expression}} \
                                  // expected-note  {{undefined function 'undef2' cannot be used in a constant expression}}
        GH94935::undef(T::fn());  // expected-error {{call to consteval function 'GH94935::undef' is not a constant expression}} \
                                  // expected-note  {{undefined function 'undef' cannot be used in a constant expression}}
    }
};

struct X {
    static consteval int fn() { return 0; }
    static consteval int undef2();  // expected-note {{declared here}}

};

void test() {
    G<X>{}.g(); // expected-note {{instantiation}}
}


template<typename T>
void g() {
    auto l = []{
        ::f(T::fn());
    };
}

struct Y {
    static int fn();
};

template void g<Y>();

}