Skip to content

Commit

Permalink
Add support for derived class special members hiding functions brough…
Browse files Browse the repository at this point in the history
…t 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.

llvm-svn: 269398
  • Loading branch information
zygoloid committed May 13, 2016
1 parent 845d0d7 commit 12e7931
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 39 deletions.
20 changes: 20 additions & 0 deletions clang/include/clang/AST/DeclCXX.h
Expand Up @@ -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.
/// @{
Expand Down Expand Up @@ -1308,6 +1316,18 @@ class CXXRecordDecl : public RecordDecl {
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;
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -4540,6 +4540,9 @@ class Sema {
/// 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);
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/ASTImporter.cpp
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/AST/DeclCXX.cpp
Expand Up @@ -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),
Expand Down Expand Up @@ -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) {
Expand Down
107 changes: 72 additions & 35 deletions clang/lib/Sema/SemaDeclCXX.cpp
Expand Up @@ -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);
}

Expand All @@ -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);
}

Expand All @@ -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);
}

Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Serialization/ASTReaderDecl.cpp
Expand Up @@ -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++];
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Serialization/ASTWriter.cpp
Expand Up @@ -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);
Expand Down
32 changes: 32 additions & 0 deletions clang/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:
Expand Down Expand Up @@ -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
5 changes: 3 additions & 2 deletions clang/test/SemaCUDA/implicit-member-target.cu
Expand Up @@ -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}}

}

Expand Down

0 comments on commit 12e7931

Please sign in to comment.