// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors \
// RUN:            -Wno-variadic-macros -Wno-c11-extensions
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
// RUN: %clang_cc1 -std=c++1z -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors

#if __cplusplus < 201103L
#define static_assert(...) _Static_assert(__VA_ARGS__)
#endif

namespace dr2083 { // dr2083: partial
#if __cplusplus >= 201103L
  void non_const_mem_ptr() {
    struct A {
      int x;
      int y;
    };
    constexpr A a = {1, 2};
    struct B {
      int A::*p;
      constexpr int g() const {
        // OK, not an odr-use of 'a'.
        return a.*p;
      };
    };
    static_assert(B{&A::x}.g() == 1, "");
    static_assert(B{&A::y}.g() == 2, "");
  }
#endif

  const int a = 1;
  int b;
  // Note, references only get special odr-use / constant initializxer
  // treatment in C++11 onwards. We continue to apply that even after DR2083.
  void ref_to_non_const() {
    int c;
    const int &ra = a; // expected-note 0-1{{here}}
    int &rb = b; // expected-note 0-1{{here}}
    int &rc = c; // expected-note {{here}}
    struct A {
      int f() {
        int a = ra;
        int b = rb;
#if __cplusplus < 201103L
        // expected-error@-3 {{in enclosing function}}
        // expected-error@-3 {{in enclosing function}}
#endif
        int c = rc; // expected-error {{in enclosing function}}
        return a + b + c;
      }
    };
  }

#if __cplusplus >= 201103L
  struct NoMut1 { int a, b; };
  struct NoMut2 { NoMut1 m; };
  struct NoMut3 : NoMut1 {
    constexpr NoMut3(int a, int b) : NoMut1{a, b} {}
  };
  struct Mut1 {
    int a;
    mutable int b;
  };
  struct Mut2 { Mut1 m; };
  struct Mut3 : Mut1 {
    constexpr Mut3(int a, int b) : Mut1{a, b} {}
  };
  void mutable_subobjects() {
    constexpr NoMut1 nm1 = {1, 2};
    constexpr NoMut2 nm2 = {1, 2};
    constexpr NoMut3 nm3 = {1, 2};
    constexpr Mut1 m1 = {1, 2}; // expected-note {{declared here}}
    constexpr Mut2 m2 = {1, 2}; // expected-note {{declared here}}
    constexpr Mut3 m3 = {1, 2}; // expected-note {{declared here}}
    struct A {
      void f() {
        static_assert(nm1.a == 1, "");
        static_assert(nm2.m.a == 1, "");
        static_assert(nm3.a == 1, "");
        // Can't even access a non-mutable member of a variable containing mutable fields.
        static_assert(m1.a == 1, ""); // expected-error {{enclosing function}}
        static_assert(m2.m.a == 1, ""); // expected-error {{enclosing function}}
        static_assert(m3.a == 1, ""); // expected-error {{enclosing function}}
      }
    };
  }
#endif

  void ellipsis() {
    void ellipsis(...);
    struct A {};
    const int n = 0;
#if __cplusplus >= 201103L
    constexpr
#endif
      A a = {}; // expected-note {{here}}
    struct B {
      void f() {
        ellipsis(n);
        // Even though this is technically modelled as an lvalue-to-rvalue
        // conversion, it calls a constructor and binds 'a' to a reference, so
        // it results in an odr-use.
        ellipsis(a); // expected-error {{enclosing function}}
      }
    };
  }

#if __cplusplus >= 201103L
  void volatile_lval() {
    struct A { int n; };
    constexpr A a = {0}; // expected-note {{here}}
    struct B {
      void f() {
        // An lvalue-to-rvalue conversion of a volatile lvalue always results
        // in odr-use.
        int A::*p = &A::n;
        int x = a.*p;
        volatile int A::*q = p;
        int y = a.*q; // expected-error {{enclosing function}}
      }
    };
  }
#endif

  void discarded_lval() {
    struct A { int x; mutable int y; volatile int z; };
    A a; // expected-note 1+{{here}}
    int &r = a.x; // expected-note {{here}}
    struct B {
      void f() {
        a.x; // expected-warning {{unused}}
        a.*&A::x; // expected-warning {{unused}}
        true ? a.x : a.y; // expected-warning {{unused}}
        (void)a.x;
        a.x, discarded_lval(); // expected-warning {{unused}}
#if 1 // FIXME: These errors are all incorrect; the above code is valid.
      // expected-error@-6 {{enclosing function}}
      // expected-error@-6 {{enclosing function}}
      // expected-error@-6 2{{enclosing function}}
      // expected-error@-6 {{enclosing function}}
      // expected-error@-6 {{enclosing function}}
#endif

        // 'volatile' qualifier triggers an lvalue-to-rvalue conversion.
        a.z; // expected-error {{enclosing function}}
#if __cplusplus < 201103L
        // expected-warning@-2 {{assign into a variable}}
#endif

        // References always get "loaded" to determine what they reference,
        // even if the result is discarded.
        r; // expected-error {{enclosing function}} expected-warning {{unused}}
      }
    };
  }

  namespace dr_example_1 {
    extern int globx;
    int main() {
      const int &x = globx;
      struct A {
#if __cplusplus < 201103L
        // expected-error@+2 {{enclosing function}} expected-note@-3 {{here}}
#endif
        const int *foo() { return &x; }
      } a;
      return *a.foo();
    }
  }

#if __cplusplus >= 201103L
  namespace dr_example_2 {
    struct A {
      int q;
      constexpr A(int q) : q(q) {}
      constexpr A(const A &a) : q(a.q * 2) {} // (note, not called)
    };

    int main(void) {
      constexpr A a(42);
      constexpr int aq = a.q;
      struct Q {
        int foo() { return a.q; }
      } q;
      return q.foo();
    }

    // Checking odr-use does not invent an lvalue-to-rvalue conversion (and
    // hence copy construction) on the potential result variable.
    struct B {
      int b = 42;
      constexpr B() {}
      constexpr B(const B&) = delete;
    };
    void f() {
      constexpr B b;
      struct Q {
        constexpr int foo() const { return b.b; }
      };
      static_assert(Q().foo() == 42, "");
    }
  }
#endif
}

namespace dr2094 { // dr2094: 5
  struct A { int n; };
  struct B { volatile int n; };
  static_assert(__is_trivially_copyable(volatile int), "");
  static_assert(__is_trivially_copyable(const volatile int), "");
  static_assert(__is_trivially_copyable(const volatile int[]), "");
  static_assert(__is_trivially_copyable(A), "");
  static_assert(__is_trivially_copyable(volatile A), "");
  static_assert(__is_trivially_copyable(const volatile A), "");
  static_assert(__is_trivially_copyable(const volatile A[]), "");
  static_assert(__is_trivially_copyable(B), "");

  static_assert(__is_trivially_constructible(A, A const&), "");
  static_assert(__is_trivially_constructible(B, B const&), "");

  static_assert(__is_trivially_assignable(A, const A&), "");
  static_assert(__is_trivially_assignable(B, const B&), "");
}
