Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -1732,6 +1732,12 @@ unsigned HasWrittenPrototype : 1; unsigned IsDeleted : 1; unsigned IsTrivial : 1; // sunk from CXXMethodDecl + + /// This flag indicates whether this function is trivial for the purpose of + /// calls. This is meaningful only when this function is a copy/move + /// constructor or a destructor. + unsigned IsTrivialForCall : 1; + unsigned IsDefaulted : 1; // sunk from CXXMethoDecl unsigned IsExplicitlyDefaulted : 1; //sunk from CXXMethodDecl unsigned HasImplicitReturnZero : 1; @@ -1842,7 +1848,8 @@ IsInline(isInlineSpecified), IsInlineSpecified(isInlineSpecified), IsExplicitSpecified(false), IsVirtualAsWritten(false), IsPure(false), HasInheritedPrototype(false), HasWrittenPrototype(true), - IsDeleted(false), IsTrivial(false), IsDefaulted(false), + IsDeleted(false), IsTrivial(false), IsTrivialForCall(false), + IsDefaulted(false), IsExplicitlyDefaulted(false), HasImplicitReturnZero(false), IsLateTemplateParsed(false), IsConstexpr(isConstexprSpecified), InstantiationIsPending(false), UsesSEHTry(false), HasSkippedBody(false), @@ -2007,6 +2014,9 @@ bool isTrivial() const { return IsTrivial; } void setTrivial(bool IT) { IsTrivial = IT; } + bool isTrivialForCall() const { return IsTrivialForCall; } + void setTrivialForCall(bool IT) { IsTrivialForCall = IT; } + /// Whether this function is defaulted per C++0x. Only valid for /// special member functions. bool isDefaulted() const { return IsDefaulted; } Index: include/clang/AST/DeclCXX.h =================================================================== --- include/clang/AST/DeclCXX.h +++ include/clang/AST/DeclCXX.h @@ -437,6 +437,11 @@ /// which have been declared but not yet defined. unsigned HasTrivialSpecialMembers : 6; + /// These bits keep track of the triviality of special functions for the + /// purpose of calls. Only the bits corresponding to SMF_CopyConstructor, + /// SMF_MoveConstructor, and SMF_Destructor are meaningful here. + unsigned HasTrivialSpecialMembersForCall : 6; + /// \brief The declared special members of this class which are known to be /// non-trivial. /// @@ -445,6 +450,12 @@ /// members which have not yet been declared. unsigned DeclaredNonTrivialSpecialMembers : 6; + /// These bits keep track of the declared special members that are + /// non-trivial for the purpose of calls. + /// Only the bits corresponding to SMF_CopyConstructor, + /// SMF_MoveConstructor, and SMF_Destructor are meaningful here. + unsigned DeclaredNonTrivialSpecialMembersForCall : 6; + /// \brief True when this class has a destructor with no semantic effect. unsigned HasIrrelevantDestructor : 1; @@ -462,6 +473,10 @@ /// pass an object of this class. unsigned CanPassInRegisters : 1; + /// This indicate whether this class has a member that makes this class + /// ill-formed if it is annotated with "trivial_abi". + unsigned DropTrivialABI : 1; + /// \brief True if a defaulted default constructor for this class would /// be constexpr. unsigned DefaultedDefaultConstructorIsConstexpr : 1; @@ -1349,6 +1364,10 @@ return data().HasTrivialSpecialMembers & SMF_CopyConstructor; } + bool hasTrivialCopyConstructorForCall() const { + return data().HasTrivialSpecialMembersForCall & SMF_CopyConstructor; + } + /// \brief Determine whether this class has a non-trivial copy constructor /// (C++ [class.copy]p6, C++11 [class.copy]p12) bool hasNonTrivialCopyConstructor() const { @@ -1356,6 +1375,11 @@ !hasTrivialCopyConstructor(); } + bool hasNonTrivialCopyConstructorForCall() const { + return data().DeclaredNonTrivialSpecialMembersForCall & + SMF_CopyConstructor || !hasTrivialCopyConstructorForCall(); + } + /// \brief Determine whether this class has a trivial move constructor /// (C++11 [class.copy]p12) bool hasTrivialMoveConstructor() const { @@ -1363,6 +1387,11 @@ (data().HasTrivialSpecialMembers & SMF_MoveConstructor); } + bool hasTrivialMoveConstructorForCall() const { + return hasMoveConstructor() && + (data().HasTrivialSpecialMembersForCall & SMF_MoveConstructor); + } + /// \brief Determine whether this class has a non-trivial move constructor /// (C++11 [class.copy]p12) bool hasNonTrivialMoveConstructor() const { @@ -1371,6 +1400,13 @@ !(data().HasTrivialSpecialMembers & SMF_MoveConstructor)); } + bool hasNonTrivialMoveConstructorForCall() const { + return (data().DeclaredNonTrivialSpecialMembersForCall & + SMF_MoveConstructor) || + (needsImplicitMoveConstructor() && + !(data().HasTrivialSpecialMembersForCall & SMF_MoveConstructor)); + } + /// \brief Determine whether this class has a trivial copy assignment operator /// (C++ [class.copy]p11, C++11 [class.copy]p25) bool hasTrivialCopyAssignment() const { @@ -1405,12 +1441,25 @@ return data().HasTrivialSpecialMembers & SMF_Destructor; } + bool hasTrivialDestructorForCall() const { + return data().HasTrivialSpecialMembersForCall & SMF_Destructor; + } + /// \brief Determine whether this class has a non-trivial destructor /// (C++ [class.dtor]p3) bool hasNonTrivialDestructor() const { return !(data().HasTrivialSpecialMembers & SMF_Destructor); } + bool hasNonTrivialDestructorForCall() const { + return !(data().HasTrivialSpecialMembersForCall & SMF_Destructor); + } + + void setHasTrivialSpecialMemberForCall() { + data().HasTrivialSpecialMembersForCall = + (SMF_CopyConstructor | SMF_MoveConstructor | SMF_Destructor); + } + /// \brief Determine whether declaring a const variable with this type is ok /// per core issue 253. bool allowConstDefaultInit() const { @@ -1440,6 +1489,18 @@ data().CanPassInRegisters = CanPass; } + bool dropTrivialABI() const { + return data().DropTrivialABI; + } + + void setDropTrivialABI() { + data().DropTrivialABI = true; + } + + bool destructInCallee() const { + return data().CanPassInRegisters && hasNonTrivialDestructor(); + } + /// \brief Determine whether this class has a non-literal or/ volatile type /// non-static data member or base class. bool hasNonLiteralTypeFieldsOrBases() const { @@ -1797,6 +1858,8 @@ /// member function is now complete. void finishedDefaultedOrDeletedMember(CXXMethodDecl *MD); + void finishedUserProvidedMethod(CXXMethodDecl *MD); + /// \brief Indicates that the definition of this class is now complete. void completeDefinition() override; Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -808,6 +808,10 @@ /// Return true if this is a trivially copyable type (C++0x [basic.types]p9) bool isTriviallyCopyableType(const ASTContext &Context) const; + /// Determine whether this type should be destructed in the callee when passed + /// to a function. + bool destructInCallee() const; + // Don't promise in the API that anything besides 'const' can be // easily added. Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -1155,6 +1155,12 @@ let Documentation = [LayoutVersionDocs]; } +def TrivialABI : InheritableAttr { + let Spellings = [Clang<"trivial_abi">]; + let Subjects = SubjectList<[CXXRecord]>; + let Documentation = [TrivialABIDocs]; +} + def MaxFieldAlignment : InheritableAttr { // This attribute has no spellings as it is only ever created implicitly. let Spellings = []; Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -2224,6 +2224,44 @@ }]; } +def TrivialABIDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +The ``trivial_abi`` attribute can be applied to a C++ class, struct, or union. +It instructs the compiler to pass and return the type using the C ABI for the +underlying type, meaning that a type may be passed or returned directly even +when the type is considered non-trivial for the purpose of calls according to +the C++ ABI. Non-trivial destructors or copy/move constructors of classes +annotated with ``trivial_abi`` do not cause the corresponding special functions +of containing classes to be non-trivial for the purpose of calls. For example: + + .. code-block:: c++ + + struct __attribute__((trivial_abi)) A { + ~A(); + A(const A &); + A(A &&); + int x; + }; + + // B's destructor and copy/move constructor are considered trivial for the + // purpose of calls despite A's non-trivial special functions. + struct B { + A a; + }; + +The destruction convention for arguments of ``trivial_abi`` types is that the +callee is responsible for destroying the object regardless of how it is passed +under the C ABI. + +Attribute ``trivial_abi`` will be dropped from a class in the following cases: + +- The class or its subobjects have virtual bases or virtual methods. +- The class or its subobjects have Objective-C pointer type members and ARC is + enabled. + }]; +} + def MSInheritanceDocs : Documentation { let Category = DocCatType; let Heading = "__single_inhertiance, __multiple_inheritance, __virtual_inheritance"; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2881,6 +2881,9 @@ def err_invalid_attribute_on_virtual_function : Error< "%0 attribute cannot be applied to virtual functions">; +def warn_cannot_use_trivial_abi : Warning< + "'trivial_abi' cannot be applied to %0">, InGroup; + // Availability attribute def warn_availability_unknown_platform : Warning< "unknown platform %0 in availability macro">, InGroup; Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -2236,7 +2236,7 @@ bool CheckNontrivialField(FieldDecl *FD); void DiagnoseNontrivial(const CXXRecordDecl *Record, CXXSpecialMember CSM); bool SpecialMemberIsTrivial(CXXMethodDecl *MD, CXXSpecialMember CSM, - bool Diagnose = false); + bool ForCall = false, bool Diagnose = false); CXXSpecialMember getSpecialMember(const CXXMethodDecl *MD); void ActOnLastBitfield(SourceLocation DeclStart, SmallVectorImpl &AllIvarDecls); Index: lib/AST/DeclCXX.cpp =================================================================== --- lib/AST/DeclCXX.cpp +++ lib/AST/DeclCXX.cpp @@ -88,10 +88,12 @@ DefaultedMoveConstructorIsDeleted(false), DefaultedMoveAssignmentIsDeleted(false), DefaultedDestructorIsDeleted(false), HasTrivialSpecialMembers(SMF_All), - DeclaredNonTrivialSpecialMembers(0), HasIrrelevantDestructor(true), + HasTrivialSpecialMembersForCall(SMF_All), + DeclaredNonTrivialSpecialMembers(0), + DeclaredNonTrivialSpecialMembersForCall(0), HasIrrelevantDestructor(true), HasConstexprNonCopyMoveConstructor(false), HasDefaultedDefaultConstructor(false), - CanPassInRegisters(true), + CanPassInRegisters(true), DropTrivialABI(false), DefaultedDefaultConstructorIsConstexpr(true), HasConstexprDefaultConstructor(false), HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false), @@ -200,6 +202,11 @@ CXXRecordDecl *BaseClassDecl = cast(BaseType->getAs()->getDecl()); + // Drop "trivial_abi" if this class is not trivial for the purpose of calls + // or has a virtual base. + if (!BaseClassDecl->canPassInRegisters() || Base->isVirtual()) + setDropTrivialABI(); + if (!BaseClassDecl->isEmpty()) { if (!data().Empty) { // C++0x [class]p7: @@ -281,6 +288,7 @@ // operator for a class X] is trivial [...] if: // -- class X has [...] no virtual base classes data().HasTrivialSpecialMembers &= SMF_Destructor; + data().HasTrivialSpecialMembersForCall &= SMF_Destructor; // C++0x [class]p7: // A standard-layout class is a class that: [...] @@ -314,6 +322,10 @@ // subobject is trivial, and if (!BaseClassDecl->hasTrivialCopyConstructor()) data().HasTrivialSpecialMembers &= ~SMF_CopyConstructor; + + if (!BaseClassDecl->hasTrivialCopyConstructorForCall()) + data().HasTrivialSpecialMembersForCall &= ~SMF_CopyConstructor; + // If the base class doesn't have a simple move constructor, we'll eagerly // declare it and perform overload resolution to determine which function // it actually calls. If it does have a simple move constructor, this @@ -321,6 +333,9 @@ if (!BaseClassDecl->hasTrivialMoveConstructor()) data().HasTrivialSpecialMembers &= ~SMF_MoveConstructor; + if (!BaseClassDecl->hasTrivialMoveConstructorForCall()) + data().HasTrivialSpecialMembersForCall &= ~SMF_MoveConstructor; + // C++0x [class.copy]p27: // A copy/move assignment operator for class X is trivial if [...] // [...] @@ -357,6 +372,9 @@ if (!BaseClassDecl->hasTrivialDestructor()) data().HasTrivialSpecialMembers &= ~SMF_Destructor; + if (!BaseClassDecl->hasTrivialDestructorForCall()) + data().HasTrivialSpecialMembersForCall &= ~SMF_Destructor; + if (!BaseClassDecl->hasIrrelevantDestructor()) data().HasIrrelevantDestructor = false; @@ -539,6 +557,10 @@ // assignment operator for a class X] is trivial [...] if: // -- class X has no virtual functions [...] data().HasTrivialSpecialMembers &= SMF_Destructor; + data().HasTrivialSpecialMembersForCall &= SMF_Destructor; + + // "trivial_abi" has to be dropped if this is a virtual function. + setDropTrivialABI(); // C++0x [class]p7: // A standard-layout class is a class that: [...] @@ -623,8 +645,10 @@ // C++11 [class.dtor]p5: // A destructor is trivial if [...] the destructor is not virtual. - if (DD->isVirtual()) + if (DD->isVirtual()) { data().HasTrivialSpecialMembers &= ~SMF_Destructor; + data().HasTrivialSpecialMembersForCall &= ~SMF_Destructor; + } } // Handle member functions. @@ -670,16 +694,27 @@ // If this is the first declaration of a special member, we no longer have // an implicit trivial special member. data().HasTrivialSpecialMembers &= - data().DeclaredSpecialMembers | ~SMKind; + data().DeclaredSpecialMembers | ~SMKind; + data().HasTrivialSpecialMembersForCall &= + data().DeclaredSpecialMembers | ~SMKind; if (!Method->isImplicit() && !Method->isUserProvided()) { // This method is user-declared but not user-provided. We can't work out // whether it's trivial yet (not until we get to the end of the class). // We'll handle this method in finishedDefaultedOrDeletedMember. - } else if (Method->isTrivial()) + } else if (Method->isTrivial()) { data().HasTrivialSpecialMembers |= SMKind; - else + data().HasTrivialSpecialMembersForCall |= SMKind; + } else { data().DeclaredNonTrivialSpecialMembers |= SMKind; + // If this is a user-provided function, do not set + // DeclaredNonTrivialSpecialMembersForCall here since we don't know + // yet whether the method would be considered non-trivial for the + // purpose of calls (attribute "trivial_abi" can be dropped from the + // class later, which can change the special method's triviality). + if (!Method->isUserProvided()) + data().DeclaredNonTrivialSpecialMembersForCall |= SMKind; + } // Note when we have declared a declared special member, and suppress the // implicit declaration of this special member. @@ -772,6 +807,13 @@ struct DefinitionData &Data = data(); Data.PlainOldData = false; Data.HasTrivialSpecialMembers = 0; + Data.HasTrivialSpecialMembersForCall = 0; + + // If a struct has Objective-C weak pointer members, it cannot have + // attribute "trivial_abi". + if (T.getObjCLifetime() == Qualifiers::OCL_Weak) + setDropTrivialABI(); + Data.HasIrrelevantDestructor = false; } else if (!Context.getLangOpts().ObjCAutoRefCount) { setHasObjectMember(true); @@ -883,6 +925,11 @@ FieldRec->data().NeedOverloadResolutionForDestructor; } + // If this field is of a type that is non-trivial for the purpose of + // calls, drop "trivial_abi". + if (!FieldRec->canPassInRegisters()) + setDropTrivialABI(); + // C++0x [class.ctor]p5: // A default constructor is trivial [...] if: // -- for all the non-static data members of its class that are of @@ -899,12 +946,19 @@ // member is trivial; if (!FieldRec->hasTrivialCopyConstructor()) data().HasTrivialSpecialMembers &= ~SMF_CopyConstructor; + + if (!FieldRec->hasTrivialCopyConstructorForCall()) + data().HasTrivialSpecialMembersForCall &= ~SMF_CopyConstructor; + // If the field doesn't have a simple move constructor, we'll eagerly // declare the move constructor for this class and we'll decide whether // it's trivial then. if (!FieldRec->hasTrivialMoveConstructor()) data().HasTrivialSpecialMembers &= ~SMF_MoveConstructor; + if (!FieldRec->hasTrivialMoveConstructorForCall()) + data().HasTrivialSpecialMembersForCall &= ~SMF_MoveConstructor; + // C++0x [class.copy]p27: // A copy/move assignment operator for class X is trivial if [...] // [...] @@ -921,6 +975,8 @@ if (!FieldRec->hasTrivialDestructor()) data().HasTrivialSpecialMembers &= ~SMF_Destructor; + if (!FieldRec->hasTrivialDestructorForCall()) + data().HasTrivialSpecialMembersForCall &= ~SMF_Destructor; if (!FieldRec->hasIrrelevantDestructor()) data().HasIrrelevantDestructor = false; if (FieldRec->hasObjectMember()) @@ -1103,6 +1159,23 @@ data().DeclaredNonTrivialSpecialMembers |= SMKind; } +void CXXRecordDecl::finishedUserProvidedMethod(CXXMethodDecl *D) { + unsigned SMKind = 0; + + if (CXXConstructorDecl *Constructor = dyn_cast(D)) { + if (Constructor->isCopyConstructor()) + SMKind = SMF_CopyConstructor; + else if (Constructor->isMoveConstructor()) + SMKind = SMF_MoveConstructor; + } else if (isa(D)) + SMKind = SMF_Destructor; + + if (D->isTrivialForCall()) + data().HasTrivialSpecialMembersForCall |= SMKind; + else + data().DeclaredNonTrivialSpecialMembersForCall |= SMKind; +} + bool CXXRecordDecl::isCLike() const { if (getTagKind() == TTK_Class || getTagKind() == TTK_Interface || !TemplateOrInstantiation.isNull()) Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -2201,6 +2201,12 @@ return false; } +bool QualType::destructInCallee() const { + if (const auto *RD = getTypePtr()->getAsCXXRecordDecl()) + return RD->destructInCallee(); + return false; +} + bool QualType::isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const { return !Context.getLangOpts().ObjCAutoRefCount && Context.getLangOpts().ObjCWeak && Index: lib/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ lib/CodeGen/CGCall.cpp @@ -3497,6 +3497,17 @@ bool HasAggregateEvalKind = hasAggregateEvaluationKind(type); + if (type.destructInCallee()) { + AggValueSlot Slot = CreateAggTemp(type, "agg.tmp"); + // If this is a C++ record passed using C ABI, do not destruct the argument + // in the caller function. + Slot.setExternallyDestructed(); + EmitAggExpr(E, Slot); + RValue RV = Slot.asRValue(); + args.add(RV, type); + return; + } + // In the Microsoft C++ ABI, aggregate arguments are destructed by the callee. // However, we still have to push an EH-only cleanup in case we unwind before // we make it to the call. Index: lib/CodeGen/CodeGenFunction.cpp =================================================================== --- lib/CodeGen/CodeGenFunction.cpp +++ lib/CodeGen/CodeGenFunction.cpp @@ -1140,6 +1140,14 @@ if (Ty->isVariablyModifiedType()) EmitVariablyModifiedType(Ty); + + // If this is a C++ record passed using C ABI, the callee is responsible for + // destructing the argument that is passed. + if (Ty.destructInCallee()) { + AutoVarEmission Emission(*VD); + Emission.Addr = GetAddrOfLocalVar(VD); + EmitAutoVarCleanups(Emission); + } } // Emit a location at the end of the prologue. if (CGDebugInfo *DI = getDebugInfo()) Index: lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- lib/CodeGen/MicrosoftCXXABI.cpp +++ lib/CodeGen/MicrosoftCXXABI.cpp @@ -844,7 +844,7 @@ // If the copy ctor has not yet been declared, we can read its triviality // off the AST. if (!RD->defaultedCopyConstructorIsDeleted() && - RD->hasTrivialCopyConstructor()) + RD->hasTrivialCopyConstructorForCall()) return RAA_Default; } else { // Otherwise, we need to find the copy constructor(s) and ask. Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -6436,6 +6436,9 @@ case AttributeList::AT_LayoutVersion: handleLayoutVersion(S, D, Attr); break; + case AttributeList::AT_TrivialABI: + handleSimpleAttribute(S, D, Attr); + break; case AttributeList::AT_MSNoVTable: handleSimpleAttribute(S, D, Attr); break; Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -5768,20 +5768,20 @@ if (D->needsImplicitCopyConstructor() && !D->defaultedCopyConstructorIsDeleted()) { - if (!D->hasTrivialCopyConstructor()) + if (!D->hasTrivialCopyConstructorForCall()) return false; HasNonDeletedCopyOrMove = true; } if (S.getLangOpts().CPlusPlus11 && D->needsImplicitMoveConstructor() && !D->defaultedMoveConstructorIsDeleted()) { - if (!D->hasTrivialMoveConstructor()) + if (!D->hasTrivialMoveConstructorForCall()) return false; HasNonDeletedCopyOrMove = true; } if (D->needsImplicitDestructor() && !D->defaultedDestructorIsDeleted() && - !D->hasTrivialDestructor()) + !D->hasTrivialDestructorForCall()) return false; for (const CXXMethodDecl *MD : D->methods()) { @@ -5794,7 +5794,7 @@ else if (!isa(MD)) continue; - if (!MD->isTrivial()) + if (!MD->isTrivialForCall()) return false; } @@ -5878,6 +5878,13 @@ } } + // Set HasTrivialSpecialMemberForCall if the record has attribute + // "trivial_abi". + bool HasTrivialABI = Record->hasAttr(); + + if (HasTrivialABI) + Record->setHasTrivialSpecialMemberForCall(); + bool HasMethodWithOverrideControl = false, HasOverridingMethodWithoutOverrideControl = false; if (!Record->isDependentType()) { @@ -5900,12 +5907,21 @@ if (!M->isImplicit() && !M->isUserProvided()) { if (CSM != CXXInvalid) { M->setTrivial(SpecialMemberIsTrivial(M, CSM)); + M->setTrivialForCall(SpecialMemberIsTrivial(M, CSM, true)); // Inform the class that we've finished declaring this member. Record->finishedDefaultedOrDeletedMember(M); } } + // Set triviality for the purpose of calls if this is a user-provided + // copy/move constructor or destructor. + if ((CSM == CXXCopyConstructor || CSM == CXXMoveConstructor || + CSM == CXXDestructor) && M->isUserProvided()) { + M->setTrivialForCall(HasTrivialABI); + Record->finishedUserProvidedMethod(M); + } + if (!M->isInvalidDecl() && M->isExplicitlyDefaulted() && M->hasAttr()) { if (getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2015) && @@ -7017,9 +7033,13 @@ /// /// If \p Selected is not \c NULL, \c *Selected will be filled in with the /// member that was most likely to be intended to be trivial, if any. +/// +/// If \p ForCall is true, look at CXXRecord::HasTrivialSpecialMembersForCall to +/// determine whether the special member is trivial. static bool findTrivialSpecialMember(Sema &S, CXXRecordDecl *RD, Sema::CXXSpecialMember CSM, unsigned Quals, - bool ConstRHS, CXXMethodDecl **Selected) { + bool ConstRHS, bool ForCall, + CXXMethodDecl **Selected) { if (Selected) *Selected = nullptr; @@ -7060,7 +7080,8 @@ // C++11 [class.dtor]p5: // A destructor is trivial if: // - all the direct [subobjects] have trivial destructors - if (RD->hasTrivialDestructor()) + if (RD->hasTrivialDestructor() || + (ForCall && RD->hasTrivialDestructorForCall())) return true; if (Selected) { @@ -7075,7 +7096,8 @@ // C++11 [class.copy]p12: // A copy constructor is trivial if: // - the constructor selected to copy each direct [subobject] is trivial - if (RD->hasTrivialCopyConstructor()) { + if (RD->hasTrivialCopyConstructor() || + (ForCall && RD->hasTrivialCopyConstructorForCall())) { if (Quals == Qualifiers::Const) // We must either select the trivial copy constructor or reach an // ambiguity; no need to actually perform overload resolution. @@ -7128,6 +7150,10 @@ // not supposed to! if (Selected) *Selected = SMOR.getMethod(); + + if (ForCall && + (CSM == Sema::CXXCopyConstructor || CSM == Sema::CXXMoveConstructor)) + return SMOR.getMethod()->isTrivialForCall(); return SMOR.getMethod()->isTrivial(); } @@ -7165,7 +7191,7 @@ static bool checkTrivialSubobjectCall(Sema &S, SourceLocation SubobjLoc, QualType SubType, bool ConstRHS, Sema::CXXSpecialMember CSM, - TrivialSubobjectKind Kind, + TrivialSubobjectKind Kind, bool ForCall, bool Diagnose) { CXXRecordDecl *SubRD = SubType->getAsCXXRecordDecl(); if (!SubRD) @@ -7173,7 +7199,8 @@ CXXMethodDecl *Selected; if (findTrivialSpecialMember(S, SubRD, CSM, SubType.getCVRQualifiers(), - ConstRHS, Diagnose ? &Selected : nullptr)) + ConstRHS, ForCall, + Diagnose ? &Selected : nullptr)) return true; if (Diagnose) { @@ -7203,7 +7230,7 @@ << Kind << SubType.getUnqualifiedType() << CSM; // Explain why the defaulted or deleted special member isn't trivial. - S.SpecialMemberIsTrivial(Selected, CSM, Diagnose); + S.SpecialMemberIsTrivial(Selected, CSM, false, Diagnose); } } @@ -7214,7 +7241,8 @@ /// trivial. static bool checkTrivialClassMembers(Sema &S, CXXRecordDecl *RD, Sema::CXXSpecialMember CSM, - bool ConstArg, bool Diagnose) { + bool ConstArg, bool ForCall, + bool Diagnose) { for (const auto *FI : RD->fields()) { if (FI->isInvalidDecl() || FI->isUnnamedBitfield()) continue; @@ -7224,7 +7252,7 @@ // Pretend anonymous struct or union members are members of this class. if (FI->isAnonymousStructOrUnion()) { if (!checkTrivialClassMembers(S, FieldType->getAsCXXRecordDecl(), - CSM, ConstArg, Diagnose)) + CSM, ConstArg, ForCall, Diagnose)) return false; continue; } @@ -7252,7 +7280,7 @@ bool ConstRHS = ConstArg && !FI->isMutable(); if (!checkTrivialSubobjectCall(S, FI->getLocation(), FieldType, ConstRHS, - CSM, TSK_Field, Diagnose)) + CSM, TSK_Field, ForCall, Diagnose)) return false; } @@ -7266,14 +7294,14 @@ bool ConstArg = (CSM == CXXCopyConstructor || CSM == CXXCopyAssignment); checkTrivialSubobjectCall(*this, RD->getLocation(), Ty, ConstArg, CSM, - TSK_CompleteObject, /*Diagnose*/true); + TSK_CompleteObject, false, /*Diagnose*/true); } /// Determine whether a defaulted or deleted special member function is trivial, /// as specified in C++11 [class.ctor]p5, C++11 [class.copy]p12, /// C++11 [class.copy]p25, and C++11 [class.dtor]p5. bool Sema::SpecialMemberIsTrivial(CXXMethodDecl *MD, CXXSpecialMember CSM, - bool Diagnose) { + bool ForCall, bool Diagnose) { assert(!MD->isUserProvided() && CSM != CXXInvalid && "not special enough"); CXXRecordDecl *RD = MD->getParent(); @@ -7350,7 +7378,8 @@ // destructors] for (const auto &BI : RD->bases()) if (!checkTrivialSubobjectCall(*this, BI.getLocStart(), BI.getType(), - ConstArg, CSM, TSK_BaseClass, Diagnose)) + ConstArg, CSM, TSK_BaseClass, ForCall, + Diagnose)) return false; // C++11 [class.ctor]p5, C++11 [class.dtor]p5: @@ -7365,7 +7394,7 @@ // -- for all of the non-static data members of its class that are of class // type (or array thereof), each such class has a trivial [default // constructor or destructor] - if (!checkTrivialClassMembers(*this, RD, CSM, ConstArg, Diagnose)) + if (!checkTrivialClassMembers(*this, RD, CSM, ConstArg, ForCall, Diagnose)) return false; // C++11 [class.dtor]p5: @@ -7565,12 +7594,22 @@ l->getName(); } + // See if trivial_abi has to be dropped. + auto RD = dyn_cast_or_null(TagDecl); + if (RD && RD->dropTrivialABI()) + if (const auto *Attr = RD->getAttr()) { + // Do not warn if this is a template instantiation. + if (!RD->getTemplateInstantiationPattern()) + Diag(Attr->getLocation(), diag::warn_cannot_use_trivial_abi) << RD; + RD->dropAttr(); + } + ActOnFields(S, RLoc, TagDecl, llvm::makeArrayRef( // strict aliasing violation! reinterpret_cast(FieldCollector->getCurFields()), FieldCollector->getCurNumFields()), LBrac, RBrac, AttrList); - CheckCompletedCXXClass(dyn_cast_or_null(TagDecl)); + CheckCompletedCXXClass(RD); } /// AddImplicitlyDeclaredMembersToClass - Adds any implicitly-declared @@ -10720,6 +10759,7 @@ // We don't need to use SpecialMemberIsTrivial here; triviality for // destructors is easy to compute. Destructor->setTrivial(ClassDecl->hasTrivialDestructor()); + Destructor->setTrivialForCall(ClassDecl->hasTrivialDestructorForCall()); // Note that we have declared this destructor. ++ASTContext::NumImplicitDestructorsDeclared; @@ -12025,6 +12065,11 @@ ? SpecialMemberIsTrivial(CopyConstructor, CXXCopyConstructor) : ClassDecl->hasTrivialCopyConstructor()); + CopyConstructor->setTrivialForCall( + ClassDecl->needsOverloadResolutionForCopyConstructor() + ? SpecialMemberIsTrivial(CopyConstructor, CXXCopyConstructor, true) + : ClassDecl->hasTrivialCopyConstructorForCall()); + // Note that we have declared this constructor. ++ASTContext::NumImplicitCopyConstructorsDeclared; @@ -12148,6 +12193,11 @@ ? SpecialMemberIsTrivial(MoveConstructor, CXXMoveConstructor) : ClassDecl->hasTrivialMoveConstructor()); + MoveConstructor->setTrivialForCall( + ClassDecl->needsOverloadResolutionForMoveConstructor() + ? SpecialMemberIsTrivial(MoveConstructor, CXXMoveConstructor, true) + : ClassDecl->hasTrivialMoveConstructorForCall()); + // Note that we have declared this constructor. ++ASTContext::NumImplicitMoveConstructorsDeclared; Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -7790,7 +7790,7 @@ diag::note_non_literal_user_provided_dtor : diag::note_non_literal_nontrivial_dtor) << RD; if (!Dtor->isUserProvided()) - SpecialMemberIsTrivial(Dtor, CXXDestructor, /*Diagnose*/true); + SpecialMemberIsTrivial(Dtor, CXXDestructor, false, /*Diagnose*/true); } return true; Index: test/CodeGenCXX/trivial_abi.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/trivial_abi.cpp @@ -0,0 +1,196 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++11 -emit-llvm -o - %s | FileCheck %s + +// CHECK: %[[STRUCT_SMALL:.*]] = type { i32* } +// CHECK: %[[STRUCT_LARGE:.*]] = type { i32*, [128 x i32] } +// CHECK: %[[STRUCT_TRIVIAL:.*]] = type { i32 } +// CHECK: %[[STRUCT_NONTRIVIAL:.*]] = type { i32 } + +struct __attribute__((trivial_abi)) Small { + int *p; + Small(); + ~Small(); + Small(const Small &); + Small &operator=(const Small &); +}; + +struct __attribute__((trivial_abi)) Large { + int *p; + int a[128]; + Large(); + ~Large(); + Large(const Large &); + Large &operator=(const Large &); +}; + +struct Trivial { + int a; +}; + +struct NonTrivial { + NonTrivial(); + ~NonTrivial(); + int a; +}; + +struct HasTrivial { + Small s; + Trivial m; +}; + +struct HasNonTrivial { + Small s; + NonTrivial m; +}; + +// CHECK: define void @_Z14testParamSmall5Small(i64 %[[A_COERCE:.*]]) +// CHECK: %[[A:.*]] = alloca %[[STRUCT_SMALL]], align 8 +// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], %[[STRUCT_SMALL]]* %[[A]], i32 0, i32 0 +// CHECK: %[[COERCE_VAL_IP:.*]] = inttoptr i64 %[[A_COERCE]] to i32* +// CHECK: store i32* %[[COERCE_VAL_IP]], i32** %[[COERCE_DIVE]], align 8 +// CHECK: %[[CALL:.*]] = call %[[STRUCT_SMALL]]* @_ZN5SmallD1Ev(%[[STRUCT_SMALL]]* %[[A]]) +// CHECK: ret void +// CHECK: } + +void testParamSmall(Small a) { +} + +// CHECK: define i64 @_Z15testReturnSmallv() +// CHECK: %[[RETVAL:.*]] = alloca %[[STRUCT_SMALL:.*]], align 8 +// CHECK: %[[CALL:.*]] = call %[[STRUCT_SMALL]]* @_ZN5SmallC1Ev(%[[STRUCT_SMALL]]* %[[RETVAL]]) +// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], %[[STRUCT_SMALL]]* %[[RETVAL]], i32 0, i32 0 +// CHECK: %[[V0:.*]] = load i32*, i32** %[[COERCE_DIVE]], align 8 +// CHECK: %[[COERCE_VAL_PI:.*]] = ptrtoint i32* %[[V0]] to i64 +// CHECK: ret i64 %[[COERCE_VAL_PI]] +// CHECK: } + +Small testReturnSmall() { + Small t; + return t; +} + +// CHECK: define void @_Z14testCallSmall0v() +// CHECK: %[[T:.*]] = alloca %[[STRUCT_SMALL:.*]], align 8 +// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_SMALL]], align 8 +// CHECK: %[[CALL:.*]] = call %[[STRUCT_SMALL]]* @_ZN5SmallC1Ev(%[[STRUCT_SMALL]]* %[[T]]) +// CHECK: %[[CALL1:.*]] = call %[[STRUCT_SMALL]]* @_ZN5SmallC1ERKS_(%[[STRUCT_SMALL]]* %[[AGG_TMP]], %[[STRUCT_SMALL]]* dereferenceable(8) %[[T]]) +// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], %[[STRUCT_SMALL]]* %[[AGG_TMP]], i32 0, i32 0 +// CHECK: %[[V0:.*]] = load i32*, i32** %[[COERCE_DIVE]], align 8 +// CHECK: %[[COERCE_VAL_PI:.*]] = ptrtoint i32* %[[V0]] to i64 +// CHECK: call void @_Z14testParamSmall5Small(i64 %[[COERCE_VAL_PI]]) +// CHECK: %[[CALL2:.*]] = call %[[STRUCT_SMALL]]* @_ZN5SmallD1Ev(%[[STRUCT_SMALL]]* %[[T]]) +// CHECK: ret void +// CHECK: } + +void testCallSmall0() { + Small t; + testParamSmall(t); +} + +// CHECK: define void @_Z14testCallSmall1v() +// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_SMALL:.*]], align 8 +// CHECK: %[[CALL:.*]] = call i64 @_Z15testReturnSmallv() +// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], %[[STRUCT_SMALL]]* %[[AGG_TMP]], i32 0, i32 0 +// CHECK: %[[COERCE_VAL_IP:.*]] = inttoptr i64 %[[CALL]] to i32* +// CHECK: store i32* %[[COERCE_VAL_IP]], i32** %[[COERCE_DIVE]], align 8 +// CHECK: %[[COERCE_DIVE1:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], %[[STRUCT_SMALL]]* %[[AGG_TMP]], i32 0, i32 0 +// CHECK: %[[V0:.*]] = load i32*, i32** %[[COERCE_DIVE1]], align 8 +// CHECK: %[[COERCE_VAL_PI:.*]] = ptrtoint i32* %[[V0]] to i64 +// CHECK: call void @_Z14testParamSmall5Small(i64 %[[COERCE_VAL_PI]]) +// CHECK: ret void +// CHECK: } + +void testCallSmall1() { + testParamSmall(testReturnSmall()); +} + +// CHECK: define void @_Z16testIgnoredSmallv() +// CHECK: %[[AGG_TMP_ENSURED:.*]] = alloca %[[STRUCT_SMALL:.*]], align 8 +// CHECK: %[[CALL:.*]] = call i64 @_Z15testReturnSmallv() +// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_SMALL]], %[[STRUCT_SMALL]]* %[[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK: %[[COERCE_VAL_IP:.*]] = inttoptr i64 %[[CALL]] to i32* +// CHECK: store i32* %[[COERCE_VAL_IP]], i32** %[[COERCE_DIVE]], align 8 +// CHECK: %[[CALL1:.*]] = call %[[STRUCT_SMALL]]* @_ZN5SmallD1Ev(%[[STRUCT_SMALL]]* %[[AGG_TMP_ENSURED]]) +// CHECK: ret void +// CHECK: } + +void testIgnoredSmall() { + testReturnSmall(); +} + +// CHECK: define void @_Z14testParamLarge5Large(%[[STRUCT_LARGE:.*]]* %[[A:.*]]) +// CHECK: %[[CALL:.*]] = call %[[STRUCT_LARGE]]* @_ZN5LargeD1Ev(%[[STRUCT_LARGE]]* %[[A]]) +// CHECK: ret void +// CHECK: } + +void testParamLarge(Large a) { +} + +// CHECK: define void @_Z15testReturnLargev(%[[STRUCT_LARGE:.*]]* noalias sret %[[AGG_RESULT:.*]]) +// CHECK: %[[CALL:.*]] = call %[[STRUCT_LARGE]]* @_ZN5LargeC1Ev(%[[STRUCT_LARGE]]* %[[AGG_RESULT]]) +// CHECK: ret void +// CHECK: } + +Large testReturnLarge() { + Large t; + return t; +} + +// CHECK: define void @_Z14testCallLarge0v() +// CHECK: %[[T:.*]] = alloca %[[STRUCT_LARGE:.*]], align 8 +// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_LARGE]], align 8 +// CHECK: %[[CALL:.*]] = call %[[STRUCT_LARGE]]* @_ZN5LargeC1Ev(%[[STRUCT_LARGE]]* %[[T]]) +// CHECK: %[[CALL1:.*]] = call %[[STRUCT_LARGE]]* @_ZN5LargeC1ERKS_(%[[STRUCT_LARGE]]* %[[AGG_TMP]], %[[STRUCT_LARGE]]* dereferenceable(520) %[[T]]) +// CHECK: call void @_Z14testParamLarge5Large(%[[STRUCT_LARGE]]* %[[AGG_TMP]]) +// CHECK: %[[CALL2:.*]] = call %[[STRUCT_LARGE]]* @_ZN5LargeD1Ev(%[[STRUCT_LARGE]]* %[[T]]) +// CHECK: ret void +// CHECK: } + +void testCallLarge0() { + Large t; + testParamLarge(t); +} + +// CHECK: define void @_Z14testCallLarge1v() +// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_LARGE:.*]], align 8 +// CHECK: call void @_Z15testReturnLargev(%[[STRUCT_LARGE]]* sret %[[AGG_TMP]]) +// CHECK: call void @_Z14testParamLarge5Large(%[[STRUCT_LARGE]]* %[[AGG_TMP]]) +// CHECK: ret void +// CHECK: } + +void testCallLarge1() { + testParamLarge(testReturnLarge()); +} + +// CHECK: define void @_Z16testIgnoredLargev() +// CHECK: %[[AGG_TMP_ENSURED:.*]] = alloca %[[STRUCT_LARGE:.*]], align 8 +// CHECK: call void @_Z15testReturnLargev(%[[STRUCT_LARGE]]* sret %[[AGG_TMP_ENSURED]]) +// CHECK: %[[CALL:.*]] = call %[[STRUCT_LARGE]]* @_ZN5LargeD1Ev(%[[STRUCT_LARGE]]* %[[AGG_TMP_ENSURED]]) +// CHECK: ret void +// CHECK: } + +void testIgnoredLarge() { + testReturnLarge(); +} + +// CHECK: define i64 @_Z20testReturnHasTrivialv() +// CHECK: %[[RETVAL:.*]] = alloca %[[STRUCT_TRIVIAL:.*]], align 4 +// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_TRIVIAL]], %[[STRUCT_TRIVIAL]]* %[[RETVAL]], i32 0, i32 0 +// CHECK: %[[V0:.*]] = load i32, i32* %[[COERCE_DIVE]], align 4 +// CHECK: %[[COERCE_VAL_II:.*]] = zext i32 %[[V0]] to i64 +// CHECK: ret i64 %[[COERCE_VAL_II]] +// CHECK: } + +Trivial testReturnHasTrivial() { + Trivial t; + return t; +} + +// CHECK: define void @_Z23testReturnHasNonTrivialv(%[[STRUCT_NONTRIVIAL:.*]]* noalias sret %[[AGG_RESULT:.*]]) +// CHECK: %[[CALL:.*]] = call %[[STRUCT_NONTRIVIAL]]* @_ZN10NonTrivialC1Ev(%[[STRUCT_NONTRIVIAL]]* %[[AGG_RESULT]]) +// CHECK: ret void +// CHECK: } + +NonTrivial testReturnHasNonTrivial() { + NonTrivial t; + return t; +} Index: test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- test/Misc/pragma-attribute-supported-attributes-list.test +++ test/Misc/pragma-attribute-supported-attributes-list.test @@ -2,7 +2,7 @@ // The number of supported attributes should never go down! -// CHECK: #pragma clang attribute supports 66 attributes: +// CHECK: #pragma clang attribute supports 67 attributes: // CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function) @@ -66,6 +66,7 @@ // CHECK-NEXT: TLSModel (SubjectMatchRule_variable_is_thread_local) // CHECK-NEXT: Target (SubjectMatchRule_function) // CHECK-NEXT: TestTypestate (SubjectMatchRule_function_is_member) +// CHECK-NEXT: TrivialABI (SubjectMatchRule_record) // CHECK-NEXT: WarnUnusedResult (SubjectMatchRule_objc_method, SubjectMatchRule_enum, SubjectMatchRule_record, SubjectMatchRule_hasType_functionType) // CHECK-NEXT: XRayInstrument (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: XRayLogArgs (SubjectMatchRule_function, SubjectMatchRule_objc_method) Index: test/SemaObjCXX/attr-trivial-abi.mm =================================================================== --- /dev/null +++ test/SemaObjCXX/attr-trivial-abi.mm @@ -0,0 +1,51 @@ +// RUN: %clang_cc1 -std=c++11 -fobjc-runtime-has-weak -fobjc-weak -fobjc-arc -fsyntax-only -verify %s + +void __attribute__((trivial_abi)) foo(); // expected-warning {{'trivial_abi' attribute only applies to classes}} + +struct [[clang::trivial_abi]] S0 { + int a; +}; + +struct __attribute__((trivial_abi)) S1 { + int a; +}; + +struct __attribute__((trivial_abi)) S2 { // expected-warning {{'trivial_abi' cannot be applied to 'S2'}} + __weak id a; +}; + +struct __attribute__((trivial_abi)) S3 { // expected-warning {{'trivial_abi' cannot be applied to 'S3'}} + virtual void m(); +}; + +struct S4 { + int a; +}; + +struct __attribute__((trivial_abi)) S5 : public virtual S4 { // expected-warning {{'trivial_abi' cannot be applied to 'S5'}} +}; + +struct __attribute__((trivial_abi)) S9 : public S4 { +}; + +struct S6 { + __weak id a; +}; + +struct __attribute__((trivial_abi)) S7 { // expected-warning {{'trivial_abi' cannot be applied to 'S7'}} + S6 a; +}; + +struct __attribute__((trivial_abi(1))) S8 { // expected-error {{'trivial_abi' attribute takes no arguments}} + int a; +}; + +template +struct __attribute__((trivial_abi)) S10 { + T p; +}; + +S10 p1; + +// Do not warn when 'trivial_abi' is used to annotate a template class. +S10<__weak id> p2;