[clang] Fix trivially copyable for copy constructor and copy assignment operator

From [class.copy.ctor]:

```
A non-template constructor for class X is a copy constructor if its first
parameter is of type X&, const X&, volatile X& or const volatile X&, and
either there are no other parameters or else all other parameters have
default arguments (9.3.4.7).

A copy/move constructor for class X is trivial if it is not user-provided and if:
- class X has no virtual functions (11.7.3) and no virtual base classes (11.7.2), and
- the constructor selected to copy/move each direct base class subobject is trivial, and
- or each non-static data member of X that is of class type (or array thereof),
  the constructor selected to copy/move that member is trivial;

otherwise the copy/move constructor is non-trivial.
```

So `T(T&) = default`; should be trivial assuming that the previous
provisions are met.

This works in GCC, but not in Clang at the moment:
https://godbolt.org/z/fTGe71b6P

Reviewed By: royjacobson

Differential Revision: https://reviews.llvm.org/D127593
This commit is contained in:
Javier Alvarez 2022-06-17 10:31:14 +03:00 committed by Roy Jacobson
parent 880ac5189d
commit 5ea341d7c4
8 changed files with 101 additions and 10 deletions

View File

@ -10,12 +10,12 @@ struct Str {
};
// This class is non-trivially copyable because the copy-constructor and copy
// assignment take non-const references.
// assignment take non-const references and are user-provided.
struct ModifiesRightSide {
ModifiesRightSide() = default;
ModifiesRightSide(ModifiesRightSide &) = default;
ModifiesRightSide(ModifiesRightSide &);
bool operator<(ModifiesRightSide &) const;
ModifiesRightSide &operator=(ModifiesRightSide &) = default;
ModifiesRightSide &operator=(ModifiesRightSide &);
};
template <typename T>

View File

@ -391,7 +391,6 @@ AIX Support
``-mignore-xcoff-visibility`` option can be manually specified on the
command-line to recover the previous behavior if desired.
C Language Changes in Clang
---------------------------
@ -478,6 +477,11 @@ ABI Changes in Clang
(e.g. ``int : 0``) no longer prevents the structure from being considered a
homogeneous floating-point or vector aggregate. The new behavior agrees with
the AAPCS specification, and matches the similar bug fix in GCC 12.1.
- All copy constructors can now be trivial if they are not user-provided,
regardless of the type qualifiers of the argument of the defaulted constructor,
fixing dr2171.
You can switch back to the old ABI behavior with the flag:
``-fclang-abi-compat=14.0``.
OpenMP Support in Clang
-----------------------

View File

@ -215,8 +215,12 @@ public:
Ver12,
/// Attempt to be ABI-compatible with code generated by Clang 14.0.x.
/// This causes clang to mangle dependent nested names incorrectly.
/// This causes clang to pack non-POD members of packed structs.
/// This causes clang to:
/// - mangle dependent nested names incorrectly.
/// - pack non-POD members of packed structs.
/// - make trivial only those defaulted copy constructors with a
/// parameter-type-list equivalent to the parameter-type-list of an
/// implicit declaration.
Ver14,
/// Conform to the underlying platform's C and C++ ABIs as closely

View File

