From: Javier Alvarez Date: Fri, 17 Jun 2022 07:31:14 +0000 (+0300) Subject: [clang] Fix trivially copyable for copy constructor and copy assignment operator X-Git-Tag: upstream/15.0.7~4401 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=5ea341d7c4f9cc4933adc04ff74d59e26ad2f306;p=platform%2Fupstream%2Fllvm.git [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 --- 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 index fd3bdde..beec7fa 100644 --- 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 @@ 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 diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index ecf574b..e9ea54d 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -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 ----------------------- diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index cf121de..f1b6cb6 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -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 diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 6c8704c..ebeb98b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -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(); - 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; } diff --git a/clang/test/CXX/drs/dr21xx.cpp b/clang/test/CXX/drs/dr21xx.cpp index 62c5670..a6277f7 100644 --- a/clang/test/CXX/drs/dr21xx.cpp +++ b/clang/test/CXX/drs/dr21xx.cpp @@ -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}} diff --git a/clang/test/CXX/special/class.copy/p12-0x.cpp b/clang/test/CXX/special/class.copy/p12-0x.cpp index a0ef49d..283e945 100644 --- 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 @@ using _ = not_trivially_copyable; 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 index c7224ae..a959932 100644 --- 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 @@ using _ = not_trivially_assignable; 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 index b9a3815..e437da1 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -12840,7 +12840,7 @@ and POD class 2171 CD4 Triviality of copy constructor with less-qualified parameter - Unknown + Clang 15 2172