// PR c++/106649
// P2448 - Relaxing some constexpr restrictions
// { dg-do compile { target c++23 } }
// { dg-options "-Winvalid-constexpr -pedantic-errors" }

// No constexpr constructors = not a literal type.
struct NonLiteral {
  NonLiteral() {}
};

// C++23: It is possible to write a constexpr function for which no
// invocation satisfies the requirements of a core constant expression.
constexpr NonLiteral
fn0 (int) // { dg-warning "invalid return type" "" { target { ! implicit_constexpr } } }
{
  return NonLiteral{};
}

constexpr int
fn1 (NonLiteral) // { dg-warning "invalid type" "" { target { ! implicit_constexpr } } }
{
  return 42;
}

// From P2448.
void f(int& i) {
    i = 0;
}

constexpr void g(int& i) {
    f(i); // { dg-warning "call to" }
}

// [dcl.constexpr] used to have this.
constexpr int f(bool b)
  { return b ? throw 0 : 0; }           // OK
constexpr int f() { return f(true); }   // ill-formed, no diagnostic required

struct B {
  constexpr B(int) : i(0) { }
  int i;
};

int global;

struct D : B {
  constexpr D() : B(global) { } // { dg-warning "not usable" }
  // ill-formed, no diagnostic required
  // lvalue-to-rvalue conversion on non-constant global
};

// If no specialization of the template would satisfy the requirements
// for a constexpr function when considered as a non-template function,
// the template is ill-formed, no diagnostic required.
template<typename>
constexpr void
fn2 ()
{
  int i = 42;
  f (i);
}

void
fn3 ()
{
  fn2<int>();
}

constexpr volatile int cvi = 10;

constexpr int
fn4 ()
{
  return cvi;  // { dg-warning "lvalue-to-rvalue conversion" }
}

constexpr unsigned int
fn5 (int *p)
{
  unsigned int *q = reinterpret_cast<unsigned int *>(p); // { dg-warning "reinterpret_cast" }
  return *q;
}

constexpr int
fn6 (int i)
{
  void *p = (void *) 1LL; // { dg-warning ".reinterpret_cast. from integer to pointer" }
  return 42;
}

constexpr int
fn7 (int i)
{
  static int s = i; // { dg-warning "static" }
  return s;
}