Commit to a primary definition for a class when we load its first
member. Previously, we wouldn't do this if the first member loaded is within a definition that's added to a class via an update record, which happens when template instantiation adds a class definition to a declaration that was imported from an AST file. This would lead to classes having member functions whose getParent returned a class declaration that wasn't the primary definition, which in turn caused the vtable builder to build broken vtables. I don't yet have a reduced testcase for the wrong-code bug here, because the setup required to get us into the broken state is very subtle, but have confirmed that this fixes it.
This commit is contained in:
@@ -98,6 +98,10 @@ Improvements to Clang's diagnostics
|
||||
|
||||
Bug Fixes in This Version
|
||||
-------------------------
|
||||
- Fixed an issue where a class template specialization whose declaration is
|
||||
instantiated in one module and whose definition is instantiated in another
|
||||
module may end up with members associated with the wrong declaration of the
|
||||
class, which can result in miscompiles in some cases.
|
||||
|
||||
Bug Fixes to Compiler Builtins
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -181,6 +181,13 @@ namespace clang {
|
||||
static void setAnonymousDeclForMerging(ASTReader &Reader, DeclContext *DC,
|
||||
unsigned Index, NamedDecl *D);
|
||||
|
||||
/// Commit to a primary definition of the class RD, which is known to be
|
||||
/// a definition of the class. We might not have read the definition data
|
||||
/// for it yet. If we haven't then allocate placeholder definition data
|
||||
/// now too.
|
||||
static CXXRecordDecl *getOrFakePrimaryClassDefinition(ASTReader &Reader,
|
||||
CXXRecordDecl *RD);
|
||||
|
||||
/// Results from loading a RedeclarableDecl.
|
||||
class RedeclarableResult {
|
||||
Decl *MergeWith;
|
||||
@@ -598,7 +605,13 @@ void ASTDeclReader::VisitDecl(Decl *D) {
|
||||
auto *LexicalDC = readDeclAs<DeclContext>();
|
||||
if (!LexicalDC)
|
||||
LexicalDC = SemaDC;
|
||||
DeclContext *MergedSemaDC = Reader.MergedDeclContexts.lookup(SemaDC);
|
||||
// If the context is a class, we might not have actually merged it yet, in
|
||||
// the case where the definition comes from an update record.
|
||||
DeclContext *MergedSemaDC;
|
||||
if (auto *RD = dyn_cast<CXXRecordDecl>(SemaDC))
|
||||
MergedSemaDC = getOrFakePrimaryClassDefinition(Reader, RD);
|
||||
else
|
||||
MergedSemaDC = Reader.MergedDeclContexts.lookup(SemaDC);
|
||||
// Avoid calling setLexicalDeclContext() directly because it uses
|
||||
// Decl::getASTContext() internally which is unsafe during derialization.
|
||||
D->setDeclContextsImpl(MergedSemaDC ? MergedSemaDC : SemaDC, LexicalDC,
|
||||
@@ -3198,6 +3211,32 @@ uint64_t ASTReader::getGlobalBitOffset(ModuleFile &M, uint64_t LocalOffset) {
|
||||
return LocalOffset + M.GlobalBitOffset;
|
||||
}
|
||||
|
||||
CXXRecordDecl *
|
||||
ASTDeclReader::getOrFakePrimaryClassDefinition(ASTReader &Reader,
|
||||
CXXRecordDecl *RD) {
|
||||
// Try to dig out the definition.
|
||||
auto *DD = RD->DefinitionData;
|
||||
if (!DD)
|
||||
DD = RD->getCanonicalDecl()->DefinitionData;
|
||||
|
||||
// If there's no definition yet, then DC's definition is added by an update
|
||||
// record, but we've not yet loaded that update record. In this case, we
|
||||
// commit to DC being the canonical definition now, and will fix this when
|
||||
// we load the update record.
|
||||
if (!DD) {
|
||||
DD = new (Reader.getContext()) struct CXXRecordDecl::DefinitionData(RD);
|
||||
RD->setCompleteDefinition(true);
|
||||
RD->DefinitionData = DD;
|
||||
RD->getCanonicalDecl()->DefinitionData = DD;
|
||||
|
||||
// Track that we did this horrible thing so that we can fix it later.
|
||||
Reader.PendingFakeDefinitionData.insert(
|
||||
std::make_pair(DD, ASTReader::PendingFakeDefinitionKind::Fake));
|
||||
}
|
||||
|
||||
return DD->Definition;
|
||||
}
|
||||
|
||||
/// Find the context in which we should search for previous declarations when
|
||||
/// looking for declarations to merge.
|
||||
DeclContext *ASTDeclReader::getPrimaryContextForMerging(ASTReader &Reader,
|
||||
@@ -3205,29 +3244,8 @@ DeclContext *ASTDeclReader::getPrimaryContextForMerging(ASTReader &Reader,
|
||||
if (auto *ND = dyn_cast<NamespaceDecl>(DC))
|
||||
return ND->getOriginalNamespace();
|
||||
|
||||
if (auto *RD = dyn_cast<CXXRecordDecl>(DC)) {
|
||||
// Try to dig out the definition.
|
||||
auto *DD = RD->DefinitionData;
|
||||
if (!DD)
|
||||
DD = RD->getCanonicalDecl()->DefinitionData;
|
||||
|
||||
// If there's no definition yet, then DC's definition is added by an update
|
||||
// record, but we've not yet loaded that update record. In this case, we
|
||||
// commit to DC being the canonical definition now, and will fix this when
|
||||
// we load the update record.
|
||||
if (!DD) {
|
||||
DD = new (Reader.getContext()) struct CXXRecordDecl::DefinitionData(RD);
|
||||
RD->setCompleteDefinition(true);
|
||||
RD->DefinitionData = DD;
|
||||
RD->getCanonicalDecl()->DefinitionData = DD;
|
||||
|
||||
// Track that we did this horrible thing so that we can fix it later.
|
||||
Reader.PendingFakeDefinitionData.insert(
|
||||
std::make_pair(DD, ASTReader::PendingFakeDefinitionKind::Fake));
|
||||
}
|
||||
|
||||
return DD->Definition;
|
||||
}
|
||||
if (auto *RD = dyn_cast<CXXRecordDecl>(DC))
|
||||
return getOrFakePrimaryClassDefinition(Reader, RD);
|
||||
|
||||
if (auto *RD = dyn_cast<RecordDecl>(DC))
|
||||
return RD->getDefinition();
|
||||
|
||||
Reference in New Issue
Block a user