// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s

// Tests for implicit (non-)declaration of move constructor and
// assignment: p9, p11, p20, p23.

// This class, used as a member, allows to distinguish move from copy because
// move operations are no-throw, copy operations aren't.
struct ThrowingCopy {
  ThrowingCopy() noexcept;
  ThrowingCopy(ThrowingCopy &&) noexcept;
  ThrowingCopy(const ThrowingCopy &) noexcept(false);
  ThrowingCopy & operator =(ThrowingCopy &&) noexcept;
  ThrowingCopy & operator =(const ThrowingCopy &) noexcept(false);
};

struct HasCopyConstructor {
  ThrowingCopy tc;
  HasCopyConstructor() noexcept;
  HasCopyConstructor(const HasCopyConstructor &) noexcept(false);
};

struct HasCopyAssignment {
  ThrowingCopy tc;
  HasCopyAssignment() noexcept;
  HasCopyAssignment & operator =(const HasCopyAssignment &) noexcept(false);
};

struct HasMoveConstructor {
  ThrowingCopy tc;
  HasMoveConstructor() noexcept;
  HasMoveConstructor(HasMoveConstructor &&) noexcept; // expected-note {{copy assignment operator is implicitly deleted because 'HasMoveConstructor' has a user-declared move constructor}}
};

struct HasMoveAssignment { // expected-note {{implicit copy constructor}}
  ThrowingCopy tc;
  HasMoveAssignment() noexcept;
  HasMoveAssignment & operator =(HasMoveAssignment &&) noexcept;
};

struct HasDestructor {
  ThrowingCopy tc;
  HasDestructor() noexcept;
  ~HasDestructor() noexcept;
};

void test_basic_exclusion() {
  static_assert(!noexcept(HasCopyConstructor((HasCopyConstructor()))), "");
  HasCopyConstructor hcc;
  static_assert(!noexcept(hcc = HasCopyConstructor()), "");

  static_assert(!noexcept(HasCopyAssignment((HasCopyAssignment()))), "");
  HasCopyAssignment hca;
  static_assert(!noexcept(hca = HasCopyAssignment()), "");

  static_assert(noexcept(HasMoveConstructor((HasMoveConstructor()))), "");
  HasMoveConstructor hmc;
  hmc = HasMoveConstructor(); // expected-error {{object of type 'HasMoveConstructor' cannot be assigned because its copy assignment operator is implicitly deleted}}

  (HasMoveAssignment(HasMoveAssignment())); // expected-error {{uses deleted function}}
  HasMoveAssignment hma;
  static_assert(noexcept(hma = HasMoveAssignment()), "");

  static_assert(!noexcept(HasDestructor((HasDestructor()))), "");
  HasDestructor hd;
  static_assert(!noexcept(hd = HasDestructor()), "");
}

struct PrivateMove {
  PrivateMove() noexcept;
  PrivateMove(const PrivateMove &) noexcept(false);
  PrivateMove & operator =(const PrivateMove &) noexcept(false);
private:
  PrivateMove(PrivateMove &&) noexcept;
  PrivateMove & operator =(PrivateMove &&) noexcept;
};

struct InheritsPrivateMove : PrivateMove {};
struct ContainsPrivateMove {
  PrivateMove pm;
};

struct PrivateDestructor {
  PrivateDestructor() noexcept;
  PrivateDestructor(const PrivateDestructor &) noexcept(false);
  PrivateDestructor(PrivateDestructor &&) noexcept;
private:
  ~PrivateDestructor() noexcept;
};

struct InheritsPrivateDestructor : PrivateDestructor {}; // expected-note{{base class 'PrivateDestructor' has an inaccessible destructor}}
struct ContainsPrivateDestructor {
  PrivateDestructor pd; // expected-note{{field 'pd' has an inaccessible destructor}}
};

struct NonTrivialCopyOnly {
  NonTrivialCopyOnly() noexcept;
  NonTrivialCopyOnly(const NonTrivialCopyOnly &) noexcept(false);
  NonTrivialCopyOnly & operator =(const NonTrivialCopyOnly &) noexcept(false);
};

struct InheritsNonTrivialCopyOnly : NonTrivialCopyOnly {};
struct ContainsNonTrivialCopyOnly {
  NonTrivialCopyOnly ntco;
};

