// RUN: %clang_cc1 -std=c++2a -fsyntax-only -fcxx-exceptions -verify=ref,both %s
// RUN: %clang_cc1 -std=c++2a -fsyntax-only -fcxx-exceptions -verify=expected,both %s -fexperimental-new-constant-interpreter

template <unsigned N>
struct S {
  S() requires (N==1) = default;
  S() requires (N==2) {} // both-note {{declared here}}
  consteval S() requires (N==3) = default;
};

consteval int aConstevalFunction() { // both-error {{consteval function never produces a constant expression}}
  S<2> s4; // both-note {{non-constexpr constructor 'S' cannot be used in a constant expression}}
  return 0;
}
/// We're NOT calling the above function. The diagnostics should appear anyway.

namespace Covariant {
  struct A {
    virtual constexpr char f() const { return 'Z'; }
    char a = f();
  };

  struct D : A {};
  struct Covariant1 {
    D d;
    virtual const A *f() const;
  };

  struct Covariant3 : Covariant1 {
    constexpr virtual const D *f() const { return &this->d; }
  };

  constexpr Covariant3 cb;
  constexpr const Covariant1 *cb1 = &cb;
  static_assert(cb1->f()->a == 'Z');
}

namespace DtorOrder {
  struct Buf {
    char buf[64];
    int n = 0;
    constexpr void operator+=(char c) { buf[n++] = c; }
    constexpr bool operator==(const char *str) const {
      if (str[n] != 0)
        return false;

      for (int i = 0; i < n; ++i) {
        if (buf[n] != str[n])
          return false;
      }
      return true;

      return __builtin_memcmp(str, buf, n) == 0;
    }
    constexpr bool operator!=(const char *str) const { return !operator==(str); }
  };

  struct A {
    constexpr A(Buf &buf, char c) : buf(buf), c(c) { buf += c; }
    constexpr ~A() { buf += (c - 32);}
    constexpr operator bool() const { return true; }
    Buf &buf;
    char c;
  };

  constexpr void abnormal_termination(Buf &buf) {
    struct Indestructible {
      constexpr ~Indestructible(); // not defined
    };
    A a(buf, 'a');
    A(buf, 'b');
    int n = 0;

    for (A &&c = A(buf, 'c'); A d = A(buf, 'd'); A(buf, 'e')) {
      switch (A f(buf, 'f'); A g = A(buf, 'g')) { // both-warning {{boolean}}
      case false: {
        A x(buf, 'x');
      }

      case true: {
        A h(buf, 'h');
        switch (n++) {
        case 0:
          break;
        case 1:
          continue;
        case 2:
          return;
        }
        break;
      }

      default:
        Indestructible indest;
      }

      A j = (A(buf, 'i'), A(buf, 'j'));
    }
  }

  constexpr bool check_abnormal_termination() {
    Buf buf = {};
    abnormal_termination(buf);
    return buf ==
      "abBc"
        "dfgh" /*break*/ "HGFijIJeED"
        "dfgh" /*continue*/ "HGFeED"
        "dfgh" /*return*/ "HGFD"
      "CA";
  }
  static_assert(check_abnormal_termination());
}

namespace std {
  struct type_info;
}

namespace TypeId {
  struct A {
    const std::type_info &ti = typeid(*this);
  };
  struct A2 : A {};
  static_assert(&A().ti == &typeid(A));
  static_assert(&typeid((A2())) == &typeid(A2));
  extern A2 extern_a2;
  static_assert(&typeid(extern_a2) == &typeid(A2));

  constexpr A2 a2;
  constexpr const A &a1 = a2;
  static_assert(&typeid(a1) == &typeid(A));

  struct B {
    virtual void f();
    const std::type_info &ti1 = typeid(*this);
  };
  struct B2 : B {
    const std::type_info &ti2 = typeid(*this);
  };
  static_assert(&B2().ti1 == &typeid(B));
  static_assert(&B2().ti2 == &typeid(B2));
  extern B2 extern_b2;
  static_assert(&typeid(extern_b2) == &typeid(B2));

  constexpr B2 b2;
  constexpr const B &b1 = b2;
  static_assert(&typeid(b1) == &typeid(B2));

  constexpr bool side_effects() {
    // Not polymorphic nor a glvalue.
    bool OK = true;
    (void)typeid(OK = false, A2()); // both-warning {{has no effect}}
    if (!OK) return false;

    // Not polymorphic.
    A2 a2;
    (void)typeid(OK = false, a2); // both-warning {{has no effect}}
    if (!OK) return false;

    // Not a glvalue.
    (void)typeid(OK = false, B2()); // both-warning {{has no effect}}
    if (!OK) return false;

    // Polymorphic glvalue: operand evaluated.
    OK = false;
    B2 b2;
    (void)typeid(OK = true, b2); // both-warning {{will be evaluated}}
    return OK;
  }
  static_assert(side_effects());
}

consteval int f(int i);
constexpr bool test(auto i) {
    return f(0) == 0;
}
consteval int f(int i) {
    return 2 * i;
}
static_assert(test(42));

namespace PureVirtual {
  struct Abstract {
    constexpr virtual void f() = 0; // both-note {{declared here}}
    constexpr Abstract() { do_it(); } // both-note {{in call to}}
    constexpr void do_it() { f(); } // both-note {{pure virtual function 'PureVirtual::Abstract::f' called}}
  };
  struct PureVirtualCall : Abstract { void f(); }; // both-note {{in call to 'Abstract}}
  constexpr PureVirtualCall pure_virtual_call; // both-error {{constant expression}} both-note {{in call to 'PureVirtualCall}}
}
