diff options
Diffstat (limited to 'clang')
-rw-r--r-- | clang/include/clang/AST/DeclCXX.h | 11 | ||||
-rw-r--r-- | clang/lib/AST/ASTImporter.cpp | 6 | ||||
-rw-r--r-- | clang/lib/AST/DeclCXX.cpp | 38 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGClass.cpp | 14 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 34 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTReaderDecl.cpp | 6 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTWriter.cpp | 3 | ||||
-rw-r--r-- | clang/test/CXX/drs/dr16xx.cpp | 118 | ||||
-rw-r--r-- | clang/test/CXX/drs/dr21xx.cpp | 24 | ||||
-rw-r--r-- | clang/test/CodeGenCXX/constructors.cpp | 56 | ||||
-rw-r--r-- | clang/test/CodeGenCXX/implicit-exception-spec.cpp | 22 | ||||
-rw-r--r-- | clang/test/SemaCXX/implicit-exception-spec.cpp | 58 |
12 files changed, 354 insertions, 36 deletions
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 637a20aa6c9..d663dbeea87 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -441,9 +441,10 @@ class CXXRecordDecl : public RecordDecl { /// either by the user or implicitly. unsigned DeclaredSpecialMembers : 6; - /// \brief Whether an implicit copy constructor would have a const-qualified - /// parameter. - unsigned ImplicitCopyConstructorHasConstParam : 1; + /// \brief Whether an implicit copy constructor could have a const-qualified + /// parameter, for initializing virtual bases and for other subobjects. + unsigned ImplicitCopyConstructorCanHaveConstParamForVBase : 1; + unsigned ImplicitCopyConstructorCanHaveConstParamForNonVBase : 1; /// \brief Whether an implicit copy assignment operator would have a /// const-qualified parameter. @@ -882,7 +883,9 @@ public: /// \brief Determine whether an implicit copy constructor for this type /// would have a parameter with a const-qualified reference type. bool implicitCopyConstructorHasConstParam() const { - return data().ImplicitCopyConstructorHasConstParam; + return data().ImplicitCopyConstructorCanHaveConstParamForNonVBase && + (isAbstract() || + data().ImplicitCopyConstructorCanHaveConstParamForVBase); } /// \brief Determine whether this class has a copy constructor with diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 780d0bc7663..525d82e22d6 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -2363,8 +2363,10 @@ bool ASTNodeImporter::ImportDefinition(RecordDecl *From, RecordDecl *To, ToData.UserProvidedDefaultConstructor = FromData.UserProvidedDefaultConstructor; ToData.DeclaredSpecialMembers = FromData.DeclaredSpecialMembers; - ToData.ImplicitCopyConstructorHasConstParam - = FromData.ImplicitCopyConstructorHasConstParam; + ToData.ImplicitCopyConstructorCanHaveConstParamForVBase + = FromData.ImplicitCopyConstructorCanHaveConstParamForVBase; + ToData.ImplicitCopyConstructorCanHaveConstParamForNonVBase + = FromData.ImplicitCopyConstructorCanHaveConstParamForNonVBase; ToData.ImplicitCopyAssignmentHasConstParam = FromData.ImplicitCopyAssignmentHasConstParam; ToData.HasDeclaredCopyConstructorWithConstParam diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 3846ace1aa0..74183701deb 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -68,7 +68,8 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) HasConstexprDefaultConstructor(false), HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false), UserProvidedDefaultConstructor(false), DeclaredSpecialMembers(0), - ImplicitCopyConstructorHasConstParam(true), + ImplicitCopyConstructorCanHaveConstParamForVBase(true), + ImplicitCopyConstructorCanHaveConstParamForNonVBase(true), ImplicitCopyAssignmentHasConstParam(true), HasDeclaredCopyConstructorWithConstParam(false), HasDeclaredCopyAssignmentWithConstParam(false), IsLambda(false), @@ -227,7 +228,7 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, // 'const B&' or 'const volatile B&' [...] if (CXXRecordDecl *VBaseDecl = VBase.getType()->getAsCXXRecordDecl()) if (!VBaseDecl->hasCopyConstructorWithConstParam()) - data().ImplicitCopyConstructorHasConstParam = false; + data().ImplicitCopyConstructorCanHaveConstParamForVBase = false; // C++1z [dcl.init.agg]p1: // An aggregate is a class with [...] no virtual base classes @@ -264,6 +265,14 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, // In the definition of a constexpr constructor [...] // -- the class shall not have any virtual base classes data().DefaultedDefaultConstructorIsConstexpr = false; + + // C++1z [class.copy]p8: + // The implicitly-declared copy constructor for a class X will have + // the form 'X::X(const X&)' if each potentially constructed subobject + // has a copy constructor whose first parameter is of type + // 'const B&' or 'const volatile B&' [...] + if (!BaseClassDecl->hasCopyConstructorWithConstParam()) + data().ImplicitCopyConstructorCanHaveConstParamForVBase = false; } else { // C++ [class.ctor]p5: // A default constructor is trivial [...] if: @@ -306,6 +315,14 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, // default constructor is constexpr. if (!BaseClassDecl->hasConstexprDefaultConstructor()) data().DefaultedDefaultConstructorIsConstexpr = false; + + // C++1z [class.copy]p8: + // The implicitly-declared copy constructor for a class X will have + // the form 'X::X(const X&)' if each potentially constructed subobject + // has a copy constructor whose first parameter is of type + // 'const B&' or 'const volatile B&' [...] + if (!BaseClassDecl->hasCopyConstructorWithConstParam()) + data().ImplicitCopyConstructorCanHaveConstParamForNonVBase = false; } // C++ [class.ctor]p3: @@ -325,14 +342,6 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, if (!BaseClassDecl->hasCopyAssignmentWithConstParam()) data().ImplicitCopyAssignmentHasConstParam = false; - // C++11 [class.copy]p8: - // The implicitly-declared copy constructor for a class X will have - // the form 'X::X(const X&)' if each direct [...] base class B of X - // has a copy constructor whose first parameter is of type - // 'const B&' or 'const volatile B&' [...] - if (!BaseClassDecl->hasCopyConstructorWithConstParam()) - data().ImplicitCopyConstructorHasConstParam = false; - // A class has an Objective-C object member if... or any of its bases // has an Objective-C object member. if (BaseClassDecl->hasObjectMember()) @@ -916,12 +925,11 @@ void CXXRecordDecl::addedMember(Decl *D) { // C++11 [class.copy]p8: // The implicitly-declared copy constructor for a class X will have - // the form 'X::X(const X&)' if [...] for all the non-static data - // members of X that are of a class type M (or array thereof), each - // such class type has a copy constructor whose first parameter is - // of type 'const M&' or 'const volatile M&'. + // the form 'X::X(const X&)' if each potentially constructed subobject + // of a class type M (or array thereof) has a copy constructor whose + // first parameter is of type 'const M&' or 'const volatile M&'. if (!FieldRec->hasCopyConstructorWithConstParam()) - data().ImplicitCopyConstructorHasConstParam = false; + data().ImplicitCopyConstructorCanHaveConstParamForNonVBase = false; // C++11 [class.copy]p18: // The implicitly-declared copy assignment oeprator for a class X will diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index dce1e817d08..365da4f1351 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -1390,6 +1390,20 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) { const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CurGD.getDecl()); CXXDtorType DtorType = CurGD.getDtorType(); + // For an abstract class, non-base destructors are never used (and can't + // be emitted in general, because vbase dtors may not have been validated + // by Sema), but the Itanium ABI doesn't make them optional and Clang may + // in fact emit references to them from other compilations, so emit them + // as functions containing a trap instruction. + if (DtorType != Dtor_Base && Dtor->getParent()->isAbstract()) { + llvm::CallInst *TrapCall = EmitTrapCall(llvm::Intrinsic::trap); + TrapCall->setDoesNotReturn(); + TrapCall->setDoesNotThrow(); + Builder.CreateUnreachable(); + Builder.ClearInsertionPoint(); + return; + } + Stmt *Body = Dtor->getBody(); if (Body) incrementProfileCounter(Body); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 557d2f02245..d3d5edfdce2 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -5081,6 +5081,10 @@ Sema::MarkBaseAndMemberDestructorsReferenced(SourceLocation Location, DiagnoseUseOfDecl(Dtor, Location); } + // We only potentially invoke the destructors of potentially constructed + // subobjects. + bool VisitVirtualBases = !ClassDecl->isAbstract(); + llvm::SmallPtrSet<const RecordType *, 8> DirectVirtualBases; // Bases. @@ -5089,8 +5093,11 @@ Sema::MarkBaseAndMemberDestructorsReferenced(SourceLocation Location, const RecordType *RT = Base.getType()->getAs<RecordType>(); // Remember direct virtual bases. - if (Base.isVirtual()) + if (Base.isVirtual()) { + if (!VisitVirtualBases) + continue; DirectVirtualBases.insert(RT); + } CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(RT->getDecl()); // If our base class is invalid, we probably can't get its dtor anyway. @@ -5112,6 +5119,9 @@ Sema::MarkBaseAndMemberDestructorsReferenced(SourceLocation Location, MarkFunctionReferenced(Location, Dtor); DiagnoseUseOfDecl(Dtor, Location); } + + if (!VisitVirtualBases) + return; // Virtual bases. for (const auto &VBase : ClassDecl->vbases()) { @@ -6881,11 +6891,13 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM, SpecialMemberDeletionInfo SMI(*this, MD, CSM, ICI, Diagnose); // Per DR1611, do not consider virtual bases of constructors of abstract - // classes, since we are not going to construct them. For assignment - // operators, we only assign (and thus only consider) direct bases. - if (SMI.visit(SMI.IsConstructor ? SMI.VisitPotentiallyConstructedBases : - SMI.IsAssignment ? SMI.VisitDirectBases : - SMI.VisitAllBases)) + // classes, since we are not going to construct them. + // Per DR1658, do not consider virtual bases of destructors of abstract + // classes either. + // Per DR2180, for assignment operators we only assign (and thus only + // consider) direct bases. + if (SMI.visit(SMI.IsAssignment ? SMI.VisitDirectBases + : SMI.VisitPotentiallyConstructedBases)) return true; if (SMI.shouldDeleteForAllConstMembers()) @@ -10192,15 +10204,15 @@ ComputeDefaultedSpecialMemberExceptionSpec( // destructor without a noexcept-specifier, is potentially-throwing if and // only if any of the destructors for any of its potentially constructed // subojects is potentially throwing. - // FIXME: We should respect the first rule but ignore the "potentially - // constructed" in the second rule to resolve a core issue (no number yet) - // that would have us reject: + // FIXME: We respect the first rule but ignore the "potentially constructed" + // in the second rule to resolve a core issue (no number yet) that would have + // us reject: // struct A { virtual void f() = 0; virtual ~A() noexcept(false) = 0; }; // struct B : A {}; // struct C : B { void f(); }; // ... due to giving B::~B() a non-throwing exception specification. - // For now we don't implement either. - Info.visit(Info.VisitAllBases); + Info.visit(Info.IsConstructor ? Info.VisitPotentiallyConstructedBases + : Info.VisitAllBases); return Info.ExceptSpec; } diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 7971c30b488..5b911744092 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1524,7 +1524,8 @@ void ASTDeclReader::ReadCXXDefinitionData( Data.ComputedVisibleConversions = Record.readInt(); Data.UserProvidedDefaultConstructor = Record.readInt(); Data.DeclaredSpecialMembers = Record.readInt(); - Data.ImplicitCopyConstructorHasConstParam = Record.readInt(); + Data.ImplicitCopyConstructorCanHaveConstParamForVBase = Record.readInt(); + Data.ImplicitCopyConstructorCanHaveConstParamForNonVBase = Record.readInt(); Data.ImplicitCopyAssignmentHasConstParam = Record.readInt(); Data.HasDeclaredCopyConstructorWithConstParam = Record.readInt(); Data.HasDeclaredCopyAssignmentWithConstParam = Record.readInt(); @@ -1654,7 +1655,8 @@ void ASTDeclReader::MergeDefinitionData( // ComputedVisibleConversions is handled below. MATCH_FIELD(UserProvidedDefaultConstructor) OR_FIELD(DeclaredSpecialMembers) - MATCH_FIELD(ImplicitCopyConstructorHasConstParam) + MATCH_FIELD(ImplicitCopyConstructorCanHaveConstParamForVBase) + MATCH_FIELD(ImplicitCopyConstructorCanHaveConstParamForNonVBase) MATCH_FIELD(ImplicitCopyAssignmentHasConstParam) OR_FIELD(HasDeclaredCopyConstructorWithConstParam) OR_FIELD(HasDeclaredCopyAssignmentWithConstParam) diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 5fe693ba8e1..cdc1cf5bcb0 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -5703,7 +5703,8 @@ void ASTRecordWriter::AddCXXDefinitionData(const CXXRecordDecl *D) { Record->push_back(Data.ComputedVisibleConversions); Record->push_back(Data.UserProvidedDefaultConstructor); Record->push_back(Data.DeclaredSpecialMembers); - Record->push_back(Data.ImplicitCopyConstructorHasConstParam); + Record->push_back(Data.ImplicitCopyConstructorCanHaveConstParamForVBase); + Record->push_back(Data.ImplicitCopyConstructorCanHaveConstParamForNonVBase); Record->push_back(Data.ImplicitCopyAssignmentHasConstParam); Record->push_back(Data.HasDeclaredCopyConstructorWithConstParam); Record->push_back(Data.HasDeclaredCopyAssignmentWithConstParam); diff --git a/clang/test/CXX/drs/dr16xx.cpp b/clang/test/CXX/drs/dr16xx.cpp index c9f084db73a..08ae92570ce 100644 --- a/clang/test/CXX/drs/dr16xx.cpp +++ b/clang/test/CXX/drs/dr16xx.cpp @@ -3,6 +3,13 @@ // RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++1z -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +namespace dr1611 { // dr1611: dup 1658 + struct A { A(int); }; + struct B : virtual A { virtual void f() = 0; }; + struct C : B { C() : A(0) {} void f(); }; + C c; +} + namespace dr1684 { // dr1684: 3.6 #if __cplusplus >= 201103L struct NonLiteral { // expected-note {{because}} @@ -101,3 +108,114 @@ namespace dr1653 { // dr1653: 4 c++17 b -= 1; // ok } } + +namespace dr1658 { // dr1658: 5 + namespace DefCtor { + class A { A(); }; // expected-note 0-2{{here}} + class B { ~B(); }; // expected-note 0-2{{here}} + + // The stars align! An abstract class does not construct its virtual bases. + struct C : virtual A { C(); virtual void foo() = 0; }; + C::C() = default; // ok, not deleted, expected-error 0-1{{extension}} + struct D : virtual B { D(); virtual void foo() = 0; }; + D::D() = default; // ok, not deleted, expected-error 0-1{{extension}} + + // In all other cases, we are not so lucky. + struct E : A { E(); virtual void foo() = 0; }; +#if __cplusplus < 201103L + E::E() = default; // expected-error {{private default constructor}} expected-error {{extension}} expected-note {{here}} +#else + E::E() = default; // expected-error {{would delete}} expected-note@-4{{inaccessible default constructor}} +#endif + struct F : virtual A { F(); }; +#if __cplusplus < 201103L + F::F() = default; // expected-error {{private default constructor}} expected-error {{extension}} expected-note {{here}} +#else + F::F() = default; // expected-error {{would delete}} expected-note@-4{{inaccessible default constructor}} +#endif + + struct G : B { G(); virtual void foo() = 0; }; +#if __cplusplus < 201103L + G::G() = default; // expected-error@-2 {{private destructor}} expected-error {{extension}} expected-note {{here}} +#else + G::G() = default; // expected-error {{would delete}} expected-note@-4{{inaccessible destructor}} +#endif + struct H : virtual B { H(); }; +#if __cplusplus < 201103L + H::H() = default; // expected-error@-2 {{private destructor}} expected-error {{extension}} expected-note {{here}} +#else + H::H() = default; // expected-error {{would delete}} expected-note@-4{{inaccessible destructor}} +#endif + } + + namespace Dtor { + class B { ~B(); }; // expected-note 0-2{{here}} + + struct D : virtual B { ~D(); virtual void foo() = 0; }; + D::~D() = default; // ok, not deleted, expected-error 0-1{{extension}} + + struct G : B { ~G(); virtual void foo() = 0; }; +#if __cplusplus < 201103L + G::~G() = default; // expected-error@-2 {{private destructor}} expected-error {{extension}} expected-note {{here}} +#else + G::~G() = default; // expected-error {{would delete}} expected-note@-4{{inaccessible destructor}} +#endif + struct H : virtual B { ~H(); }; +#if __cplusplus < 201103L + H::~H() = default; // expected-error@-2 {{private destructor}} expected-error {{extension}} expected-note {{here}} +#else + H::~H() = default; // expected-error {{would delete}} expected-note@-4{{inaccessible destructor}} +#endif + } + + namespace MemInit { + struct A { A(int); }; // expected-note {{here}} + struct B : virtual A { + B() {} + virtual void f() = 0; + }; + struct C : virtual A { + C() {} // expected-error {{must explicitly initialize}} + }; + } + + namespace CopyCtorParamType { + struct A { A(A&); }; + struct B : virtual A { virtual void f() = 0; }; + struct C : virtual A { virtual void f(); }; + struct D : A { virtual void f() = 0; }; + + struct X { + friend B::B(const B&) throw(); + friend C::C(C&); + friend D::D(D&); + }; + } + + namespace CopyCtor { + class A { A(const A&); A(A&&); }; // expected-note 0-4{{here}} expected-error 0-1{{extension}} + + struct C : virtual A { C(const C&); C(C&&); virtual void foo() = 0; }; // expected-error 0-1{{extension}} + C::C(const C&) = default; // expected-error 0-1{{extension}} + C::C(C&&) = default; // expected-error 0-2{{extension}} + + struct E : A { E(const E&); E(E&&); virtual void foo() = 0; }; // expected-error 0-1{{extension}} +#if __cplusplus < 201103L + E::E(const E&) = default; // expected-error {{private copy constructor}} expected-error {{extension}} expected-note {{here}} + E::E(E&&) = default; // expected-error {{private move constructor}} expected-error 2{{extension}} expected-note {{here}} +#else + E::E(const E&) = default; // expected-error {{would delete}} expected-note@-5{{inaccessible copy constructor}} + E::E(E&&) = default; // expected-error {{would delete}} expected-note@-6{{inaccessible move constructor}} +#endif + struct F : virtual A { F(const F&); F(F&&); }; // expected-error 0-1{{extension}} +#if __cplusplus < 201103L + F::F(const F&) = default; // expected-error {{private copy constructor}} expected-error {{extension}} expected-note {{here}} + F::F(F&&) = default; // expected-error {{private move constructor}} expected-error 2{{extension}} expected-note {{here}} +#else + F::F(const F&) = default; // expected-error {{would delete}} expected-note@-5{{inaccessible copy constructor}} + F::F(F&&) = default; // expected-error {{would delete}} expected-note@-6{{inaccessible move constructor}} +#endif + } + + // assignment case is superseded by dr2180 +} diff --git a/clang/test/CXX/drs/dr21xx.cpp b/clang/test/CXX/drs/dr21xx.cpp new file mode 100644 index 00000000000..78fc0bec40b --- /dev/null +++ b/clang/test/CXX/drs/dr21xx.cpp @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++1z -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors + +namespace dr2180 { // dr2180: yes + class A { + A &operator=(const A &); // expected-note 0-2{{here}} + A &operator=(A &&); // expected-note 0-2{{here}} expected-error 0-1{{extension}} + }; + + struct B : virtual A { + B &operator=(const B &); + B &operator=(B &&); // expected-error 0-1{{extension}} + virtual void foo() = 0; + }; +#if __cplusplus < 201103L + B &B::operator=(const B&) = default; // expected-error {{private member}} expected-error {{extension}} expected-note {{here}} + B &B::operator=(B&&) = default; // expected-error {{private member}} expected-error 2{{extension}} expected-note {{here}} +#else + B &B::operator=(const B&) = default; // expected-error {{would delete}} expected-note@-9{{inaccessible copy assignment}} + B &B::operator=(B&&) = default; // expected-error {{would delete}} expected-note@-10{{inaccessible move assignment}} +#endif +} diff --git a/clang/test/CodeGenCXX/constructors.cpp b/clang/test/CodeGenCXX/constructors.cpp index ecbe5bb45b6..a8981403834 100644 --- a/clang/test/CodeGenCXX/constructors.cpp +++ b/clang/test/CodeGenCXX/constructors.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -emit-llvm -o - | FileCheck %s --implicit-check-not=should_not_appear_in_output struct Member { int x; Member(); Member(int); Member(const Member &); }; struct VBase { int x; VBase(); VBase(int); VBase(const VBase &); }; @@ -109,3 +109,57 @@ namespace test1 { // CHECK: [[THIS:%.*]] = load [[B:%.*]]*, [[B:%.*]]** // CHECK-NEXT: ret void } + +// Ensure that we +// a) emit the ABI-required but useless complete object and deleting destructor +// symbols for an abstract class, and +// b) do *not* emit references to virtual base destructors for an abstract class +// +// Our approach to this is to give these functions a body that simply traps. +// +// FIXME: We should ideally not create these symbols at all, but Clang can +// actually generate references to them in other TUs in some cases, so we can't +// stop emitting them without breaking ABI. See: +// +// https://github.com/itanium-cxx-abi/cxx-abi/issues/10 +namespace abstract { + // Note, the destructor of this class is not instantiated here. + template<typename T> struct should_not_appear_in_output { + ~should_not_appear_in_output() { int arr[-(int)sizeof(T)]; } + }; + + struct X { ~X(); }; + + struct A : virtual should_not_appear_in_output<int>, X { + virtual ~A() = 0; + }; + + // CHECK-LABEL: define void @_ZN8abstract1AD2Ev( + // CHECK: call {{.*}}@_ZN8abstract1XD2Ev( + // CHECK: ret + + // CHECK-LABEL: define void @_ZN8abstract1AD1Ev( + // CHECK: call {{.*}}@llvm.trap( + // CHECK: unreachable + + // CHECK-LABEL: define void @_ZN8abstract1AD0Ev( + // CHECK: call {{.*}}@llvm.trap( + // CHECK: unreachable + A::~A() {} + + struct B : virtual should_not_appear_in_output<int>, X { + virtual void f() = 0; + ~B(); + }; + + // CHECK-LABEL: define void @_ZN8abstract1BD2Ev( + // CHECK: call {{.*}}@_ZN8abstract1XD2Ev( + // CHECK: ret + + // CHECK-LABEL: define void @_ZN8abstract1BD1Ev( + // CHECK: call {{.*}}@llvm.trap( + // CHECK: unreachable + + // CHECK-NOT: @_ZN8abstract1BD0Ev( + B::~B() {} +} diff --git a/clang/test/CodeGenCXX/implicit-exception-spec.cpp b/clang/test/CodeGenCXX/implicit-exception-spec.cpp new file mode 100644 index 00000000000..cf363bd685a --- /dev/null +++ b/clang/test/CodeGenCXX/implicit-exception-spec.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 %s -triple=x86_64-linux-gnu -emit-llvm -std=c++11 -o - -fcxx-exceptions -fexceptions | FileCheck -check-prefix=CHECK %s + +struct A { + A(); + A(const A&); + A(A&&); +}; +struct B : virtual A { + virtual void f() = 0; +}; +struct C : B { + void f(); +}; + +// CHECK-DAG: define {{.*}} @_ZN1BC2Ev({{.*}} #[[NOUNWIND:[0-9]*]] +C c1; +// CHECK-DAG: define {{.*}} @_ZN1BC2ERKS_({{.*}} #[[NOUNWIND]] +C c2(c1); +// CHECK-DAG: define {{.*}} @_ZN1BC2EOS_({{.*}} #[[NOUNWIND]] +C c3(static_cast<C&&>(c1)); + +// CHECK-DAG: #[[NOUNWIND]] = {{{.*}} nounwind diff --git a/clang/test/SemaCXX/implicit-exception-spec.cpp b/clang/test/SemaCXX/implicit-exception-spec.cpp index fc86d1810ba..f400c222de8 100644 --- a/clang/test/SemaCXX/implicit-exception-spec.cpp +++ b/clang/test/SemaCXX/implicit-exception-spec.cpp @@ -92,3 +92,61 @@ namespace ImplicitDtorExceptionSpec { } e; }; } + +struct nothrow_t {} nothrow; +void *operator new(decltype(sizeof(0)), nothrow_t) noexcept; + +namespace PotentiallyConstructed { + template<bool NE> struct A { + A() noexcept(NE); + A(const A&) noexcept(NE); + A(A&&) noexcept(NE); + A &operator=(const A&) noexcept(NE); + A &operator=(A&&) noexcept(NE); + ~A() noexcept(NE); + }; + + template<bool NE> struct B : virtual A<NE> {}; + + template<bool NE> struct C : virtual A<NE> { + virtual void f() = 0; // expected-note 2{{unimplemented}} + }; + + template<bool NE> struct D final : C<NE> { + void f(); + }; + + template<typename T, bool A, bool B, bool C, bool D, bool E, bool F> void check() { + T *p = nullptr; + T &a = *p; + static_assert(noexcept(a = a) == D, ""); + static_assert(noexcept(a = static_cast<T&&>(a)) == E, ""); + static_assert(noexcept(delete &a) == F, ""); // expected-warning 2{{abstract}} + + // These are last because the first failure here causes instantiation to bail out. + static_assert(noexcept(new (nothrow) T()) == A, ""); // expected-error 2{{abstract}} + static_assert(noexcept(new (nothrow) T(a)) == B, ""); + static_assert(noexcept(new (nothrow) T(static_cast<T&&>(a))) == C, ""); + } + + template void check<A<false>, 0, 0, 0, 0, 0, 0>(); + template void check<A<true >, 1, 1, 1, 1, 1, 1>(); + template void check<B<false>, 0, 0, 0, 0, 0, 0>(); + template void check<B<true >, 1, 1, 1, 1, 1, 1>(); + template void check<C<false>, 1, 1, 1, 0, 0, 0>(); // expected-note {{instantiation}} + template void check<C<true >, 1, 1, 1, 1, 1, 1>(); // expected-note {{instantiation}} + template void check<D<false>, 0, 0, 0, 0, 0, 0>(); + template void check<D<true >, 1, 1, 1, 1, 1, 1>(); + + // ... the above trick doesn't work for this case... + struct Cfalse : virtual A<false> { + virtual void f() = 0; + + Cfalse() noexcept; + Cfalse(const Cfalse&) noexcept; + Cfalse(Cfalse&&) noexcept; + }; + Cfalse::Cfalse() noexcept = default; + Cfalse::Cfalse(const Cfalse&) noexcept = default; + Cfalse::Cfalse(Cfalse&&) noexcept = default; +} |