struct ContainsConst {
  const int i;
  ContainsConst() noexcept;
  ContainsConst & operator =(ContainsConst &); // expected-note {{not viable}}
};

struct ContainsRef {
  int &i;
  ContainsRef() noexcept;
  ContainsRef & operator =(ContainsRef &); // expected-note {{not viable}}
};

struct Base {
  Base & operator =(Base &);
};
struct DirectVirtualBase : virtual Base {}; // expected-note {{copy assignment operator) not viable}}
struct IndirectVirtualBase : DirectVirtualBase {}; // expected-note {{copy assignment operator) not viable}}

void test_deletion_exclusion() {
  // FIXME: How to test the union thing?

  static_assert(!noexcept(InheritsPrivateMove(InheritsPrivateMove())), "");
  static_assert(!noexcept(ContainsPrivateMove(ContainsPrivateMove())), "");
  InheritsPrivateMove ipm;
  static_assert(!noexcept(ipm = InheritsPrivateMove()), "");
  ContainsPrivateMove cpm;
  static_assert(!noexcept(cpm = ContainsPrivateMove()), "");

  (InheritsPrivateDestructor(InheritsPrivateDestructor())); // expected-error {{call to implicitly-deleted default constructor}}
  (ContainsPrivateDestructor(ContainsPrivateDestructor())); // expected-error {{call to implicitly-deleted default constructor}}

  static_assert(!noexcept(InheritsNonTrivialCopyOnly(InheritsNonTrivialCopyOnly())), "");
  static_assert(!noexcept(ContainsNonTrivialCopyOnly(ContainsNonTrivialCopyOnly())), "");
  InheritsNonTrivialCopyOnly intco;
  static_assert(!noexcept(intco = InheritsNonTrivialCopyOnly()), "");
  ContainsNonTrivialCopyOnly cntco;
  static_assert(!noexcept(cntco = ContainsNonTrivialCopyOnly()), "");

  ContainsConst cc;
  cc = ContainsConst(); // expected-error {{no viable}} 

  ContainsRef cr;
  cr = ContainsRef(); // expected-error {{no viable}} 

  DirectVirtualBase dvb;
  dvb = DirectVirtualBase(); // expected-error {{no viable}} 

  IndirectVirtualBase ivb;
  ivb = IndirectVirtualBase(); // expected-error {{no viable}} 
}

struct ContainsRValueRef {
  int&& ri;
  ContainsRValueRef() noexcept;
};

void test_contains_rref() {
  (ContainsRValueRef(ContainsRValueRef()));
}


namespace DR1402 {
  struct NonTrivialCopyCtor {
    NonTrivialCopyCtor(const NonTrivialCopyCtor &);
  };
  struct NonTrivialCopyAssign {
    NonTrivialCopyAssign &operator=(const NonTrivialCopyAssign &);
  };

  struct NonTrivialCopyCtorVBase : virtual NonTrivialCopyCtor {
    NonTrivialCopyCtorVBase(NonTrivialCopyCtorVBase &&);
    NonTrivialCopyCtorVBase &operator=(NonTrivialCopyCtorVBase &&) = default;
  };
  struct NonTrivialCopyAssignVBase : virtual NonTrivialCopyAssign {
    NonTrivialCopyAssignVBase(NonTrivialCopyAssignVBase &&);
    NonTrivialCopyAssignVBase &operator=(NonTrivialCopyAssignVBase &&) = default;
  };

  struct NonTrivialMoveAssign {
    NonTrivialMoveAssign(NonTrivialMoveAssign&&);
    NonTrivialMoveAssign &operator=(NonTrivialMoveAssign &&);
  };
  struct NonTrivialMoveAssignVBase : virtual NonTrivialMoveAssign {
    NonTrivialMoveAssignVBase(NonTrivialMoveAssignVBase &&);
    NonTrivialMoveAssignVBase &operator=(NonTrivialMoveAssignVBase &&) = default;
  };

