From: Richard Smith Date: Wed, 29 Jun 2016 01:10:27 +0000 (+0000) Subject: Mark inheriting constructors as deleted if the corresponding defaulted default X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=071180678d2add49ebfe64e5d6625fe01b235302;p=clang Mark inheriting constructors as deleted if the corresponding defaulted default constructor would be; this is effectively required by P0136R1. This has the effect of exposing the validity of the base class initialization steps to SFINAE checks. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@274088 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 245921324c..73e390f1ed 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -4295,20 +4295,25 @@ def err_new_abi_tag_on_redeclaration : Error< def note_deleted_dtor_no_operator_delete : Note< "virtual destructor requires an unambiguous, accessible 'operator delete'">; def note_deleted_special_member_class_subobject : Note< - "%select{default constructor|copy constructor|move constructor|" - "copy assignment operator|move assignment operator|destructor}0 of " + "%select{default constructor of|copy constructor of|move constructor of|" + "copy assignment operator of|move assignment operator of|destructor of|" + "constructor inherited by}0 " "%1 is implicitly deleted because " "%select{base class %3|%select{||||variant }4field %3}2 has " "%select{no|a deleted|multiple|an inaccessible|a non-trivial}4 " "%select{%select{default constructor|copy constructor|move constructor|copy " - "assignment operator|move assignment operator|destructor}0|destructor}5" + "assignment operator|move assignment operator|destructor|" + "%select{default|corresponding|default|default|default}4 constructor}0|" + "destructor}5" "%select{||s||}4">; def note_deleted_default_ctor_uninit_field : Note< - "default constructor of %0 is implicitly deleted because field %1 of " - "%select{reference|const-qualified}3 type %2 would not be initialized">; + "%select{default constructor of|constructor inherited by}0 " + "%1 is implicitly deleted because field %2 of " + "%select{reference|const-qualified}4 type %3 would not be initialized">; def note_deleted_default_ctor_all_const : Note< - "default constructor of %0 is implicitly deleted because all " - "%select{data members|data members of an anonymous union member}1" + "%select{default constructor of|constructor inherited by}0 " + "%1 is implicitly deleted because all " + "%select{data members|data members of an anonymous union member}2" " are const-qualified">; def note_deleted_copy_ctor_rvalue_reference : Note< "copy constructor of %0 is implicitly deleted because field %1 is of " @@ -6567,6 +6572,8 @@ def err_nontemporal_builtin_must_be_pointer_intfltptr_or_vector : Error< "pointer, or a vector of such types (%0 invalid)">; def err_deleted_function_use : Error<"attempt to use a deleted function">; +def err_deleted_inherited_ctor_use : Error< + "constructor inherited by %0 from base class %1 is implicitly deleted">; def err_kern_type_not_void_return : Error< "kernel function type %0 must have void return type">; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 456c1c96ee..8b2e6e8b5b 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -3634,6 +3634,7 @@ public: const ObjCInterfaceDecl *UnknownObjCClass=nullptr, bool ObjCPropertyAccess=false); void NoteDeletedFunction(FunctionDecl *FD); + void NoteDeletedInheritingConstructor(CXXConstructorDecl *CD); std::string getDeletedOrUnavailableSuffix(const FunctionDecl *FD); bool DiagnosePropertyAccessorMismatch(ObjCPropertyDecl *PD, ObjCMethodDecl *Getter, @@ -4460,9 +4461,12 @@ public: ArrayRef DynamicExceptionRanges, Expr *NoexceptExpr); + class InheritedConstructorInfo; + /// \brief Determine if a special member function should have a deleted /// definition when it is defaulted. bool ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM, + InheritedConstructorInfo *ICI = nullptr, bool Diagnose = false); /// \brief Declare the implicit default constructor for the given class. diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index a5d636e95e..d8bb3d0d95 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -5071,11 +5071,9 @@ static Sema::SpecialMemberOverloadResult *lookupCallFromSpecialMember( LHSQuals & Qualifiers::Volatile); } -namespace { -struct InheritedConstructorInfo { +class Sema::InheritedConstructorInfo { Sema &S; SourceLocation UseLoc; - ConstructorUsingShadowDecl *Shadow; /// A mapping from the base classes through which the constructor was /// inherited to the using shadow declaration in that base class (or a null @@ -5083,9 +5081,10 @@ struct InheritedConstructorInfo { llvm::DenseMap InheritedFromBases; +public: InheritedConstructorInfo(Sema &S, SourceLocation UseLoc, ConstructorUsingShadowDecl *Shadow) - : S(S), UseLoc(UseLoc), Shadow(Shadow) { + : S(S), UseLoc(UseLoc) { bool DiagnosedMultipleConstructedBases = false; CXXRecordDecl *ConstructedBase = nullptr; UsingDecl *ConstructedBaseUsing = nullptr; @@ -5152,7 +5151,6 @@ struct InheritedConstructorInfo { return std::make_pair(Ctor, false); } }; -} /// Is the special member function which would be selected to perform the /// specified operation on the specified class type a constexpr constructor? @@ -5161,7 +5159,7 @@ specialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl, Sema::CXXSpecialMember CSM, unsigned Quals, bool ConstRHS, CXXConstructorDecl *InheritedCtor = nullptr, - InheritedConstructorInfo *Inherited = nullptr) { + Sema::InheritedConstructorInfo *Inherited = nullptr) { // If we're inheriting a constructor, see if we need to call it for this base // class. if (InheritedCtor) { @@ -5189,7 +5187,7 @@ specialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl, static bool defaultedSpecialMemberIsConstexpr( Sema &S, CXXRecordDecl *ClassDecl, Sema::CXXSpecialMember CSM, bool ConstArg, CXXConstructorDecl *InheritedCtor = nullptr, - InheritedConstructorInfo *Inherited = nullptr) { + Sema::InheritedConstructorInfo *Inherited = nullptr) { if (!S.getLangOpts().CPlusPlus11) return false; @@ -5508,7 +5506,7 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) { // [For a] user-provided explicitly-defaulted function [...] if such a // function is implicitly defined as deleted, the program is ill-formed. Diag(MD->getLocation(), diag::err_out_of_line_default_deletes) << CSM; - ShouldDeleteSpecialMember(MD, CSM, /*Diagnose*/true); + ShouldDeleteSpecialMember(MD, CSM, nullptr, /*Diagnose*/true); HadError = true; } } @@ -5569,6 +5567,7 @@ struct SpecialMemberDeletionInfo { Sema &S; CXXMethodDecl *MD; Sema::CXXSpecialMember CSM; + Sema::InheritedConstructorInfo *ICI; bool Diagnose; // Properties of the special member, computed for convenience. @@ -5578,11 +5577,11 @@ struct SpecialMemberDeletionInfo { bool AllFieldsAreConst; SpecialMemberDeletionInfo(Sema &S, CXXMethodDecl *MD, - Sema::CXXSpecialMember CSM, bool Diagnose) - : S(S), MD(MD), CSM(CSM), Diagnose(Diagnose), - IsConstructor(false), IsAssignment(false), IsMove(false), - ConstArg(false), Loc(MD->getLocation()), - AllFieldsAreConst(true) { + Sema::CXXSpecialMember CSM, + Sema::InheritedConstructorInfo *ICI, bool Diagnose) + : S(S), MD(MD), CSM(CSM), ICI(ICI), Diagnose(Diagnose), + IsConstructor(false), IsAssignment(false), IsMove(false), + ConstArg(false), Loc(MD->getLocation()), AllFieldsAreConst(true) { switch (CSM) { case Sema::CXXDefaultConstructor: case Sema::CXXCopyConstructor: @@ -5614,6 +5613,10 @@ struct SpecialMemberDeletionInfo { bool inUnion() const { return MD->getParent()->isUnion(); } + Sema::CXXSpecialMember getEffectiveCSM() { + return ICI ? Sema::CXXInvalid : CSM; + } + /// Look up the corresponding special member in the given class. Sema::SpecialMemberOverloadResult *lookupIn(CXXRecordDecl *Class, unsigned Quals, bool IsMutable) { @@ -5690,13 +5693,13 @@ bool SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall( if (Field) { S.Diag(Field->getLocation(), diag::note_deleted_special_member_class_subobject) - << CSM << MD->getParent() << /*IsField*/true + << getEffectiveCSM() << MD->getParent() << /*IsField*/true << Field << DiagKind << IsDtorCallInCtor; } else { CXXBaseSpecifier *Base = Subobj.get(); S.Diag(Base->getLocStart(), diag::note_deleted_special_member_class_subobject) - << CSM << MD->getParent() << /*IsField*/false + << getEffectiveCSM() << MD->getParent() << /*IsField*/false << Base->getType() << DiagKind << IsDtorCallInCtor; } @@ -5755,7 +5758,29 @@ bool SpecialMemberDeletionInfo::shouldDeleteForBase(CXXBaseSpecifier *Base) { CXXRecordDecl *BaseClass = Base->getType()->getAsCXXRecordDecl(); // If program is correct, BaseClass cannot be null, but if it is, the error // must be reported elsewhere. - return BaseClass && shouldDeleteForClassSubobject(BaseClass, Base, 0); + if (!BaseClass) + return false; + // If we have an inheriting constructor, check whether we're calling an + // inherited constructor instead of a default constructor. + if (ICI) { + assert(CSM == Sema::CXXDefaultConstructor); + auto *BaseCtor = + ICI->findConstructorForBase(BaseClass, cast(MD) + ->getInheritedConstructor() + .getConstructor()) + .first; + if (BaseCtor) { + if (BaseCtor->isDeleted() && Diagnose) { + S.Diag(Base->getLocStart(), + diag::note_deleted_special_member_class_subobject) + << getEffectiveCSM() << MD->getParent() << /*IsField*/false + << Base->getType() << /*Deleted*/1 << /*IsDtorCallInCtor*/false; + S.NoteDeletedFunction(BaseCtor); + } + return BaseCtor->isDeleted(); + } + } + return shouldDeleteForClassSubobject(BaseClass, Base, 0); } /// Check whether we should delete a special member function due to the class @@ -5770,7 +5795,7 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) { if (FieldType->isReferenceType() && !FD->hasInClassInitializer()) { if (Diagnose) S.Diag(FD->getLocation(), diag::note_deleted_default_ctor_uninit_field) - << MD->getParent() << FD << FieldType << /*Reference*/0; + << !!ICI << MD->getParent() << FD << FieldType << /*Reference*/0; return true; } // C++11 [class.ctor]p5: any non-variant non-static data member of @@ -5782,7 +5807,7 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) { (!FieldRecord || !FieldRecord->hasUserProvidedDefaultConstructor())) { if (Diagnose) S.Diag(FD->getLocation(), diag::note_deleted_default_ctor_uninit_field) - << MD->getParent() << FD << FD->getType() << /*Const*/1; + << !!ICI << MD->getParent() << FD << FD->getType() << /*Const*/1; return true; } @@ -5841,7 +5866,7 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) { if (Diagnose) S.Diag(FieldRecord->getLocation(), diag::note_deleted_default_ctor_all_const) - << MD->getParent() << /*anonymous union*/1; + << !!ICI << MD->getParent() << /*anonymous union*/1; return true; } @@ -5869,7 +5894,7 @@ bool SpecialMemberDeletionInfo::shouldDeleteForAllConstMembers() { if (Diagnose) S.Diag(MD->getParent()->getLocation(), diag::note_deleted_default_ctor_all_const) - << MD->getParent() << /*not anonymous union*/0; + << !!ICI << MD->getParent() << /*not anonymous union*/0; return true; } return false; @@ -5879,6 +5904,7 @@ bool SpecialMemberDeletionInfo::shouldDeleteForAllConstMembers() { /// deleted, as specified in C++11 [class.ctor]p5, C++11 [class.copy]p11, /// C++11 [class.copy]p23, and C++11 [class.dtor]p5. bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM, + InheritedConstructorInfo *ICI, bool Diagnose) { if (MD->isInvalidDecl()) return false; @@ -5968,7 +5994,7 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM, } } - SpecialMemberDeletionInfo SMI(*this, MD, CSM, Diagnose); + SpecialMemberDeletionInfo SMI(*this, MD, CSM, ICI, Diagnose); for (auto &BI : RD->bases()) if (!BI.isVirtual() && @@ -9288,9 +9314,20 @@ Sema::findInheritingConstructor(SourceLocation Loc, DerivedCtor->setAccess(BaseCtor->getAccess()); DerivedCtor->setParams(ParamDecls); Derived->addDecl(DerivedCtor); + + if (ShouldDeleteSpecialMember(DerivedCtor, CXXDefaultConstructor, &ICI)) + SetDeclDeleted(DerivedCtor, UsingLoc); + return DerivedCtor; } +void Sema::NoteDeletedInheritingConstructor(CXXConstructorDecl *Ctor) { + InheritedConstructorInfo ICI(*this, Ctor->getLocation(), + Ctor->getInheritedConstructor().getShadowDecl()); + ShouldDeleteSpecialMember(Ctor, CXXDefaultConstructor, &ICI, + /*Diagnose*/true); +} + void Sema::DefineInheritingConstructor(SourceLocation CurrentLocation, CXXConstructorDecl *Constructor) { CXXRecordDecl *ClassDecl = Constructor->getParent(); @@ -11441,8 +11478,11 @@ Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType, bool RequiresZeroInit, unsigned ConstructKind, SourceRange ParenRange) { - if (auto *Shadow = dyn_cast(FoundDecl)) + if (auto *Shadow = dyn_cast(FoundDecl)) { Constructor = findInheritingConstructor(ConstructLoc, Constructor, Shadow); + if (DiagnoseUseOfDecl(Constructor, ConstructLoc)) + return ExprError(); + } return BuildCXXConstructExpr( ConstructLoc, DeclInitType, Constructor, Elidable, ExprArgs, diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index de65efca2e..56cf5d4fd3 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -216,11 +216,15 @@ void Sema::NoteDeletedFunction(FunctionDecl *Decl) { // deleted. This might fail, if that reason no longer applies. CXXSpecialMember CSM = getSpecialMember(Method); if (CSM != CXXInvalid) - ShouldDeleteSpecialMember(Method, CSM, /*Diagnose=*/true); + ShouldDeleteSpecialMember(Method, CSM, nullptr, /*Diagnose=*/true); return; } + auto *Ctor = dyn_cast(Decl); + if (Ctor && Ctor->isInheritingConstructor()) + return NoteDeletedInheritingConstructor(Ctor); + Diag(Decl->getLocation(), diag::note_availability_specified_here) << Decl << true; } @@ -346,7 +350,13 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, // See if this is a deleted function. if (FunctionDecl *FD = dyn_cast(D)) { if (FD->isDeleted()) { - Diag(Loc, diag::err_deleted_function_use); + auto *Ctor = dyn_cast(FD); + if (Ctor && Ctor->isInheritingConstructor()) + Diag(Loc, diag::err_deleted_inherited_ctor_use) + << Ctor->getParent() + << Ctor->getInheritedConstructor().getConstructor()->getParent(); + else + Diag(Loc, diag::err_deleted_function_use); NoteDeletedFunction(FD); return true; } diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 1ed2d40ba1..c7b88be316 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -5741,8 +5741,11 @@ PerformConstructorInitialization(Sema &S, : Kind.getParenRange(); if (auto *Shadow = dyn_cast( - Step.Function.FoundDecl.getDecl())) + Step.Function.FoundDecl.getDecl())) { Constructor = S.findInheritingConstructor(Loc, Constructor, Shadow); + if (S.DiagnoseUseOfDecl(Constructor, Loc)) + return ExprError(); + } S.MarkFunctionReferenced(Loc, Constructor); CurInit = new (S.Context) CXXTemporaryObjectExpr( diff --git a/test/CXX/drs/dr15xx.cpp b/test/CXX/drs/dr15xx.cpp index 71f2f719fb..439823410a 100644 --- a/test/CXX/drs/dr15xx.cpp +++ b/test/CXX/drs/dr15xx.cpp @@ -41,9 +41,9 @@ namespace dr1573 { // dr1573: 3.9 constexpr F f = F(0); // expected-error {{constant expression}} expected-note {{constructor inherited from base class 'C'}} // inherited constructor is effectively deleted if the user-written constructor would be - struct G { G(int); }; // expected-note {{declared here}} - struct H : G { using G::G; G g; }; // expected-error {{cannot use constructor inherited from base class 'G'; member 'g' of 'dr1573::H' does not have a default constructor}} expected-note {{declared here}} - H h(0); // expected-note {{first required here}} + struct G { G(int); }; + struct H : G { using G::G; G g; }; // expected-note {{constructor inherited by 'H' is implicitly deleted because field 'g' has no default constructor}} + H h(0); // expected-error {{constructor inherited by 'H' from base class 'G' is implicitly deleted}} #endif } diff --git a/test/CXX/special/class.init/class.inhctor.init/p1.cpp b/test/CXX/special/class.init/class.inhctor.init/p1.cpp index 346b0b05de..e07d879df8 100644 --- a/test/CXX/special/class.init/class.inhctor.init/p1.cpp +++ b/test/CXX/special/class.init/class.inhctor.init/p1.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -std=c++11 -verify %s namespace std_example { - struct B1 { // expected-note {{declared here}} + struct B1 { B1(int, ...) {} }; @@ -25,11 +25,11 @@ namespace std_example { } struct D2 : B2 { - using B2::B2; // expected-error {{cannot use constructor inherited from base class 'B2'; member 'b' of 'std_example::D2' does not have a default constructor}} - B1 b; // expected-note {{member}} + using B2::B2; + B1 b; // expected-note {{constructor inherited by 'D2' is implicitly deleted because field 'b' has no default constructor}} }; - D2 f(1.0); // expected-note {{inherited constructor for 'D2' first required here}} + D2 f(1.0); // expected-error {{constructor inherited by 'D2' from base class 'B2' is implicitly deleted}} struct W { W(int); @@ -53,7 +53,7 @@ namespace std_example { } namespace vbase { - struct V { // expected-note 2{{declared here}} + struct V { V(int); }; @@ -61,17 +61,17 @@ namespace vbase { A() = delete; // expected-note 2{{deleted here}} expected-note {{deleted}} using V::V; }; - struct B : virtual V { + struct B : virtual V { // expected-note {{no default constructor}} B() = delete; // expected-note 2{{deleted here}} B(int, int); using V::V; }; struct C : B { // expected-note {{deleted default constructor}} - using B::B; // expected-error {{cannot use constructor inherited from base class 'B'; base class 'vbase::V' of 'vbase::C' does not have a default constructor}} + using B::B; }; - struct D : A, C { // expected-note {{deleted default constructor}} + struct D : A, C { // expected-note {{deleted default constructor}} expected-note {{deleted corresponding constructor}} using A::A; - using C::C; // expected-error {{cannot use constructor inherited from base class 'C'; base class 'vbase::V' of 'vbase::D' does not have a default constructor}} expected-error {{call to deleted constructor of 'vbase::A'}} + using C::C; }; A a0; // expected-error {{deleted}} @@ -81,10 +81,10 @@ namespace vbase { B b2(0, 0); C c0; // expected-error {{deleted}} C c1(0); - C c2(0, 0); // expected-note {{first required here}} - D d0; // expected-error {{implicitly-deleted}} + C c2(0, 0); // expected-error {{deleted}} + D d0; // expected-error {{deleted}} D d1(0); - D d2(0, 0); // expected-note {{first required here}} + D d2(0, 0); // expected-error {{deleted}} } namespace constexpr_init_order { diff --git a/test/SemaCXX/cxx11-inheriting-ctors.cpp b/test/SemaCXX/cxx11-inheriting-ctors.cpp index 8e053212ed..9c33ac05cc 100644 --- a/test/SemaCXX/cxx11-inheriting-ctors.cpp +++ b/test/SemaCXX/cxx11-inheriting-ctors.cpp @@ -44,3 +44,15 @@ namespace DefaultCtorConflict { B b; } c; } + +namespace InvalidConstruction { + struct A { A(int); }; + struct B { B() = delete; }; + struct C : A, B { using A::A; }; + // Initialization here is performed as if by a defaulted default constructor, + // which would be ill-formed (in the immediate context) in this case because + // it would be defined as deleted. + template void f(decltype(T(0))*); + template int &f(...); + int &r = f(0); +}