From 37b415dd0637005d5d01d75e7a55b860f7fc79d9 Mon Sep 17 00:00:00 2001 From: Richard Smith <richard-llvm@metafoo.co.uk> Date: Fri, 13 May 2016 06:47:56 +0000 Subject: [PATCH] Add support for derived class special members hiding functions brought in from a base class via a using-declaration. If a class has a using-declaration declaring either a constructor or an assignment operator, eagerly declare its special members in case they need to displace a shadow declaration from a using-declaration. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@269398 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclCXX.h | 20 ++++ include/clang/Sema/Sema.h | 3 + lib/AST/ASTImporter.cpp | 2 + lib/AST/DeclCXX.cpp | 10 ++ lib/Sema/SemaDeclCXX.cpp | 107 ++++++++++++------ lib/Serialization/ASTReaderDecl.cpp | 4 + lib/Serialization/ASTWriter.cpp | 2 + .../basic.namespace/namespace.udecl/p12.cpp | 32 ++++++ test/SemaCUDA/implicit-member-target.cu | 5 +- test/SemaCXX/constructor-recovery.cpp | 4 +- 10 files changed, 150 insertions(+), 39 deletions(-) diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 8906d9c798b..20aa7d1ab48 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -382,6 +382,14 @@ class CXXRecordDecl : public RecordDecl { /// provided default ctor also doesn't have an in-class initializer. unsigned HasUninitializedFields : 1; + /// \brief True if there are any member using-declarations that inherit + /// constructors from a base class. + unsigned HasInheritedConstructor : 1; + + /// \brief True if there are any member using-declarations named + /// 'operator='. + unsigned HasInheritedAssignment : 1; + /// \brief These flags are \c true if a defaulted corresponding special /// member can't be fully analyzed without performing overload resolution. /// @{ @@ -1308,6 +1316,18 @@ public: return data().HasNonLiteralTypeFieldsOrBases; } + /// \brief Determine whether this class has a using-declaration that names + /// a base class constructor. + bool hasInheritedConstructor() const { + return data().HasInheritedConstructor; + } + + /// \brief Determine whether this class has a using-declaration that names + /// a base class assignment operator. + bool hasInheritedAssignment() const { + return data().HasInheritedAssignment; + } + /// \brief Determine whether this class is considered trivially copyable per /// (C++11 [class]p6). bool isTriviallyCopyable() const; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 0d1a0af780e..6db8d7e1ea5 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -4540,6 +4540,9 @@ public: /// class. void ForceDeclarationOfImplicitMembers(CXXRecordDecl *Class); + /// \brief Check a completed declaration of an implicit special member. + void CheckImplicitSpecialMemberDeclaration(Scope *S, FunctionDecl *FD); + /// \brief Determine whether the given function is an implicitly-deleted /// special member function. bool isImplicitlyDeleted(FunctionDecl *FD); diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 8665d809ae4..ec6655391e9 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -2115,6 +2115,8 @@ bool ASTNodeImporter::ImportDefinition(RecordDecl *From, RecordDecl *To, ToData.HasUninitializedReferenceMember = FromData.HasUninitializedReferenceMember; ToData.HasUninitializedFields = FromData.HasUninitializedFields; + ToData.HasInheritedConstructor = FromData.HasInheritedConstructor; + ToData.HasInheritedAssignment = FromData.HasInheritedAssignment; ToData.NeedOverloadResolutionForMoveConstructor = FromData.NeedOverloadResolutionForMoveConstructor; ToData.NeedOverloadResolutionForMoveAssignment diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 385ac0b83e9..f22f2ee0afe 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -53,6 +53,7 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) HasPublicFields(false), HasMutableFields(false), HasVariantMembers(false), HasOnlyCMembers(true), HasInClassInitializer(false), HasUninitializedReferenceMember(false), HasUninitializedFields(false), + HasInheritedConstructor(false), HasInheritedAssignment(false), NeedOverloadResolutionForMoveConstructor(false), NeedOverloadResolutionForMoveAssignment(false), NeedOverloadResolutionForDestructor(false), @@ -955,6 +956,15 @@ void CXXRecordDecl::addedMember(Decl *D) { data().Conversions.get(Ctx).addDecl(Ctx, Shadow, Shadow->getAccess()); } } + + if (UsingDecl *Using = dyn_cast<UsingDecl>(D)) { + if (Using->getDeclName().getNameKind() == + DeclarationName::CXXConstructorName) + data().HasInheritedConstructor = true; + + if (Using->getDeclName().getCXXOverloadedOperator() == OO_Equal) + data().HasInheritedAssignment = true; + } } void CXXRecordDecl::finishedDefaultedOrDeletedMember(CXXMethodDecl *D) { diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 5967c20ea6e..e3cca7f8105 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -6455,20 +6455,28 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) { if (!ClassDecl->hasUserDeclaredConstructor()) ++ASTContext::NumImplicitDefaultConstructors; + // If this class inherited any constructors, declare the default constructor + // now in case it displaces one from a base class. + if (ClassDecl->needsImplicitDefaultConstructor() && + ClassDecl->hasInheritedConstructor()) + DeclareImplicitDefaultConstructor(ClassDecl); + if (!ClassDecl->hasUserDeclaredCopyConstructor()) { ++ASTContext::NumImplicitCopyConstructors; // If the properties or semantics of the copy constructor couldn't be // determined while the class was being declared, force a declaration // of it now. - if (ClassDecl->needsOverloadResolutionForCopyConstructor()) + if (ClassDecl->needsOverloadResolutionForCopyConstructor() || + ClassDecl->hasInheritedConstructor()) DeclareImplicitCopyConstructor(ClassDecl); } if (getLangOpts().CPlusPlus11 && ClassDecl->needsImplicitMoveConstructor()) { ++ASTContext::NumImplicitMoveConstructors; - if (ClassDecl->needsOverloadResolutionForMoveConstructor()) + if (ClassDecl->needsOverloadResolutionForMoveConstructor() || + ClassDecl->hasInheritedConstructor()) DeclareImplicitMoveConstructor(ClassDecl); } @@ -6480,7 +6488,8 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) { // it shows up in the right place in the vtable and that we diagnose // problems with the implicit exception specification. if (ClassDecl->isDynamicClass() || - ClassDecl->needsOverloadResolutionForCopyAssignment()) + ClassDecl->needsOverloadResolutionForCopyAssignment() || + ClassDecl->hasInheritedAssignment()) DeclareImplicitCopyAssignment(ClassDecl); } @@ -6489,7 +6498,8 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) { // Likewise for the move assignment operator. if (ClassDecl->isDynamicClass() || - ClassDecl->needsOverloadResolutionForMoveAssignment()) + ClassDecl->needsOverloadResolutionForMoveAssignment() || + ClassDecl->hasInheritedAssignment()) DeclareImplicitMoveAssignment(ClassDecl); } @@ -8898,10 +8908,11 @@ namespace { struct DeclaringSpecialMember { Sema &S; Sema::SpecialMemberDecl D; + Sema::ContextRAII SavedContext; bool WasAlreadyBeingDeclared; DeclaringSpecialMember(Sema &S, CXXRecordDecl *RD, Sema::CXXSpecialMember CSM) - : S(S), D(RD, CSM) { + : S(S), D(RD, CSM), SavedContext(S, RD) { WasAlreadyBeingDeclared = !S.SpecialMembersBeingDeclared.insert(D).second; if (WasAlreadyBeingDeclared) // This almost never happens, but if it does, ensure that our cache @@ -8923,6 +8934,20 @@ struct DeclaringSpecialMember { }; } +void Sema::CheckImplicitSpecialMemberDeclaration(Scope *S, FunctionDecl *FD) { + // Look up any existing declarations, but don't trigger declaration of all + // implicit special members with this name. + DeclarationName Name = FD->getDeclName(); + LookupResult R(*this, Name, SourceLocation(), LookupOrdinaryName, + ForRedeclaration); + for (auto *D : FD->getParent()->lookup(Name)) + if (auto *Acceptable = R.getAcceptableDecl(D)) + R.addDecl(Acceptable); + R.resolveKind(); + + CheckFunctionDeclaration(S, FD, R, /*IsExplicitSpecialization*/false); +} + CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( CXXRecordDecl *ClassDecl) { // C++ [class.ctor]p5: @@ -8971,13 +8996,16 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( // constructors is easy to compute. DefaultCon->setTrivial(ClassDecl->hasTrivialDefaultConstructor()); - if (ShouldDeleteSpecialMember(DefaultCon, CXXDefaultConstructor)) - SetDeclDeleted(DefaultCon, ClassLoc); - // Note that we have declared this constructor. ++ASTContext::NumImplicitDefaultConstructorsDeclared; - if (Scope *S = getScopeForContext(ClassDecl)) + Scope *S = getScopeForContext(ClassDecl); + CheckImplicitSpecialMemberDeclaration(S, DefaultCon); + + if (ShouldDeleteSpecialMember(DefaultCon, CXXDefaultConstructor)) + SetDeclDeleted(DefaultCon, ClassLoc); + + if (S) PushOnScopeChains(DefaultCon, S, false); ClassDecl->addDecl(DefaultCon); @@ -9433,20 +9461,21 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { FunctionProtoType::ExtProtoInfo EPI = getImplicitMethodEPI(*this, Destructor); Destructor->setType(Context.getFunctionType(Context.VoidTy, None, EPI)); - AddOverriddenMethods(ClassDecl, Destructor); - // We don't need to use SpecialMemberIsTrivial here; triviality for // destructors is easy to compute. Destructor->setTrivial(ClassDecl->hasTrivialDestructor()); - if (ShouldDeleteSpecialMember(Destructor, CXXDestructor)) - SetDeclDeleted(Destructor, ClassLoc); - // Note that we have declared this destructor. ++ASTContext::NumImplicitDestructorsDeclared; + Scope *S = getScopeForContext(ClassDecl); + CheckImplicitSpecialMemberDeclaration(S, Destructor); + + if (ShouldDeleteSpecialMember(Destructor, CXXDestructor)) + SetDeclDeleted(Destructor, ClassLoc); + // Introduce this destructor into its scope. - if (Scope *S = getScopeForContext(ClassDecl)) + if (S) PushOnScopeChains(Destructor, S, false); ClassDecl->addDecl(Destructor); @@ -10147,20 +10176,21 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) { nullptr); CopyAssignment->setParams(FromParam); - AddOverriddenMethods(ClassDecl, CopyAssignment); - CopyAssignment->setTrivial( ClassDecl->needsOverloadResolutionForCopyAssignment() ? SpecialMemberIsTrivial(CopyAssignment, CXXCopyAssignment) : ClassDecl->hasTrivialCopyAssignment()); - if (ShouldDeleteSpecialMember(CopyAssignment, CXXCopyAssignment)) - SetDeclDeleted(CopyAssignment, ClassLoc); - // Note that we have added this copy-assignment operator. ++ASTContext::NumImplicitCopyAssignmentOperatorsDeclared; - if (Scope *S = getScopeForContext(ClassDecl)) + Scope *S = getScopeForContext(ClassDecl); + CheckImplicitSpecialMemberDeclaration(S, CopyAssignment); + + if (ShouldDeleteSpecialMember(CopyAssignment, CXXCopyAssignment)) + SetDeclDeleted(CopyAssignment, ClassLoc); + + if (S) PushOnScopeChains(CopyAssignment, S, false); ClassDecl->addDecl(CopyAssignment); @@ -10538,22 +10568,23 @@ CXXMethodDecl *Sema::DeclareImplicitMoveAssignment(CXXRecordDecl *ClassDecl) { nullptr); MoveAssignment->setParams(FromParam); - AddOverriddenMethods(ClassDecl, MoveAssignment); - MoveAssignment->setTrivial( ClassDecl->needsOverloadResolutionForMoveAssignment() ? SpecialMemberIsTrivial(MoveAssignment, CXXMoveAssignment) : ClassDecl->hasTrivialMoveAssignment()); + // Note that we have added this copy-assignment operator. + ++ASTContext::NumImplicitMoveAssignmentOperatorsDeclared; + + Scope *S = getScopeForContext(ClassDecl); + CheckImplicitSpecialMemberDeclaration(S, MoveAssignment); + if (ShouldDeleteSpecialMember(MoveAssignment, CXXMoveAssignment)) { ClassDecl->setImplicitMoveAssignmentIsDeleted(); SetDeclDeleted(MoveAssignment, ClassLoc); } - // Note that we have added this copy-assignment operator. - ++ASTContext::NumImplicitMoveAssignmentOperatorsDeclared; - - if (Scope *S = getScopeForContext(ClassDecl)) + if (S) PushOnScopeChains(MoveAssignment, S, false); ClassDecl->addDecl(MoveAssignment); @@ -10979,13 +11010,16 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor( ? SpecialMemberIsTrivial(CopyConstructor, CXXCopyConstructor) : ClassDecl->hasTrivialCopyConstructor()); - if (ShouldDeleteSpecialMember(CopyConstructor, CXXCopyConstructor)) - SetDeclDeleted(CopyConstructor, ClassLoc); - // Note that we have declared this constructor. ++ASTContext::NumImplicitCopyConstructorsDeclared; - if (Scope *S = getScopeForContext(ClassDecl)) + Scope *S = getScopeForContext(ClassDecl); + CheckImplicitSpecialMemberDeclaration(S, CopyConstructor); + + if (ShouldDeleteSpecialMember(CopyConstructor, CXXCopyConstructor)) + SetDeclDeleted(CopyConstructor, ClassLoc); + + if (S) PushOnScopeChains(CopyConstructor, S, false); ClassDecl->addDecl(CopyConstructor); @@ -11156,15 +11190,18 @@ CXXConstructorDecl *Sema::DeclareImplicitMoveConstructor( ? SpecialMemberIsTrivial(MoveConstructor, CXXMoveConstructor) : ClassDecl->hasTrivialMoveConstructor()); + // Note that we have declared this constructor. + ++ASTContext::NumImplicitMoveConstructorsDeclared; + + Scope *S = getScopeForContext(ClassDecl); + CheckImplicitSpecialMemberDeclaration(S, MoveConstructor); + if (ShouldDeleteSpecialMember(MoveConstructor, CXXMoveConstructor)) { ClassDecl->setImplicitMoveConstructorIsDeleted(); SetDeclDeleted(MoveConstructor, ClassLoc); } - // Note that we have declared this constructor. - ++ASTContext::NumImplicitMoveConstructorsDeclared; - - if (Scope *S = getScopeForContext(ClassDecl)) + if (S) PushOnScopeChains(MoveConstructor, S, false); ClassDecl->addDecl(MoveConstructor); diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 6b933f2bba1..1b8044e1238 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -1466,6 +1466,8 @@ void ASTDeclReader::ReadCXXDefinitionData( Data.HasInClassInitializer = Record[Idx++]; Data.HasUninitializedReferenceMember = Record[Idx++]; Data.HasUninitializedFields = Record[Idx++]; + Data.HasInheritedConstructor = Record[Idx++]; + Data.HasInheritedAssignment = Record[Idx++]; Data.NeedOverloadResolutionForMoveConstructor = Record[Idx++]; Data.NeedOverloadResolutionForMoveAssignment = Record[Idx++]; Data.NeedOverloadResolutionForDestructor = Record[Idx++]; @@ -1593,6 +1595,8 @@ void ASTDeclReader::MergeDefinitionData( MATCH_FIELD(HasInClassInitializer) MATCH_FIELD(HasUninitializedReferenceMember) MATCH_FIELD(HasUninitializedFields) + MATCH_FIELD(HasInheritedConstructor) + MATCH_FIELD(HasInheritedAssignment) MATCH_FIELD(NeedOverloadResolutionForMoveConstructor) MATCH_FIELD(NeedOverloadResolutionForMoveAssignment) MATCH_FIELD(NeedOverloadResolutionForDestructor) diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 29a20b2e45e..7540bc03d15 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -5489,6 +5489,8 @@ void ASTRecordWriter::AddCXXDefinitionData(const CXXRecordDecl *D) { Record->push_back(Data.HasInClassInitializer); Record->push_back(Data.HasUninitializedReferenceMember); Record->push_back(Data.HasUninitializedFields); + Record->push_back(Data.HasInheritedConstructor); + Record->push_back(Data.HasInheritedAssignment); Record->push_back(Data.NeedOverloadResolutionForMoveConstructor); Record->push_back(Data.NeedOverloadResolutionForMoveAssignment); Record->push_back(Data.NeedOverloadResolutionForDestructor); diff --git a/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p12.cpp b/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p12.cpp index cc28bf6c28c..ce43720cb2d 100644 --- a/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p12.cpp +++ b/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p12.cpp @@ -1,3 +1,5 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s // RUN: %clang_cc1 -fsyntax-only -verify %s // C++03 [namespace.udecl]p12: @@ -161,3 +163,33 @@ namespace test4 { d.bar<int>(3); // expected-error {{'bar' is a protected member}} } } + +namespace test5 { + struct Derived; + struct Base { + void operator=(const Derived&); + }; + struct Derived : Base { + // Hidden by implicit derived class operator. + using Base::operator=; + }; + void f(Derived d) { + d = d; + } +} + +#if __cplusplus >= 201103L +namespace test6 { + struct Derived; + struct Base { + void operator=(Derived&&); + }; + struct Derived : Base { + // Hidden by implicit derived class operator. + using Base::operator=; + }; + void f(Derived d) { + d = Derived(); + } +} +#endif diff --git a/test/SemaCUDA/implicit-member-target.cu b/test/SemaCUDA/implicit-member-target.cu index 6064560f2c6..242d345fb93 100644 --- a/test/SemaCUDA/implicit-member-target.cu +++ b/test/SemaCUDA/implicit-member-target.cu @@ -60,13 +60,14 @@ struct A3_with_device_ctors { struct B3_with_implicit_ctors : A3_with_device_ctors { }; +// expected-note@-2 2{{call to __device__ function from __host__ function}} +// expected-note@-3 {{default constructor}} -// expected-note@-3 {{copy constructor of 'B3_with_implicit_ctors' is implicitly deleted}} void hostfoo3() { B3_with_implicit_ctors b; // this is OK because the inferred default ctor // here is __host__ - B3_with_implicit_ctors b2 = b; // expected-error {{call to implicitly-deleted copy constructor}} + B3_with_implicit_ctors b2 = b; // expected-error {{no matching constructor}} } diff --git a/test/SemaCXX/constructor-recovery.cpp b/test/SemaCXX/constructor-recovery.cpp index c1bb4362835..a0d8441012c 100644 --- a/test/SemaCXX/constructor-recovery.cpp +++ b/test/SemaCXX/constructor-recovery.cpp @@ -1,9 +1,9 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -struct C { +struct C { // expected-note 1+{{candidate}} virtual C() = 0; // expected-error{{constructor cannot be declared 'virtual'}} }; void f() { - C c; + C c; // expected-error {{no matching constructor}} } -- GitLab