  // DR1402: A non-movable, non-trivially-copyable class type as a subobject no
  // longer inhibits the declaration of a move operation.
  struct NoMove1 { NonTrivialCopyCtor ntcc; };
  struct NoMove2 { NonTrivialCopyAssign ntcc; };
  struct NoMove3 : NonTrivialCopyCtor {};
  struct NoMove4 : NonTrivialCopyAssign {};
  struct NoMove5 : virtual NonTrivialCopyCtor {};
  struct NoMove6 : virtual NonTrivialCopyAssign {};
  struct NoMove7 : NonTrivialCopyCtorVBase {};
  struct NoMove8 : NonTrivialCopyAssignVBase {};

  // DR1402: A non-trivially-move-assignable virtual base class no longer
  // inhibits the declaration of a move assignment (even though it might
  // move-assign the base class multiple times).
  struct NoMove9 : NonTrivialMoveAssign {};
  struct NoMove10 : virtual NonTrivialMoveAssign {};
  struct NoMove11 : NonTrivialMoveAssignVBase {};

  template<typename T> void test(T t) {
    (void)T(static_cast<T&&>(t)); // ok
    t = static_cast<T&&>(t); // ok
  }
  template void test(NoMove1);
  template void test(NoMove2);
  template void test(NoMove3);
  template void test(NoMove4);
  template void test(NoMove5);
  template void test(NoMove6);
  template void test(NoMove7);
  template void test(NoMove8);
  template void test(NoMove9);
  template void test(NoMove10);
  template void test(NoMove11);

  struct CopyOnly {
    CopyOnly(const CopyOnly&);
    CopyOnly &operator=(const CopyOnly&);
  };
  struct MoveOnly {
    MoveOnly(MoveOnly&&); // expected-note {{user-declared move}}
    MoveOnly &operator=(MoveOnly&&);
  };
  template void test(CopyOnly); // ok, copies
  template void test(MoveOnly); // ok, moves
  struct CopyAndMove { // expected-note {{implicitly deleted}}
    CopyOnly co;
    MoveOnly mo; // expected-note {{deleted copy}}
  };
  template void test(CopyAndMove); // ok, copies co, moves mo
  void test2(CopyAndMove cm) {
    (void)CopyAndMove(cm); // expected-error {{deleted}}
    cm = cm; // expected-error {{deleted}}
  }

  namespace VbaseMove {
    struct A {};
    struct B { B &operator=(B&&); };
    struct C { C &operator=(const C&); };
    struct D { B b; };

    template<typename T, unsigned I, bool NonTrivialMove = false>
    struct E : virtual T {};

    template<typename T, unsigned I>
    struct E<T, I, true> : virtual T { E &operator=(E&&); };

    template<typename T>
    struct F :
      E<T, 0>, // expected-note-re 2{{'{{[BD]}}' is a virtual base class of base class 'E<}}
      E<T, 1> {}; // expected-note-re 2{{'{{[BD]}}' is a virtual base class of base class 'E<}}

    template<typename T>
    struct G : E<T, 0, true>, E<T, 0> {};

    template<typename T>
    struct H : E<T, 0, true>, E<T, 1, true> {};

    template<typename T>
    struct I : E<T, 0>, T {};

    template<typename T>
    struct J :
      E<T, 0>, // expected-note-re 2{{'{{[BD]}}' is a virtual base class of base class 'E<}}
      virtual T {}; // expected-note-re 2{{virtual base class '{{[BD]}}' declared here}}

    template<typename T> void move(T t) { t = static_cast<T&&>(t); }
    // expected-warning-re@-1 4{{defaulted move assignment operator of {{.*}} will move assign virtual base class '{{[BD]}}' multiple times}}
    template void move(F<A>);
    template void move(F<B>); // expected-note {{in instantiation of}}
    template void move(F<C>);
    template void move(F<D>); // expected-note {{in instantiation of}}
    template void move(G<A>);
    template void move(G<B>);
    template void move(G<C>);
    template void move(G<D>);
    template void move(H<A>);
    template void move(H<B>);
    template void move(H<C>);
    template void move(H<D>);
    template void move(I<A>);
    template void move(I<B>);
    template void move(I<C>);
    template void move(I<D>);
    template void move(J<A>);
    template void move(J<B>); // expected-note {{in instantiation of}}
    template void move(J<C>);
    template void move(J<D>); // expected-note {{in instantiation of}}
  }
}

namespace PR12625 {
  struct X; // expected-note {{forward decl}}
  struct Y {
    X x; // expected-error {{incomplete}}
  } y = Y();
}
