diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize-loop-convert-const.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize-loop-convert-const.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/modernize-loop-convert-const.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize-loop-convert-const.cpp @@ -10,12 +10,12 @@ }; // 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 diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -386,7 +386,6 @@ ``-mignore-xcoff-visibility`` option can be manually specified on the command-line to recover the previous behavior if desired. - C Language Changes in Clang --------------------------- @@ -473,6 +472,11 @@ (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 ----------------------- diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -215,8 +215,12 @@ 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 diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -9772,11 +9772,22 @@ 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(); - 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 @@ Context.getRecordType(RD).withConst()); return false; } + + ConstArg = RT->getPointeeType().isConstQualified(); break; } diff --git a/clang/test/CXX/drs/dr21xx.cpp b/clang/test/CXX/drs/dr21xx.cpp --- a/clang/test/CXX/drs/dr21xx.cpp +++ b/clang/test/CXX/drs/dr21xx.cpp @@ -140,6 +140,33 @@ #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}} diff --git a/clang/test/CXX/special/class.copy/p12-0x.cpp b/clang/test/CXX/special/class.copy/p12-0x.cpp --- a/clang/test/CXX/special/class.copy/p12-0x.cpp +++ b/clang/test/CXX/special/class.copy/p12-0x.cpp @@ -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 @@ 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; +#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; +#endif // class X has no virtual functions struct VFn { diff --git a/clang/test/CXX/special/class.copy/p25-0x.cpp b/clang/test/CXX/special/class.copy/p25-0x.cpp --- a/clang/test/CXX/special/class.copy/p25-0x.cpp +++ b/clang/test/CXX/special/class.copy/p25-0x.cpp @@ -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 @@ 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; +#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; +#endif // class X has no virtual functions struct VFn { diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -12840,7 +12840,7 @@ 2171 CD4 Triviality of copy constructor with less-qualified parameter - Unknown + Clang 15 2172