diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -2983,9 +2983,9 @@ It instructs the compiler to pass and return the type using the C ABI for the underlying type when the type would otherwise be considered non-trivial for the purpose of calls. -A class annotated with ``trivial_abi`` can have non-trivial destructors or -copy/move constructors without automatically becoming non-trivial for the -purposes of calls. For example: +A class annotated with ``trivial_abi`` can have non-trivial (or completely +deleted) copy/move constructors or a non-trivial destructor without +automatically becoming non-trivial for the purposes of calls. For example: .. code-block:: c++ @@ -3011,7 +3011,6 @@ Attribute ``trivial_abi`` has no effect in the following cases: - The class directly declares a virtual base or virtual methods. -- Copy constructors and move constructors of the class are all deleted. - The class has a base class that is non-trivial for the purposes of calls. - The class has a non-static data member whose type is non-trivial for the purposes of calls, which includes: @@ -3019,6 +3018,11 @@ - classes that are non-trivial for the purposes of calls - __weak-qualified types in Objective-C++ - arrays of any of the above + +Note: If a type has implicitly-deleted copy and move constructors only because +of a subobject that has the trivial_abi attribute, the type will also be +considered trivial for the purposes of calls. This requires the type to have no +user defined special members. }]; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3365,7 +3365,6 @@ "'trivial_abi' cannot be applied to %0">, InGroup; def note_cannot_use_trivial_abi_reason : Note< "'trivial_abi' is disallowed on %0 because %select{" - "its copy constructors and move constructors are all deleted|" "it is polymorphic|" "it has a base of a non-trivial class type|it has a virtual base|" "it has a __weak field|it has a field of a non-trivial class type}1">; 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 @@ -6422,6 +6422,13 @@ if (D->isDependentType() || D->isInvalidDecl()) return false; + // If this record has a non-trivial member (or base), the trivial_abi + // attribute won't be applied, and we will emit a warning. This means any + // record passed to this function can be passed through registers so long as + // it still has the trivial_abi attribute. + if (D->hasAttr()) + return true; + // Clang <= 4 used the pre-C++11 rule, which ignores move operations. // The PS4 platform ABI follows the behavior of Clang 3.2. if (CCK == TargetInfo::CCK_ClangABI4OrPS4) @@ -6488,6 +6495,28 @@ return false; } + // If all bases and fields are trivial, and all special members are implicit, + // then this type can be passed through registers. This might happen if we + // have an class that would otherwise be trivial for the purposes of a call + // except that it has a non-trivial member *with* the trivial_abi attribute. + for (auto Base : D->bases()) { + if (auto CxxRecord = Base.getType()->getAsCXXRecordDecl()) + if (!canPassInRegisters(S, CxxRecord, CCK)) + return false; + } + for (auto Field : D->fields()) { + if (auto CxxRecord = Field->getType()->getAsCXXRecordDecl()) + if (!canPassInRegisters(S, CxxRecord, CCK)) + return false; + } + + // Intentionally don't check if these are deleted. + if (!D->hasUserDeclaredCopyConstructor() && + !D->hasUserDeclaredCopyAssignment() && + !D->hasUserDeclaredMoveOperation() && !D->hasUserDeclaredDestructor() && + D->isAggregate()) + return true; + // Per C++ [class.temporary]p3, the relevant condition is: // each copy constructor, move constructor, and destructor of X is // either trivial or deleted, and X has at least one non-deleted copy @@ -6862,8 +6891,9 @@ // or a field qualified with ObjC __strong/__weak. if (Context.getTargetInfo().getCXXABI().areArgsDestroyedLeftToRightInCallee()) Record->setParamDestroyedInCallee(true); - else if (Record->hasNonTrivialDestructor()) + else if (Record->hasNonTrivialDestructor()) { Record->setParamDestroyedInCallee(CanPass); + } if (getLangOpts().ForceEmitVTables) { // If we want to emit all the vtables, we need to mark it as used. This @@ -9774,32 +9804,9 @@ RD.dropAttr(); }; - // Ill-formed if the copy and move constructors are deleted. - auto HasNonDeletedCopyOrMoveConstructor = [&]() { - // If the type is dependent, then assume it might have - // implicit copy or move ctor because we won't know yet at this point. - if (RD.isDependentType()) - return true; - if (RD.needsImplicitCopyConstructor() && - !RD.defaultedCopyConstructorIsDeleted()) - return true; - if (RD.needsImplicitMoveConstructor() && - !RD.defaultedMoveConstructorIsDeleted()) - return true; - for (const CXXConstructorDecl *CD : RD.ctors()) - if (CD->isCopyOrMoveConstructor() && !CD->isDeleted()) - return true; - return false; - }; - - if (!HasNonDeletedCopyOrMoveConstructor()) { - PrintDiagAndRemoveAttr(0); - return; - } - // Ill-formed if the struct has virtual functions. if (RD.isPolymorphic()) { - PrintDiagAndRemoveAttr(1); + PrintDiagAndRemoveAttr(0); return; } @@ -9808,12 +9815,12 @@ // virtual base. if (!B.getType()->isDependentType() && !B.getType()->getAsCXXRecordDecl()->canPassInRegisters()) { - PrintDiagAndRemoveAttr(2); + PrintDiagAndRemoveAttr(1); return; } if (B.isVirtual()) { - PrintDiagAndRemoveAttr(3); + PrintDiagAndRemoveAttr(2); return; } } @@ -9823,14 +9830,14 @@ // non-trivial for the purpose of calls. QualType FT = FD->getType(); if (FT.getObjCLifetime() == Qualifiers::OCL_Weak) { - PrintDiagAndRemoveAttr(4); + PrintDiagAndRemoveAttr(3); return; } if (const auto *RT = FT->getBaseElementTypeUnsafe()->getAs()) if (!RT->isDependentType() && !cast(RT->getDecl())->canPassInRegisters()) { - PrintDiagAndRemoveAttr(5); + PrintDiagAndRemoveAttr(4); return; } } diff --git a/clang/test/CodeGenCXX/trivial_abi.cpp b/clang/test/CodeGenCXX/trivial_abi.cpp --- a/clang/test/CodeGenCXX/trivial_abi.cpp +++ b/clang/test/CodeGenCXX/trivial_abi.cpp @@ -1,10 +1,17 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++11 -fcxx-exceptions -fexceptions -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++11 -fcxx-exceptions -fexceptions -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-NEW-ABI // RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++11 -fcxx-exceptions -fexceptions -fclang-abi-compat=4.0 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++20 -fcxx-exceptions -fexceptions -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-20 %s // CHECK: %[[STRUCT_SMALL:.*]] = type { i32* } // CHECK: %[[STRUCT_LARGE:.*]] = type { i32*, [128 x i32] } // CHECK: %[[STRUCT_TRIVIAL:.*]] = type { i32 } // CHECK: %[[STRUCT_NONTRIVIAL:.*]] = type { i32 } +// CHECK: [[STRUCT_COPY_MOVE_DELETED:%.*]] = type { i8 } +// CHECK: [[STRUCT_MOVE_LIKE_1:%.*]] = type { i8 } +// CHECK: [[STRUCT_MOVE_LIKE_2:%.*]] = type { i8 } +// CHECK: [[STRUCT_HAS_MEMBER_COPY_MOVE_DELETED:%.*]] = type { [[STRUCT_COPY_MOVE_DELETED]] } +// CHECK: [[STRUCT_TEMPLATED_NON_TRIVIAL:%.*]] = type { %[[STRUCT_NONTRIVIAL]] } +// CHECK: [[STRUCT_TEMPLATED_BASE_NON_TRIVIAL:%.*]] = type { %[[STRUCT_NONTRIVIAL]], i32 } struct __attribute__((trivial_abi)) Small { int *p; @@ -55,6 +62,57 @@ Small m0() override; }; +struct __attribute__((trivial_abi)) CopyMoveDeleted { + CopyMoveDeleted(CopyMoveDeleted const &) = delete; + CopyMoveDeleted(CopyMoveDeleted &&) = delete; + CopyMoveDeleted(int) {} + ~CopyMoveDeleted() {} +}; + +struct __attribute__((trivial_abi)) TemplatedMoveLikeCtor { + TemplatedMoveLikeCtor(TemplatedMoveLikeCtor const &) = delete; + TemplatedMoveLikeCtor(TemplatedMoveLikeCtor &&) = delete; + template TemplatedMoveLikeCtor(T &&) {} + ~TemplatedMoveLikeCtor() {} +}; + +struct __attribute__((trivial_abi)) TemplatedMoveLikeCtor2 { + TemplatedMoveLikeCtor2(TemplatedMoveLikeCtor2 const &) = delete; + template TemplatedMoveLikeCtor2(TemplatedMoveLikeCtor2 &&) {} + ~TemplatedMoveLikeCtor2() {} +}; + +struct __attribute__((trivial_abi)) HasMemberCopyMoveDeleted { + CopyMoveDeleted member; +}; + +struct __attribute__((trivial_abi)) CopyDeleted { + CopyDeleted(const CopyDeleted &) = delete; + CopyDeleted(CopyDeleted &&) = default; +}; + +struct __attribute__((trivial_abi)) MoveDeleted { + MoveDeleted(const MoveDeleted &) = default; + MoveDeleted(MoveDeleted &&) = delete; +}; + +struct __attribute__((trivial_abi)) HasMembersCopyDeletedAndMoveDeleted { + CopyDeleted m1; + MoveDeleted m2; +}; + +template +struct __attribute__((trivial_abi)) TemplatedTrivialABI { + T n; + ~TemplatedTrivialABI() {} +}; + +template +struct __attribute__((trivial_abi)) TemplatedBaseTrivialABI : T { + int n; + ~TemplatedBaseTrivialABI() {} +}; + // CHECK-LABEL: define i64 @_ZThn8_N2D02m0Ev( // CHECK: %[[RETVAL:.*]] = alloca %[[STRUCT_SMALL]], align 8 // CHECK: %[[CALL:.*]] = tail call i64 @_ZN2D02m0Ev( @@ -262,3 +320,142 @@ void testExceptionLarge() { calleeExceptionLarge(Large(), Large()); } + +// CHECK-LABEL: define void @_Z19testCopyMoveDeleted15CopyMoveDeleted(i8 %{{.*}}) +// CHECK: call [[STRUCT_COPY_MOVE_DELETED]]* @_ZN15CopyMoveDeletedD1Ev([[STRUCT_COPY_MOVE_DELETED]]* +// CHECK: ret void +void testCopyMoveDeleted(CopyMoveDeleted) {} + +// CHECK-LABEL: define void @_Z25testTemplatedMoveLikeCtor21TemplatedMoveLikeCtor(i8 %{{.*}}) +// CHECK: call [[STRUCT_MOVE_LIKE_1]]* @_ZN21TemplatedMoveLikeCtorD1Ev([[STRUCT_MOVE_LIKE_1]]* +// CHECK: ret void +TemplatedMoveLikeCtor testTemplatedMoveLikeCtor(TemplatedMoveLikeCtor a) { + return a; +} + +// CHECK: define void @_Z26testTemplatedMoveLikeCtor222TemplatedMoveLikeCtor2(i8 %{{.*}}) +// CHECK: call [[STRUCT_MOVE_LIKE_2]]* @_ZN22TemplatedMoveLikeCtor2D1Ev([[STRUCT_MOVE_LIKE_2]]* +// CHECK: ret void +TemplatedMoveLikeCtor2 testTemplatedMoveLikeCtor2(TemplatedMoveLikeCtor2 a) { + return a; +} + +// CHECK: define void @_Z28testHasMemberCopyMoveDeleted24HasMemberCopyMoveDeleted(i64 %{{.*}}) +// CHECK: call [[STRUCT_HAS_MEMBER_COPY_MOVE_DELETED]]* @_ZN24HasMemberCopyMoveDeletedD1Ev([[STRUCT_HAS_MEMBER_COPY_MOVE_DELETED]]* +// CHECK: ret void +void testHasMemberCopyMoveDeleted(HasMemberCopyMoveDeleted) {} + +// CHECK: define void @_Z39testHasMembersCopyDeletedAndMoveDeleted35HasMembersCopyDeletedAndMoveDeleted(i64 %{{.*}}) +// CHECK: ret void +void testHasMembersCopyDeletedAndMoveDeleted(HasMembersCopyDeletedAndMoveDeleted) {} + +// CHECK: define void @_Z23testTemplatedTrivialABI19TemplatedTrivialABII10NonTrivialE([[STRUCT_TEMPLATED_NON_TRIVIAL]]* +// CHECK: ret void +void testTemplatedTrivialABI(TemplatedTrivialABI a) {} + +// CHECK: define void @_Z27testTemplatedBaseTrivialABI23TemplatedBaseTrivialABII10NonTrivialE([[STRUCT_TEMPLATED_BASE_NON_TRIVIAL]]* +// CHECK: ret void +void testTemplatedBaseTrivialABI(TemplatedBaseTrivialABI a) {} + +namespace DeletedCopyCtor { + +struct __attribute__((trivial_abi)) S0 { + S0(); + S0(const S0 &) = delete; + S0(S0 &&) = delete; + int a; +}; +struct S1 : S0 {}; +struct S2 : virtual S0 {}; +struct S3 { + S0 s0; +}; +struct S4 { + S0 s0; + virtual void test(); +}; +struct S5 { + S0 s0; + S5(S5 const &); +}; +struct S6 { + S0 s0; + S6(S6 const &) = delete; +}; +struct S7 { + S0 s0; + S7(S7 const &) = default; +}; +struct S8 : S0 { + virtual void test(); +}; +struct S9 : S0 { + S9(S9 const &); +}; +struct S10 : S0 { + S10(S10 const &) = delete; +}; +struct S11 : S0 { + S11(S11 const &) = default; +}; + +// CHECK-20-LABEL: define void @_ZN15DeletedCopyCtor4testENS_2S0E(i64 +// CHECK-LABEL: define void @_ZN15DeletedCopyCtor4testENS_2S0E(i64 +// CHECK: ret void +void test(S0) {} + +// CHECK-20-LABEL: define void @_ZN15DeletedCopyCtor4testENS_2S1E(i64 +// CHECK-20: ret void +void test(S1) {} + +// CHECK-20-LABEL: define void @_ZN15DeletedCopyCtor4testENS_2S2E(%"struct.DeletedCopyCtor::S2"* +// CHECK-LABEL: define void @_ZN15DeletedCopyCtor4testENS_2S2E(%"struct.DeletedCopyCtor::S2"* +// CHECK: ret void +void test(S2) {} + +// CHECK-20-LABEL: define void @_ZN15DeletedCopyCtor4testENS_2S3E(i64 +// CHECK-LABEL: define void @_ZN15DeletedCopyCtor4testENS_2S3E(i64 +// CHECK: ret void +void test(S3) {} + +// CHECK-20-LABEL: define void @_ZN15DeletedCopyCtor4testENS_2S4E(%"struct.DeletedCopyCtor::S4"* +// CHECK-LABEL: define void @_ZN15DeletedCopyCtor4testENS_2S4E(%"struct.DeletedCopyCtor::S4"* +// CHECK: ret void +void test(S4) {} + +// CHECK-20-LABEL: define void @_ZN15DeletedCopyCtor4testENS_2S5E(%"struct.DeletedCopyCtor::S5"* +// CHECK-LABEL: define void @_ZN15DeletedCopyCtor4testENS_2S5E(%"struct.DeletedCopyCtor::S5"* +// CHECK: ret void +void test(S5) {} + +// CHECK-20-LABEL: define void @_ZN15DeletedCopyCtor4testENS_2S6E(%"struct.DeletedCopyCtor::S6"* +// CHECK-NEW-ABI-LABEL: define void @_ZN15DeletedCopyCtor4testENS_2S6E(%"struct.DeletedCopyCtor::S6"* +// CHECK-NEW-ABI: ret void +void test(S6) {} + +// CHECK-20-LABEL: define void @_ZN15DeletedCopyCtor4testENS_2S7E(%"struct.DeletedCopyCtor::S7"* +// CHECK-NEW-ABI-LABEL: define void @_ZN15DeletedCopyCtor4testENS_2S7E(%"struct.DeletedCopyCtor::S7"* +// CHECK-NEW-ABI: ret void +void test(S7) {} + +// CHECK-20-LABEL: define void @_ZN15DeletedCopyCtor4testENS_2S8E(%"struct.DeletedCopyCtor::S8"* +// CHECK-LABEL: define void @_ZN15DeletedCopyCtor4testENS_2S8E(%"struct.DeletedCopyCtor::S8"* +// CHECK: ret void +void test(S8) {} + +// CHECK-20-LABEL: define void @_ZN15DeletedCopyCtor4testENS_2S9E(%"struct.DeletedCopyCtor::S9"* +// CHECK-LABEL: define void @_ZN15DeletedCopyCtor4testENS_2S9E(%"struct.DeletedCopyCtor::S9"* +// CHECK: ret void +void test(S9) {} + +// CHECK-20-LABEL: define void @_ZN15DeletedCopyCtor4testENS_3S10E(%"struct.DeletedCopyCtor::S10"* +// CHECK-NEW-ABI-LABEL: define void @_ZN15DeletedCopyCtor4testENS_3S10E(%"struct.DeletedCopyCtor::S10"* +// CHECK-NEW-ABI: ret void +void test(S10) {} + +// CHECK-20-LABEL: define void @_ZN15DeletedCopyCtor4testENS_3S11E(%"struct.DeletedCopyCtor::S11"* +// CHECK-NEW-ABI-LABEL: define void @_ZN15DeletedCopyCtor4testENS_3S11E(%"struct.DeletedCopyCtor::S11"* +// CHECK-NEW-ABI: ret void +void test(S11) {} + +} // namespace DeletedCopyCtor diff --git a/clang/test/SemaCXX/attr-trivial-abi.cpp b/clang/test/SemaCXX/attr-trivial-abi.cpp --- a/clang/test/SemaCXX/attr-trivial-abi.cpp +++ b/clang/test/SemaCXX/attr-trivial-abi.cpp @@ -34,6 +34,21 @@ S3_2 s32; }; +struct NonTrivial { + NonTrivial(const NonTrivial &) {} + ~NonTrivial() {} +}; + +struct __attribute__((trivial_abi)) S18 { // expected-warning {{'trivial_abi' cannot be applied to 'S18'}} expected-note {{has a field of a non-trivial class type}} + NonTrivial n; + ~S18() {} +}; + +struct __attribute__((trivial_abi)) S19 : NonTrivial { // expected-warning {{'trivial_abi' cannot be applied to 'S19'}} expected-note {{'trivial_abi' is disallowed on 'S19' because it has a base of a non-trivial class type}} + int n; + ~S19() {} +}; + struct S4 { int a; }; @@ -79,34 +94,3 @@ }; S17 s17; - -namespace deletedCopyMoveConstructor { -struct __attribute__((trivial_abi)) CopyMoveDeleted { // expected-warning {{'trivial_abi' cannot be applied to 'CopyMoveDeleted'}} expected-note {{copy constructors and move constructors are all deleted}} - CopyMoveDeleted(const CopyMoveDeleted &) = delete; - CopyMoveDeleted(CopyMoveDeleted &&) = delete; -}; - -struct __attribute__((trivial_abi)) S18 { // expected-warning {{'trivial_abi' cannot be applied to 'S18'}} expected-note {{copy constructors and move constructors are all deleted}} - CopyMoveDeleted a; -}; - -struct __attribute__((trivial_abi)) CopyDeleted { - CopyDeleted(const CopyDeleted &) = delete; - CopyDeleted(CopyDeleted &&) = default; -}; - -struct __attribute__((trivial_abi)) MoveDeleted { - MoveDeleted(const MoveDeleted &) = default; - MoveDeleted(MoveDeleted &&) = delete; -}; - -struct __attribute__((trivial_abi)) S19 { // expected-warning {{'trivial_abi' cannot be applied to 'S19'}} expected-note {{copy constructors and move constructors are all deleted}} - CopyDeleted a; - MoveDeleted b; -}; - -// This is fine since the move constructor isn't deleted. -struct __attribute__((trivial_abi)) S20 { - int &&a; // a member of rvalue reference type deletes the copy constructor. -}; -} // namespace deletedCopyMoveConstructor diff --git a/clang/test/SemaObjCXX/attr-trivial-abi.mm b/clang/test/SemaObjCXX/attr-trivial-abi.mm --- a/clang/test/SemaObjCXX/attr-trivial-abi.mm +++ b/clang/test/SemaObjCXX/attr-trivial-abi.mm @@ -101,34 +101,3 @@ }; S17 s17; - -namespace deletedCopyMoveConstructor { - struct __attribute__((trivial_abi)) CopyMoveDeleted { // expected-warning {{'trivial_abi' cannot be applied to 'CopyMoveDeleted'}} expected-note {{copy constructors and move constructors are all deleted}} - CopyMoveDeleted(const CopyMoveDeleted &) = delete; - CopyMoveDeleted(CopyMoveDeleted &&) = delete; - }; - - struct __attribute__((trivial_abi)) S18 { // expected-warning {{'trivial_abi' cannot be applied to 'S18'}} expected-note {{copy constructors and move constructors are all deleted}} - CopyMoveDeleted a; - }; - - struct __attribute__((trivial_abi)) CopyDeleted { - CopyDeleted(const CopyDeleted &) = delete; - CopyDeleted(CopyDeleted &&) = default; - }; - - struct __attribute__((trivial_abi)) MoveDeleted { - MoveDeleted(const MoveDeleted &) = default; - MoveDeleted(MoveDeleted &&) = delete; - }; - - struct __attribute__((trivial_abi)) S19 { // expected-warning {{'trivial_abi' cannot be applied to 'S19'}} expected-note {{copy constructors and move constructors are all deleted}} - CopyDeleted a; - MoveDeleted b; - }; - - // This is fine since the move constructor isn't deleted. - struct __attribute__((trivial_abi)) S20 { - int &&a; // a member of rvalue reference type deletes the copy constructor. - }; -}