the cases where we can't determine whether special members would be trivial
while building the class, we eagerly declare those special members. The impact
of this is bounded, since it does not trigger implicit declarations of special
members in classes which merely *use* those classes.
In order to determine whether we need to apply this rule, we also need to
eagerly declare move operations and destructors in cases where they might be
deleted. If a move operation were supposed to be deleted, it would instead
be suppressed, and we could need overload resolution to determine if we fall
back to a trivial copy operation. If a destructor were implicitly deleted,
it would cause the move constructor of any derived classes to be suppressed.
As discussed on cxx-abi-dev, C++11's selected constructor rules are also
retroactively applied as a defect resolution in C++03 mode, in order to
identify that class B has a non-trivial copy constructor (since it calls
A's constructor template, not A's copy constructor):
struct A { template<typename T> A(T &); };
struct B { mutable A a; };
llvm-svn: 169673
203 lines
8.9 KiB
C++
203 lines
8.9 KiB
C++
// RUN: %clang_cc1 -std=c++11 -verify %s
|
|
|
|
// expected-no-diagnostics
|
|
|
|
template<typename T, bool B> struct trivially_assignable_check {
|
|
static_assert(B == __has_trivial_assign(T), "");
|
|
static_assert(B == __is_trivially_assignable(T&, T), "");
|
|
static_assert(B == __is_trivially_assignable(T&, const T &), "");
|
|
static_assert(B == __is_trivially_assignable(T&, T &&), "");
|
|
static_assert(B == __is_trivially_assignable(T&&, T), "");
|
|
static_assert(B == __is_trivially_assignable(T&&, const T &), "");
|
|
static_assert(B == __is_trivially_assignable(T&&, T &&), "");
|
|
typedef void type;
|
|
};
|
|
template<typename T> using trivially_assignable =
|
|
typename trivially_assignable_check<T, true>::type;
|
|
template<typename T> using not_trivially_assignable =
|
|
typename trivially_assignable_check<T, false>::type;
|
|
|
|
struct Trivial {};
|
|
using _ = trivially_assignable<Trivial>;
|
|
|
|
// A copy/move assignment operator for class X is trivial if it is not user-provided,
|
|
struct UserProvided {
|
|
UserProvided &operator=(const UserProvided &);
|
|
};
|
|
using _ = not_trivially_assignable<UserProvided>;
|
|
|
|
// its declared parameter type is the same as if it had been implicitly
|
|
// declared,
|
|
struct NonConstCopy {
|
|
NonConstCopy &operator=(NonConstCopy &) = default;
|
|
};
|
|
using _ = not_trivially_assignable<NonConstCopy>;
|
|
|
|
// class X has no virtual functions
|
|
struct VFn {
|
|
virtual void f();
|
|
};
|
|
using _ = not_trivially_assignable<VFn>;
|
|
|
|
// and no virtual base classes
|
|
struct VBase : virtual Trivial {};
|
|
using _ = not_trivially_assignable<VBase>;
|
|
|
|
// and the assignment operator selected to copy/move each [direct subobject] is trivial
|
|
struct TemplateCtor {
|
|
template<typename T> TemplateCtor operator=(T &);
|
|
};
|
|
using _ = trivially_assignable<TemplateCtor>;
|
|
struct TemplateCtorMember {
|
|
TemplateCtor tc;
|
|
};
|
|
using _ = trivially_assignable<TemplateCtorMember>;
|
|
struct MutableTemplateCtorMember {
|
|
mutable TemplateCtor mtc;
|
|
};
|
|
static_assert(!__is_trivially_assignable(MutableTemplateCtorMember, const MutableTemplateCtorMember &), "");
|
|
static_assert(__is_trivially_assignable(MutableTemplateCtorMember, MutableTemplateCtorMember &&), "");
|
|
|
|
// Both trivial and non-trivial special members.
|
|
struct TNT {
|
|
TNT &operator=(const TNT &) = default; // trivial
|
|
TNT &operator=(TNT &); // non-trivial
|
|
|
|
TNT &operator=(TNT &&) = default; // trivial
|
|
TNT &operator=(const TNT &&); // non-trivial
|
|
};
|
|
|
|
static_assert(!__has_trivial_assign(TNT), "lie deliberately for gcc compatibility");
|
|
static_assert(__is_trivially_assignable(TNT, TNT), "");
|
|
static_assert(!__is_trivially_assignable(TNT, TNT &), "");
|
|
static_assert(__is_trivially_assignable(TNT, const TNT &), "");
|
|
static_assert(!__is_trivially_assignable(TNT, volatile TNT &), "");
|
|
static_assert(__is_trivially_assignable(TNT, TNT &&), "");
|
|
static_assert(!__is_trivially_assignable(TNT, const TNT &&), "");
|
|
static_assert(!__is_trivially_assignable(TNT, volatile TNT &&), "");
|
|
|
|
// This has only trivial special members.
|
|
struct DerivedFromTNT : TNT {};
|
|
|
|
static_assert(__has_trivial_assign(DerivedFromTNT), "");
|
|
static_assert(__is_trivially_assignable(DerivedFromTNT, DerivedFromTNT), "");
|
|
static_assert(__is_trivially_assignable(DerivedFromTNT, DerivedFromTNT &), "");
|
|
static_assert(__is_trivially_assignable(DerivedFromTNT, const DerivedFromTNT &), "");
|
|
static_assert(!__is_trivially_assignable(DerivedFromTNT, volatile DerivedFromTNT &), "");
|
|
static_assert(__is_trivially_assignable(DerivedFromTNT, DerivedFromTNT &&), "");
|
|
static_assert(__is_trivially_assignable(DerivedFromTNT, const DerivedFromTNT &&), "");
|
|
static_assert(!__is_trivially_assignable(DerivedFromTNT, volatile DerivedFromTNT &&), "");
|
|
|
|
// This has only trivial special members.
|
|
struct TNTMember {
|
|
TNT tnt;
|
|
};
|
|
|
|
static_assert(__has_trivial_assign(TNTMember), "");
|
|
static_assert(__is_trivially_assignable(TNTMember, TNTMember), "");
|
|
static_assert(__is_trivially_assignable(TNTMember, TNTMember &), "");
|
|
static_assert(__is_trivially_assignable(TNTMember, const TNTMember &), "");
|
|
static_assert(!__is_trivially_assignable(TNTMember, volatile TNTMember &), "");
|
|
static_assert(__is_trivially_assignable(TNTMember, TNTMember &&), "");
|
|
static_assert(__is_trivially_assignable(TNTMember, const TNTMember &&), "");
|
|
static_assert(!__is_trivially_assignable(TNTMember, volatile TNTMember &&), "");
|
|
|
|
struct NCCTNT : NonConstCopy, TNT {};
|
|
|
|
static_assert(!__has_trivial_assign(NCCTNT), "");
|
|
static_assert(!__is_trivially_assignable(NCCTNT, NCCTNT), "");
|
|
static_assert(!__is_trivially_assignable(NCCTNT, NCCTNT &), "");
|
|
static_assert(!__is_trivially_assignable(NCCTNT, const NCCTNT &), "");
|
|
static_assert(!__is_trivially_assignable(NCCTNT, volatile NCCTNT &), "");
|
|
static_assert(!__is_trivially_assignable(NCCTNT, NCCTNT &&), "");
|
|
static_assert(!__is_trivially_assignable(NCCTNT, const NCCTNT &&), "");
|
|
static_assert(!__is_trivially_assignable(NCCTNT, volatile NCCTNT &&), "");
|
|
|
|
struct MultipleTrivial {
|
|
// All four of these are trivial.
|
|
MultipleTrivial &operator=(const MultipleTrivial &) & = default;
|
|
MultipleTrivial &operator=(const MultipleTrivial &) && = default;
|
|
MultipleTrivial &operator=(MultipleTrivial &&) & = default;
|
|
MultipleTrivial &operator=(MultipleTrivial &&) && = default;
|
|
};
|
|
|
|
using _ = trivially_assignable<MultipleTrivial>;
|
|
|
|
struct RefQualifier {
|
|
RefQualifier &operator=(const RefQualifier &) & = default;
|
|
RefQualifier &operator=(const RefQualifier &) &&;
|
|
RefQualifier &operator=(RefQualifier &&) &;
|
|
RefQualifier &operator=(RefQualifier &&) && = default;
|
|
};
|
|
struct DerivedFromRefQualifier : RefQualifier {
|
|
// Both of these call the trivial copy operation.
|
|
DerivedFromRefQualifier &operator=(const DerivedFromRefQualifier &) & = default;
|
|
DerivedFromRefQualifier &operator=(const DerivedFromRefQualifier &) && = default;
|
|
// Both of these call the non-trivial move operation.
|
|
DerivedFromRefQualifier &operator=(DerivedFromRefQualifier &&) & = default;
|
|
DerivedFromRefQualifier &operator=(DerivedFromRefQualifier &&) && = default;
|
|
};
|
|
static_assert(__is_trivially_assignable(DerivedFromRefQualifier&, const DerivedFromRefQualifier&), "");
|
|
static_assert(__is_trivially_assignable(DerivedFromRefQualifier&&, const DerivedFromRefQualifier&), "");
|
|
static_assert(!__is_trivially_assignable(DerivedFromRefQualifier&, DerivedFromRefQualifier&&), "");
|
|
static_assert(!__is_trivially_assignable(DerivedFromRefQualifier&&, DerivedFromRefQualifier&&), "");
|
|
|
|
struct TemplateAssignNoMove {
|
|
TemplateAssignNoMove &operator=(const TemplateAssignNoMove &) = default;
|
|
template<typename T> TemplateAssignNoMove &operator=(T &&);
|
|
};
|
|
static_assert(__is_trivially_assignable(TemplateAssignNoMove, const TemplateAssignNoMove &), "");
|
|
static_assert(!__is_trivially_assignable(TemplateAssignNoMove, TemplateAssignNoMove &&), "");
|
|
|
|
struct UseTemplateAssignNoMove {
|
|
TemplateAssignNoMove tanm;
|
|
};
|
|
static_assert(__is_trivially_assignable(UseTemplateAssignNoMove, const UseTemplateAssignNoMove &), "");
|
|
static_assert(!__is_trivially_assignable(UseTemplateAssignNoMove, UseTemplateAssignNoMove &&), "");
|
|
|
|
struct TemplateAssignNoMoveSFINAE {
|
|
TemplateAssignNoMoveSFINAE &operator=(const TemplateAssignNoMoveSFINAE &) = default;
|
|
template<typename T, typename U = typename T::error> TemplateAssignNoMoveSFINAE &operator=(T &&);
|
|
};
|
|
static_assert(__is_trivially_assignable(TemplateAssignNoMoveSFINAE, const TemplateAssignNoMoveSFINAE &), "");
|
|
static_assert(__is_trivially_assignable(TemplateAssignNoMoveSFINAE, TemplateAssignNoMoveSFINAE &&), "");
|
|
|
|
struct UseTemplateAssignNoMoveSFINAE {
|
|
TemplateAssignNoMoveSFINAE tanm;
|
|
};
|
|
static_assert(__is_trivially_assignable(UseTemplateAssignNoMoveSFINAE, const UseTemplateAssignNoMoveSFINAE &), "");
|
|
static_assert(__is_trivially_assignable(UseTemplateAssignNoMoveSFINAE, UseTemplateAssignNoMoveSFINAE &&), "");
|
|
|
|
namespace TrivialityDependsOnImplicitDeletion {
|
|
struct PrivateMove {
|
|
PrivateMove &operator=(const PrivateMove &) = default;
|
|
private:
|
|
PrivateMove &operator=(PrivateMove &&);
|
|
friend class Access;
|
|
};
|
|
static_assert(__is_trivially_assignable(PrivateMove, const PrivateMove &), "");
|
|
static_assert(!__is_trivially_assignable(PrivateMove, PrivateMove &&), "");
|
|
|
|
struct NoAccess {
|
|
PrivateMove pm;
|
|
// NoAccess's move would be deleted, so is suppressed,
|
|
// so moves of it use PrivateMove's copy ctor, which is trivial.
|
|
};
|
|
static_assert(__is_trivially_assignable(NoAccess, const NoAccess &), "");
|
|
static_assert(__is_trivially_assignable(NoAccess, NoAccess &&), "");
|
|
struct TopNoAccess : NoAccess {};
|
|
static_assert(__is_trivially_assignable(TopNoAccess, const TopNoAccess &), "");
|
|
static_assert(__is_trivially_assignable(TopNoAccess, TopNoAccess &&), "");
|
|
|
|
struct Access {
|
|
PrivateMove pm;
|
|
// NoAccess's move would *not* be deleted, so is *not* suppressed,
|
|
// so moves of it use PrivateMove's move ctor, which is not trivial.
|
|
};
|
|
static_assert(__is_trivially_assignable(Access, const Access &), "");
|
|
static_assert(!__is_trivially_assignable(Access, Access &&), "");
|
|
struct TopAccess : Access {};
|
|
static_assert(__is_trivially_assignable(TopAccess, const TopAccess &), "");
|
|
static_assert(!__is_trivially_assignable(TopAccess, TopAccess &&), "");
|
|
}
|