@ -9772,11 +9772,22 @@ bool Sema::SpecialMemberIsTrivial(CXXMethodDecl *MD, CXXSpecialMember CSM,
case CXXCopyConstructor:
case CXXCopyAssignment: {
// Trivial copy operations always have const, non-volatile parameter types.
ConstArg = true;
const ParmVarDecl *Param0 = MD->getParamDecl(0);
const ReferenceType *RT = Param0->getType()->getAs<ReferenceType>();
if (!RT || RT->getPointeeType().getCVRQualifiers() != Qualifiers::Const) {
// When ClangABICompat14 is true, CXX copy constructors will only be trivial
// if they are not user-provided and their parameter-type-list is equivalent
// to the parameter-type-list of an implicit declaration. This maintains the
// behavior before dr2171 was implemented.
//
// Otherwise, if ClangABICompat14 is false, All copy constructors can be
// trivial, if they are not user-provided, regardless of the qualifiers on
// the reference type.
const bool ClangABICompat14 = Context.getLangOpts().getClangABICompat() <=
LangOptions::ClangABI::Ver14;
if (!RT ||
((RT->getPointeeType().getCVRQualifiers() != Qualifiers::Const) &&
ClangABICompat14)) {
if (Diagnose)
Diag(Param0->getLocation(), diag::note_nontrivial_param_type)
<< Param0->getSourceRange() << Param0->getType()
@ -9784,6 +9795,8 @@ bool Sema::SpecialMemberIsTrivial(CXXMethodDecl *MD, CXXSpecialMember CSM,
Context.getRecordType(RD).withConst());
return false;
}
ConstArg = RT->getPointeeType().isConstQualified();
break;
}

View File

@ -140,6 +140,33 @@ namespace dr2170 { // dr2170: 9
#endif
}
namespace dr2171 { // dr2171: 15
#if __cplusplus >= 201103L
struct NonConstCopy {
NonConstCopy(NonConstCopy &) = default;
NonConstCopy &operator=(NonConstCopy &) = default;
};
static_assert(__has_trivial_copy(NonConstCopy), "");
static_assert(__is_trivially_constructible(NonConstCopy, NonConstCopy &), "");
static_assert(!__is_trivially_constructible(NonConstCopy, NonConstCopy), "");
static_assert(!__is_trivially_constructible(NonConstCopy, const NonConstCopy &), "");
static_assert(!__is_trivially_constructible(NonConstCopy, NonConstCopy &&), "");
static_assert(__has_trivial_assign(NonConstCopy), "");
static_assert(__is_trivially_assignable(NonConstCopy &, NonConstCopy &), "");
static_assert(!__is_trivially_assignable(NonConstCopy &, const NonConstCopy &), "");
static_assert(!__is_trivially_assignable(NonConstCopy &, NonConstCopy), "");
static_assert(!__is_trivially_assignable(NonConstCopy &, NonConstCopy &&), "");
static_assert(__is_trivially_assignable(NonConstCopy &&, NonConstCopy &), "");
static_assert(!__is_trivially_assignable(NonConstCopy &&, const NonConstCopy &), "");
static_assert(!__is_trivially_assignable(NonConstCopy &&, NonConstCopy), "");
static_assert(!__is_trivially_assignable(NonConstCopy &&, NonConstCopy &&), "");
#endif
} // namespace dr2171
namespace dr2180 { // dr2180: yes
class A {
A &operator=(const A &); // expected-note 0-2{{here}}

View File

@ -1,4 +1,5 @@
// RUN: %clang_cc1 -std=c++11 -verify %s -Wno-defaulted-function-deleted
// RUN: %clang_cc1 -std=c++11 -verify %s -Wno-defaulted-function-deleted -fclang-abi-compat=14 -DCLANG_ABI_COMPAT=14
// expected-no-diagnostics
@ -28,7 +29,25 @@ using _ = not_trivially_copyable<UserProvided>;
struct NonConstCopy {
NonConstCopy(NonConstCopy &) = default;
};
#if defined(CLANG_ABI_COMPAT) && CLANG_ABI_COMPAT <= 14
// Up until (and including) Clang 14, non-const copy constructors were not trivial because of dr2171
using _ = not_trivially_copyable<NonConstCopy>;
#else
// In the latest Clang version, all defaulted constructors are trivial, even if non-const, because
// dr2171 is fixed.
static_assert(__has_trivial_copy(NonConstCopy), "");
static_assert(__is_trivially_constructible(NonConstCopy, NonConstCopy &), "");
static_assert(!__is_trivially_constructible(NonConstCopy, NonConstCopy), "");
static_assert(!__is_trivially_constructible(NonConstCopy, const NonConstCopy &), "");
static_assert(!__is_trivially_constructible(NonConstCopy, NonConstCopy &&), "");
struct DefaultedSpecialMembers {
DefaultedSpecialMembers(const DefaultedSpecialMembers &) = default;
DefaultedSpecialMembers(DefaultedSpecialMembers &) = default;
DefaultedSpecialMembers(DefaultedSpecialMembers &&) = default;
};
using _ = trivially_copyable<DefaultedSpecialMembers>;
#endif
// class X has no virtual functions
struct VFn {

View File

@ -1,4 +1,5 @@
// RUN: %clang_cc1 -std=c++11 -verify %s
// RUN: %clang_cc1 -std=c++11 -verify %s -fclang-abi-compat=14 -DCLANG_ABI_COMPAT=14
// expected-no-diagnostics
@ -31,7 +32,30 @@ using _ = not_trivially_assignable<UserProvided>;
struct NonConstCopy {
NonConstCopy &operator=(NonConstCopy &) = default;
};
#if defined(CLANG_ABI_COMPAT) && CLANG_ABI_COMPAT <= 14
// Up until (and including) Clang 14, non-const copy assignment operators were not trivial because
// of dr2171
using _ = not_trivially_assignable<NonConstCopy>;
#else
// In the latest Clang version, all defaulted assignment operators are trivial, even if non-const,
// because dr2171 is fixed
static_assert(__has_trivial_assign(NonConstCopy), "");
static_assert(__is_trivially_assignable(NonConstCopy &, NonConstCopy &), "");
static_assert(!__is_trivially_assignable(NonConstCopy &, const NonConstCopy &), "");
static_assert(!__is_trivially_assignable(NonConstCopy &, NonConstCopy), "");
static_assert(!__is_trivially_assignable(NonConstCopy &, NonConstCopy &&), "");
static_assert(__is_trivially_assignable(NonConstCopy &&, NonConstCopy &), "");
static_assert(!__is_trivially_assignable(NonConstCopy &&, const NonConstCopy &), "");
static_assert(!__is_trivially_assignable(NonConstCopy &&, NonConstCopy), "");
static_assert(!__is_trivially_assignable(NonConstCopy &&, NonConstCopy &&), "");
struct DefaultedSpecialMembers {
DefaultedSpecialMembers &operator=(const DefaultedSpecialMembers &) = default;
DefaultedSpecialMembers &operator=(DefaultedSpecialMembers &) = default;
DefaultedSpecialMembers &operator=(DefaultedSpecialMembers &&) = default;
};
using _ = trivially_assignable<DefaultedSpecialMembers>;
#endif
// class X has no virtual functions
struct VFn {

View File

@ -12840,7 +12840,7 @@ and <I>POD class</I></td>
<td><a href="https://wg21.link/cwg2171">2171</a></td>
<td>CD4</td>
<td>Triviality of copy constructor with less-qualified parameter</td>
<td class="none" align="center">Unknown</td>
<td class="unreleased" align="center">Clang 15</td>
</tr>
<tr class="open" id="2172">
<td><a href="https://wg21.link/cwg2172">2172</a></td>