C++ modules: if a class is defined in multiple modules (for instance, because

it is an implicit instantiation of a class template specialization), pick the
first-loaded definition to be the canonical definition, and merge all other
definitions into it.

This is still rather incomplete -- we need to extend every form of declaration
that can appear within a CXXRecordDecl to be redeclarable if it came from an
AST file (this includes fields, enumerators, ...).

llvm-svn: 190315
This commit is contained in:
Richard Smith 2013-09-09 16:55:27 +00:00
parent 1b18a5ec28
commit d55889a655
12 changed files with 196 additions and 100 deletions

View File

@ -1450,7 +1450,7 @@ public:
ClassTemplateSpecializationDecl *getMostRecentDecl() {
CXXRecordDecl *Recent
= cast<CXXRecordDecl>(CXXRecordDecl::getMostRecentDecl());
if (!isa<ClassTemplateSpecializationDecl>(Recent)) {
while (!isa<ClassTemplateSpecializationDecl>(Recent)) {
// FIXME: Does injected class name need to be in the redeclarations chain?
assert(Recent->isInjectedClassName() && Recent->getPreviousDecl());
Recent = Recent->getPreviousDecl();

View File

@ -912,6 +912,11 @@ private:
MergedDeclsMap::iterator
combineStoredMergedDecls(Decl *Canon, serialization::GlobalDeclID CanonID);
/// \brief A mapping from DeclContexts to the semantic DeclContext that we
/// are treating as the definition of the entity. This is used, for instance,
/// when merging implicit instantiations of class templates across modules.
llvm::DenseMap<DeclContext *, DeclContext *> MergedDeclContexts;
/// \brief When reading a Stmt tree, Stmt operands are placed in this stack.
SmallVector<Stmt *, 16> StmtStack;

View File

@ -2506,7 +2506,7 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *RD,
SmallVector<NamedDecl *, 8> Candidates(R.begin(), R.end());
for (SmallVectorImpl<NamedDecl *>::iterator I = Candidates.begin(),
E = Candidates.end();
E = Candidates.end();
I != E; ++I) {
NamedDecl *Cand = *I;

View File

@ -291,7 +291,11 @@ namespace clang {
template<typename T>
void mergeRedeclarable(Redeclarable<T> *D, RedeclarableResult &Redecl);
template<typename T>
void mergeRedeclarable(Redeclarable<T> *D, T *Existing,
RedeclarableResult &Redecl);
// FIXME: Reorder according to DeclNodes.td?
void VisitObjCMethodDecl(ObjCMethodDecl *D);
void VisitObjCContainerDecl(ObjCContainerDecl *D);
@ -363,9 +367,11 @@ void ASTDeclReader::VisitDecl(Decl *D) {
} else {
DeclContext *SemaDC = ReadDeclAs<DeclContext>(Record, Idx);
DeclContext *LexicalDC = ReadDeclAs<DeclContext>(Record, Idx);
DeclContext *MergedSemaDC = Reader.MergedDeclContexts.lookup(SemaDC);
// Avoid calling setLexicalDeclContext() directly because it uses
// Decl::getASTContext() internally which is unsafe during derialization.
D->setDeclContextsImpl(SemaDC, LexicalDC, Reader.getContext());
D->setDeclContextsImpl(MergedSemaDC ? MergedSemaDC : SemaDC, LexicalDC,
Reader.getContext());
}
D->setLocation(Reader.ReadSourceLocation(F, RawLocation));
D->setInvalidDecl(Record[Idx++]);
@ -1230,7 +1236,8 @@ ASTDeclReader::VisitCXXRecordDeclImpl(CXXRecordDecl *D) {
RedeclarableResult Redecl = VisitRecordDeclImpl(D);
ASTContext &C = Reader.getContext();
if (Record[Idx++]) {
bool WasDefinition = Record[Idx++];
if (WasDefinition) {
// Determine whether this is a lambda closure type, so that we can
// allocate the appropriate DefinitionData structure.
bool IsLambda = Record[Idx++];
@ -1239,21 +1246,34 @@ ASTDeclReader::VisitCXXRecordDeclImpl(CXXRecordDecl *D) {
false);
else
D->DefinitionData = new (C) struct CXXRecordDecl::DefinitionData(D);
ReadCXXDefinitionData(*D->DefinitionData, Record, Idx);
// Propagate the DefinitionData pointer to the canonical declaration, so
// that all other deserialized declarations will see it.
// FIXME: Complain if there already is a DefinitionData!
D->getCanonicalDecl()->DefinitionData = D->DefinitionData;
ReadCXXDefinitionData(*D->DefinitionData, Record, Idx);
// Note that we have deserialized a definition. Any declarations
// deserialized before this one will be be given the DefinitionData pointer
// at the end.
Reader.PendingDefinitions.insert(D);
CXXRecordDecl *Canon = D->getCanonicalDecl();
if (Canon == D) {
// Nothing to do.
} else if (!Canon->DefinitionData) {
Canon->DefinitionData = D->DefinitionData;
// Note that we have deserialized a definition. Any declarations
// deserialized before this one will be be given the DefinitionData
// pointer at the end.
Reader.PendingDefinitions.insert(D);
} else {
// We have already deserialized a definition of this record. This
// definition is no longer really a definition. Note that the pre-existing
// definition is the *real* definition.
// FIXME: Check DefinitionData for consistency with prior definition.
Reader.MergedDeclContexts.insert(
std::make_pair(D, D->getCanonicalDecl()->DefinitionData->Definition));
D->IsCompleteDefinition = false;
D->DefinitionData = D->getCanonicalDecl()->DefinitionData;
}
} else {
// Propagate DefinitionData pointer from the canonical declaration.
D->DefinitionData = D->getCanonicalDecl()->DefinitionData;
D->DefinitionData = D->getCanonicalDecl()->DefinitionData;
}
enum CXXRecKind {
@ -1278,8 +1298,9 @@ ASTDeclReader::VisitCXXRecordDeclImpl(CXXRecordDecl *D) {
// Lazily load the key function to avoid deserializing every method so we can
// compute it.
if (D->IsCompleteDefinition) {
if (DeclID KeyFn = ReadDeclID(Record, Idx))
if (WasDefinition) {
DeclID KeyFn = ReadDeclID(Record, Idx);
if (KeyFn && D->IsCompleteDefinition)
C.KeyFunctions[D] = KeyFn;
}
@ -1502,16 +1523,6 @@ ASTDeclReader::VisitClassTemplateSpecializationDeclImpl(
}
}
// Explicit info.
if (TypeSourceInfo *TyInfo = GetTypeSourceInfo(Record, Idx)) {
ClassTemplateSpecializationDecl::ExplicitSpecializationInfo *ExplicitInfo
= new (C) ClassTemplateSpecializationDecl::ExplicitSpecializationInfo;
ExplicitInfo->TypeAsWritten = TyInfo;
ExplicitInfo->ExternLoc = ReadSourceLocation(Record, Idx);
ExplicitInfo->TemplateKeywordLoc = ReadSourceLocation(Record, Idx);
D->ExplicitInfo = ExplicitInfo;
}
SmallVector<TemplateArgument, 8> TemplArgs;
Reader.ReadTemplateArgumentList(TemplArgs, F, Record, Idx);
D->TemplateArgs = TemplateArgumentList::CreateCopy(C, TemplArgs.data(),
@ -1523,16 +1534,48 @@ ASTDeclReader::VisitClassTemplateSpecializationDeclImpl(
if (writtenAsCanonicalDecl) {
ClassTemplateDecl *CanonPattern = ReadDeclAs<ClassTemplateDecl>(Record,Idx);
if (D->isCanonicalDecl()) { // It's kept in the folding set.
// Set this as, or find, the canonical declaration for this specialization
ClassTemplateSpecializationDecl *CanonSpec;
if (ClassTemplatePartialSpecializationDecl *Partial =
dyn_cast<ClassTemplatePartialSpecializationDecl>(D)) {
CanonPattern->getCommonPtr()->PartialSpecializations
CanonSpec = CanonPattern->getCommonPtr()->PartialSpecializations
.GetOrInsertNode(Partial);
} else {
CanonPattern->getCommonPtr()->Specializations.GetOrInsertNode(D);
CanonSpec =
CanonPattern->getCommonPtr()->Specializations.GetOrInsertNode(D);
}
// If there was already a canonical specialization, merge into it.
if (CanonSpec != D) {
mergeRedeclarable<TagDecl>(D, CanonSpec, Redecl);
// This declaration might be a definition. Merge with any existing
// definition.
if (D->DefinitionData) {
if (!CanonSpec->DefinitionData) {
CanonSpec->DefinitionData = D->DefinitionData;
} else {
// FIXME: Check DefinitionData for consistency with prior definition
Reader.PendingDefinitions.erase(D);
Reader.MergedDeclContexts.insert(
std::make_pair(D, CanonSpec->DefinitionData->Definition));
D->IsCompleteDefinition = false;
D->DefinitionData = CanonSpec->DefinitionData;
}
}
}
}
}
// Explicit info.
if (TypeSourceInfo *TyInfo = GetTypeSourceInfo(Record, Idx)) {
ClassTemplateSpecializationDecl::ExplicitSpecializationInfo *ExplicitInfo
= new (C) ClassTemplateSpecializationDecl::ExplicitSpecializationInfo;
ExplicitInfo->TypeAsWritten = TyInfo;
ExplicitInfo->ExternLoc = ReadSourceLocation(Record, Idx);
ExplicitInfo->TemplateKeywordLoc = ReadSourceLocation(Record, Idx);
D->ExplicitInfo = ExplicitInfo;
}
return Redecl;
}
@ -1765,59 +1808,65 @@ ASTDeclReader::VisitRedeclarable(Redeclarable<T> *D) {
/// \brief Attempts to merge the given declaration (D) with another declaration
/// of the same entity.
template<typename T>
void ASTDeclReader::mergeRedeclarable(Redeclarable<T> *D,
void ASTDeclReader::mergeRedeclarable(Redeclarable<T> *D,
RedeclarableResult &Redecl) {
// If modules are not available, there is no reason to perform this merge.
if (!Reader.getContext().getLangOpts().Modules)
return;
if (FindExistingResult ExistingRes = findExisting(static_cast<T*>(D))) {
if (T *Existing = ExistingRes) {
T *ExistingCanon = Existing->getCanonicalDecl();
T *DCanon = static_cast<T*>(D)->getCanonicalDecl();
if (ExistingCanon != DCanon) {
// Have our redeclaration link point back at the canonical declaration
// of the existing declaration, so that this declaration has the
// appropriate canonical declaration.
D->RedeclLink = Redeclarable<T>::PreviousDeclLink(ExistingCanon);
// When we merge a namespace, update its pointer to the first namespace.
if (NamespaceDecl *Namespace
= dyn_cast<NamespaceDecl>(static_cast<T*>(D))) {
Namespace->AnonOrFirstNamespaceAndInline.setPointer(
static_cast<NamespaceDecl *>(static_cast<void*>(ExistingCanon)));
}
// Don't introduce DCanon into the set of pending declaration chains.
Redecl.suppress();
// Introduce ExistingCanon into the set of pending declaration chains,
// if in fact it came from a module file.
if (ExistingCanon->isFromASTFile()) {
GlobalDeclID ExistingCanonID = ExistingCanon->getGlobalID();
assert(ExistingCanonID && "Unrecorded canonical declaration ID?");
if (Reader.PendingDeclChainsKnown.insert(ExistingCanonID))
Reader.PendingDeclChains.push_back(ExistingCanonID);
}
// If this declaration was the canonical declaration, make a note of
// that. We accept the linear algorithm here because the number of
// unique canonical declarations of an entity should always be tiny.
if (DCanon == static_cast<T*>(D)) {
SmallVectorImpl<DeclID> &Merged = Reader.MergedDecls[ExistingCanon];
if (std::find(Merged.begin(), Merged.end(), Redecl.getFirstID())
== Merged.end())
Merged.push_back(Redecl.getFirstID());
// If ExistingCanon did not come from a module file, introduce the
// first declaration that *does* come from a module file to the
// set of pending declaration chains, so that we merge this
// declaration.
if (!ExistingCanon->isFromASTFile() &&
Reader.PendingDeclChainsKnown.insert(Redecl.getFirstID()))
Reader.PendingDeclChains.push_back(Merged[0]);
}
}
if (FindExistingResult ExistingRes = findExisting(static_cast<T*>(D)))
if (T *Existing = ExistingRes)
mergeRedeclarable(D, Existing, Redecl);
}
/// \brief Attempts to merge the given declaration (D) with another declaration
/// of the same entity.
template<typename T>
void ASTDeclReader::mergeRedeclarable(Redeclarable<T> *D, T *Existing,
RedeclarableResult &Redecl) {
T *ExistingCanon = Existing->getCanonicalDecl();
T *DCanon = static_cast<T*>(D)->getCanonicalDecl();
if (ExistingCanon != DCanon) {
// Have our redeclaration link point back at the canonical declaration
// of the existing declaration, so that this declaration has the
// appropriate canonical declaration.
D->RedeclLink = Redeclarable<T>::PreviousDeclLink(ExistingCanon);
// When we merge a namespace, update its pointer to the first namespace.
if (NamespaceDecl *Namespace
= dyn_cast<NamespaceDecl>(static_cast<T*>(D))) {
Namespace->AnonOrFirstNamespaceAndInline.setPointer(
static_cast<NamespaceDecl *>(static_cast<void*>(ExistingCanon)));
}
// Don't introduce DCanon into the set of pending declaration chains.
Redecl.suppress();
// Introduce ExistingCanon into the set of pending declaration chains,
// if in fact it came from a module file.
if (ExistingCanon->isFromASTFile()) {
GlobalDeclID ExistingCanonID = ExistingCanon->getGlobalID();
assert(ExistingCanonID && "Unrecorded canonical declaration ID?");
if (Reader.PendingDeclChainsKnown.insert(ExistingCanonID))
Reader.PendingDeclChains.push_back(ExistingCanonID);
}
// If this declaration was the canonical declaration, make a note of
// that. We accept the linear algorithm here because the number of
// unique canonical declarations of an entity should always be tiny.
if (DCanon == static_cast<T*>(D)) {
SmallVectorImpl<DeclID> &Merged = Reader.MergedDecls[ExistingCanon];
if (std::find(Merged.begin(), Merged.end(), Redecl.getFirstID())
== Merged.end())
Merged.push_back(Redecl.getFirstID());
// If ExistingCanon did not come from a module file, introduce the
// first declaration that *does* come from a module file to the
// set of pending declaration chains, so that we merge this
// declaration.
if (!ExistingCanon->isFromASTFile() &&
Reader.PendingDeclChainsKnown.insert(Redecl.getFirstID()))
Reader.PendingDeclChains.push_back(Merged[0]);
}
}
}
@ -1992,12 +2041,11 @@ static bool isSameEntity(NamedDecl *X, NamedDecl *Y) {
return true;
if (isa<ClassTemplateSpecializationDecl>(X)) {
// FIXME: Deal with merging of template specializations.
// For now, don't merge these; we need to check more than just the name to
// determine if they refer to the same entity.
// No need to handle these here: we merge them when adding them to the
// template.
return false;
}
// Compatible tags match.
if (TagDecl *TagX = dyn_cast<TagDecl>(X)) {
TagDecl *TagY = cast<TagDecl>(Y);
@ -2044,6 +2092,18 @@ static bool isSameEntity(NamedDecl *X, NamedDecl *Y) {
return false;
}
/// Find the context in which we should search for previous declarations when
/// looking for declarations to merge.
static DeclContext *getPrimaryContextForMerging(DeclContext *DC) {
if (NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC))
return ND->getOriginalNamespace();
if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(DC))
return RD->getDefinition();
return 0;
}
ASTDeclReader::FindExistingResult::~FindExistingResult() {
if (!AddResult || Existing)
return;
@ -2051,11 +2111,10 @@ ASTDeclReader::FindExistingResult::~FindExistingResult() {
DeclContext *DC = New->getDeclContext()->getRedeclContext();
if (DC->isTranslationUnit() && Reader.SemaObj) {
Reader.SemaObj->IdResolver.tryAddTopLevelDecl(New, New->getDeclName());
} else if (NamespaceDecl *NS = dyn_cast<NamespaceDecl>(DC)) {
} else if (DeclContext *MergeDC = getPrimaryContextForMerging(DC)) {
// Add the declaration to its redeclaration context so later merging
// lookups will find it.
NS->getFirstDeclaration()->makeDeclVisibleInContextImpl(New,
/*Internal*/true);
MergeDC->makeDeclVisibleInContextImpl(New, /*Internal*/true);
}
}
@ -2069,9 +2128,6 @@ ASTDeclReader::FindExistingResult ASTDeclReader::findExisting(NamedDecl *D) {
}
DeclContext *DC = D->getDeclContext()->getRedeclContext();
if (!DC->isFileContext())
return FindExistingResult(Reader);
if (DC->isTranslationUnit() && Reader.SemaObj) {
IdentifierResolver &IdResolver = Reader.SemaObj->IdResolver;
@ -2104,15 +2160,17 @@ ASTDeclReader::FindExistingResult ASTDeclReader::findExisting(NamedDecl *D) {
if (isSameEntity(*I, D))
return FindExistingResult(Reader, D, *I);
}
} else if (NamespaceDecl *NS = dyn_cast<NamespaceDecl>(DC)) {
DeclContext::lookup_result R = NS->getFirstDeclaration()->noload_lookup(Name);
return FindExistingResult(Reader, D, /*Existing=*/0);
} else if (DeclContext *MergeDC = getPrimaryContextForMerging(DC)) {
DeclContext::lookup_result R = MergeDC->noload_lookup(Name);
for (DeclContext::lookup_iterator I = R.begin(), E = R.end(); I != E; ++I) {
if (isSameEntity(*I, D))
return FindExistingResult(Reader, D, *I);
}
return FindExistingResult(Reader, D, /*Existing=*/0);
}
return FindExistingResult(Reader, D, /*Existing=*/0);
return FindExistingResult(Reader);
}
void ASTDeclReader::attachPreviousDecl(Decl *D, Decl *previous) {

View File

@ -1158,13 +1158,6 @@ void ASTDeclWriter::VisitClassTemplateSpecializationDecl(
Writer.AddTemplateArgumentList(&D->getTemplateInstantiationArgs(), Record);
}
// Explicit info.
Writer.AddTypeSourceInfo(D->getTypeAsWritten(), Record);
if (D->getTypeAsWritten()) {
Writer.AddSourceLocation(D->getExternLoc(), Record);
Writer.AddSourceLocation(D->getTemplateKeywordLoc(), Record);
}
Writer.AddTemplateArgumentList(&D->getTemplateArgs(), Record);
Writer.AddSourceLocation(D->getPointOfInstantiation(), Record);
Record.push_back(D->getSpecializationKind());
@ -1175,6 +1168,13 @@ void ASTDeclWriter::VisitClassTemplateSpecializationDecl(
Writer.AddDeclRef(D->getSpecializedTemplate()->getCanonicalDecl(), Record);
}
// Explicit info.
Writer.AddTypeSourceInfo(D->getTypeAsWritten(), Record);
if (D->getTypeAsWritten()) {
Writer.AddSourceLocation(D->getExternLoc(), Record);
Writer.AddSourceLocation(D->getTemplateKeywordLoc(), Record);
}
Code = serialization::DECL_CLASS_TEMPLATE_SPECIALIZATION;
}

View File

@ -24,3 +24,9 @@ template<typename T> void PerformDelayedLookup(T &t) {
template<typename T> void PerformDelayedLookupInDefaultArgument(T &t, int a = (FoundByADL(T()), 0)) {}
template<typename T> struct RedeclaredAsFriend {};
void use_some_template_a() {
SomeTemplate<char[2]> a;
SomeTemplate<char[1]> b, c;
b = c;
}

View File

@ -35,6 +35,12 @@ struct RedeclareTemplateAsFriend {
friend struct RedeclaredAsFriend;
};
void use_some_template_b() {
SomeTemplate<char[1]> a;
SomeTemplate<char[2]> b, c;
b = c;
}
@import cxx_templates_a;
template<typename T> void UseDefinedInBImplIndirectly(T &v) {
PerformDelayedLookup(v);

View File

@ -27,3 +27,5 @@ void triggerPendingInstantiation() {
}
void redeclDefinitionEmit(){}
typedef Outer<int>::Inner OuterIntInner_left;

View File

@ -25,3 +25,5 @@ void triggerPendingInstantiationToo() {
}
void redeclDefinitionEmit(){}
typedef Outer<int>::Inner OuterIntInner_right;

View File

@ -15,3 +15,7 @@ template <typename T> class A::WhereAmI {
public:
static void func() {}
};
template<typename T> struct Outer {
struct Inner {};
};

View File

@ -82,6 +82,14 @@ typedef SomeTemplate<int&> SomeTemplateIntRef;
SomeTemplate<char*> some_template_char_ptr;
SomeTemplate<char&> some_template_char_ref;
void testImplicitSpecialMembers(SomeTemplate<char[1]> &a,
const SomeTemplate<char[1]> &b,
SomeTemplate<char[2]> &c,
const SomeTemplate<char[2]> &d) {
a = b;
c = d;
}
// CHECK-GLOBAL: DeclarationName 'f'
// CHECK-GLOBAL-NEXT: |-FunctionTemplate {{.*}} 'f'
// CHECK-GLOBAL-NEXT: `-FunctionTemplate {{.*}} 'f'

View File

@ -32,5 +32,10 @@ void testRedeclDefinition() {
redeclDefinitionEmit();
}
// These three are all the same type.
typedef OuterIntInner_left OuterIntInner;
typedef OuterIntInner_right OuterIntInner;
typedef Outer<int>::Inner OuterIntInner;
// CHECK: call {{.*pendingInstantiation}}
// CHECK: call {{.*redeclDefinitionEmit}}