[ODRHash] Add base classes to hashing CXXRecordDecl.

llvm-svn: 314581
This commit is contained in:
Richard Trieu 2017-09-30 02:19:17 +00:00
parent 305ee8e5f8
commit e13eabe7d3
6 changed files with 277 additions and 4 deletions

View File

@ -122,6 +122,27 @@ def note_second_module_difference : Note<
def err_module_odr_violation_different_instantiations : Error<
"instantiation of %q0 is different in different modules">;
def err_module_odr_violation_definition_data : Error <
"%q0 has different definitions in different modules; first difference is "
"%select{definition in module '%2'|defined here}1 found "
"%select{"
"%4 base %plural{1:class|:classes}4|"
"%4 virtual base %plural{1:class|:classes}4|"
"%ordinal4 base class with type %5|"
"%ordinal4 %select{non-virtual|virtual}5 base class %6|"
"%ordinal4 base class %5 with "
"%select{public|protected|private|no}6 access specifier}3">;
def note_module_odr_violation_definition_data : Note <
"but in '%0' found "
"%select{"
"%2 base %plural{1:class|:classes}2|"
"%2 virtual base %plural{1:class|:classes}2|"
"%ordinal2 base class with different type %3|"
"%ordinal2 %select{non-virtual|virtual}3 base class %4|"
"%ordinal2 base class %3 with "
"%select{public|protected|private|no}4 access specifier}1">;
def err_module_odr_violation_template_parameter : Error <
"%q0 has different definitions in different modules; first difference is "
"%select{definition in module '%2'|defined here}1 found "

View File

@ -1043,8 +1043,11 @@ private:
/// once recursing loading has been completed.
llvm::SmallVector<NamedDecl *, 16> PendingOdrMergeChecks;
using DataPointers =
std::pair<CXXRecordDecl *, struct CXXRecordDecl::DefinitionData *>;
/// \brief Record definitions in which we found an ODR violation.
llvm::SmallDenseMap<CXXRecordDecl *, llvm::TinyPtrVector<CXXRecordDecl *>, 2>
llvm::SmallDenseMap<CXXRecordDecl *, llvm::SmallVector<DataPointers, 2>, 2>
PendingOdrMergeFailures;
/// \brief DeclContexts in which we have diagnosed an ODR violation.

View File

@ -456,6 +456,14 @@ void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) {
if (TD) {
AddTemplateParameterList(TD->getTemplateParameters());
}
ID.AddInteger(Record->getNumBases());
auto Bases = Record->bases();
for (auto Base : Bases) {
AddQualType(Base.getType());
ID.AddInteger(Base.isVirtual());
ID.AddInteger(Base.getAccessSpecifierAsWritten());
}
}
void ODRHash::AddDecl(const Decl *D) {

View File

@ -9190,7 +9190,8 @@ void ASTReader::diagnoseOdrViolations() {
Merge.first->decls_begin();
Merge.first->bases_begin();
Merge.first->vbases_begin();
for (auto *RD : Merge.second) {
for (auto &RecordPair : Merge.second) {
auto *RD = RecordPair.first;
RD->decls_begin();
RD->bases_begin();
RD->vbases_begin();
@ -9296,13 +9297,132 @@ void ASTReader::diagnoseOdrViolations() {
bool Diagnosed = false;
CXXRecordDecl *FirstRecord = Merge.first;
std::string FirstModule = getOwningModuleNameForDiagnostic(FirstRecord);
for (CXXRecordDecl *SecondRecord : Merge.second) {
for (auto &RecordPair : Merge.second) {
CXXRecordDecl *SecondRecord = RecordPair.first;
// Multiple different declarations got merged together; tell the user
// where they came from.
if (FirstRecord == SecondRecord)
continue;
std::string SecondModule = getOwningModuleNameForDiagnostic(SecondRecord);
auto *FirstDD = FirstRecord->DefinitionData;
auto *SecondDD = RecordPair.second;
assert(FirstDD && SecondDD && "Definitions without DefinitionData");
// Diagnostics from DefinitionData are emitted here.
if (FirstDD != SecondDD) {
enum ODRDefinitionDataDifference {
NumBases,
NumVBases,
BaseType,
BaseVirtual,
BaseAccess,
};
auto ODRDiagError = [FirstRecord, &FirstModule,
this](SourceLocation Loc, SourceRange Range,
ODRDefinitionDataDifference DiffType) {
return Diag(Loc, diag::err_module_odr_violation_definition_data)
<< FirstRecord << FirstModule.empty() << FirstModule << Range
<< DiffType;
};
auto ODRDiagNote = [&SecondModule,
this](SourceLocation Loc, SourceRange Range,
ODRDefinitionDataDifference DiffType) {
return Diag(Loc, diag::note_module_odr_violation_definition_data)
<< SecondModule << Range << DiffType;
};
ODRHash Hash;
auto ComputeQualTypeODRHash = [&Hash](QualType Ty) {
Hash.clear();
Hash.AddQualType(Ty);
return Hash.CalculateHash();
};
unsigned FirstNumBases = FirstDD->NumBases;
unsigned FirstNumVBases = FirstDD->NumVBases;
unsigned SecondNumBases = SecondDD->NumBases;
unsigned SecondNumVBases = SecondDD->NumVBases;
auto GetSourceRange = [](struct CXXRecordDecl::DefinitionData *DD) {
unsigned NumBases = DD->NumBases;
if (NumBases == 0) return SourceRange();
auto bases = DD->bases();
return SourceRange(bases[0].getLocStart(),
bases[NumBases - 1].getLocEnd());
};
if (FirstNumBases != SecondNumBases) {
ODRDiagError(FirstRecord->getLocation(), GetSourceRange(FirstDD),
NumBases)
<< FirstNumBases;
ODRDiagNote(SecondRecord->getLocation(), GetSourceRange(SecondDD),
NumBases)
<< SecondNumBases;
Diagnosed = true;
break;
}
if (FirstNumVBases != SecondNumVBases) {
ODRDiagError(FirstRecord->getLocation(), GetSourceRange(FirstDD),
NumVBases)
<< FirstNumVBases;
ODRDiagNote(SecondRecord->getLocation(), GetSourceRange(SecondDD),
NumVBases)
<< SecondNumVBases;
Diagnosed = true;
break;
}
auto FirstBases = FirstDD->bases();
auto SecondBases = SecondDD->bases();
unsigned i = 0;
for (i = 0; i < FirstNumBases; ++i) {
auto FirstBase = FirstBases[i];
auto SecondBase = SecondBases[i];
if (ComputeQualTypeODRHash(FirstBase.getType()) !=
ComputeQualTypeODRHash(SecondBase.getType())) {
ODRDiagError(FirstRecord->getLocation(), FirstBase.getSourceRange(),
BaseType)
<< (i + 1) << FirstBase.getType();
ODRDiagNote(SecondRecord->getLocation(),
SecondBase.getSourceRange(), BaseType)
<< (i + 1) << SecondBase.getType();
break;
}
if (FirstBase.isVirtual() != SecondBase.isVirtual()) {
ODRDiagError(FirstRecord->getLocation(), FirstBase.getSourceRange(),
BaseVirtual)
<< (i + 1) << FirstBase.isVirtual() << FirstBase.getType();
ODRDiagNote(SecondRecord->getLocation(),
SecondBase.getSourceRange(), BaseVirtual)
<< (i + 1) << SecondBase.isVirtual() << SecondBase.getType();
break;
}
if (FirstBase.getAccessSpecifierAsWritten() !=
SecondBase.getAccessSpecifierAsWritten()) {
ODRDiagError(FirstRecord->getLocation(), FirstBase.getSourceRange(),
BaseAccess)
<< (i + 1) << FirstBase.getType()
<< (int)FirstBase.getAccessSpecifierAsWritten();
ODRDiagNote(SecondRecord->getLocation(),
SecondBase.getSourceRange(), BaseAccess)
<< (i + 1) << SecondBase.getType()
<< (int)SecondBase.getAccessSpecifierAsWritten();
break;
}
}
if (i != FirstNumBases) {
Diagnosed = true;
break;
}
}
using DeclHashes = llvm::SmallVector<std::pair<Decl *, unsigned>, 4>;
const ClassTemplateDecl *FirstTemplate =

View File

@ -1755,7 +1755,8 @@ void ASTDeclReader::MergeDefinitionData(
}
if (DetectedOdrViolation)
Reader.PendingOdrMergeFailures[DD.Definition].push_back(MergeDD.Definition);
Reader.PendingOdrMergeFailures[DD.Definition].push_back(
{MergeDD.Definition, &MergeDD});
}
void ASTDeclReader::ReadCXXRecordDefinition(CXXRecordDecl *D, bool Update) {

View File

@ -1577,6 +1577,126 @@ using TemplateParameters::S6;
#endif
} // namespace TemplateParameters
namespace BaseClass {
#if defined(FIRST)
struct B1 {};
struct S1 : B1 {};
#elif defined(SECOND)
struct S1 {};
#else
S1 s1;
// expected-error@second.h:* {{'BaseClass::S1' has different definitions in different modules; first difference is definition in module 'SecondModule' found 0 base classes}}
// expected-note@first.h:* {{but in 'FirstModule' found 1 base class}}
#endif
#if defined(FIRST)
struct S2 {};
#elif defined(SECOND)
struct B2 {};
struct S2 : virtual B2 {};
#else
S2 s2;
// expected-error@second.h:* {{'BaseClass::S2' has different definitions in different modules; first difference is definition in module 'SecondModule' found 1 base class}}
// expected-note@first.h:* {{but in 'FirstModule' found 0 base classes}}
#endif
#if defined(FIRST)
struct B3a {};
struct S3 : B3a {};
#elif defined(SECOND)
struct B3b {};
struct S3 : virtual B3b {};
#else
S3 s3;
// expected-error@second.h:* {{'BaseClass::S3' has different definitions in different modules; first difference is definition in module 'SecondModule' found 1 virtual base class}}
// expected-note@first.h:* {{but in 'FirstModule' found 0 virtual base classes}}
#endif
#if defined(FIRST)
struct B4a {};
struct S4 : B4a {};
#elif defined(SECOND)
struct B4b {};
struct S4 : B4b {};
#else
S4 s4;
// expected-error@second.h:* {{'BaseClass::S4' has different definitions in different modules; first difference is definition in module 'SecondModule' found 1st base class with type 'BaseClass::B4b'}}
// expected-note@first.h:* {{but in 'FirstModule' found 1st base class with different type 'BaseClass::B4a'}}
#endif
#if defined(FIRST)
struct B5a {};
struct S5 : virtual B5a {};
#elif defined(SECOND)
struct B5a {};
struct S5 : B5a {};
#else
S5 s5;
// expected-error@second.h:* {{'BaseClass::S5' has different definitions in different modules; first difference is definition in module 'SecondModule' found 0 virtual base classes}}
// expected-note@first.h:* {{but in 'FirstModule' found 1 virtual base class}}
#endif
#if defined(FIRST)
struct B6a {};
struct S6 : B6a {};
#elif defined(SECOND)
struct B6a {};
struct S6 : virtual B6a {};
#else
S6 s6;
// expected-error@second.h:* {{'BaseClass::S6' has different definitions in different modules; first difference is definition in module 'SecondModule' found 1 virtual base class}}
// expected-note@first.h:* {{but in 'FirstModule' found 0 virtual base classes}}
#endif
#if defined(FIRST)
struct B7a {};
struct S7 : protected B7a {};
#elif defined(SECOND)
struct B7a {};
struct S7 : B7a {};
#else
S7 s7;
// expected-error@second.h:* {{'BaseClass::S7' has different definitions in different modules; first difference is definition in module 'SecondModule' found 1st base class 'BaseClass::B7a' with no access specifier}}
// expected-note@first.h:* {{but in 'FirstModule' found 1st base class 'BaseClass::B7a' with protected access specifier}}
#endif
#if defined(FIRST)
struct B8a {};
struct S8 : public B8a {};
#elif defined(SECOND)
struct B8a {};
struct S8 : private B8a {};
#else
S8 s8;
// expected-error@second.h:* {{'BaseClass::S8' has different definitions in different modules; first difference is definition in module 'SecondModule' found 1st base class 'BaseClass::B8a' with private access specifier}}
// expected-note@first.h:* {{but in 'FirstModule' found 1st base class 'BaseClass::B8a' with public access specifier}}
#endif
#if defined(FIRST)
struct B9a {};
struct S9 : private B9a {};
#elif defined(SECOND)
struct B9a {};
struct S9 : public B9a {};
#else
S9 s9;
// expected-error@second.h:* {{'BaseClass::S9' has different definitions in different modules; first difference is definition in module 'SecondModule' found 1st base class 'BaseClass::B9a' with public access specifier}}
// expected-note@first.h:* {{but in 'FirstModule' found 1st base class 'BaseClass::B9a' with private access specifier}}
#endif
#if defined(FIRST)
struct B10a {};
struct S10 : B10a {};
#elif defined(SECOND)
struct B10a {};
struct S10 : protected B10a {};
#else
S10 s10;
// expected-error@second.h:* {{'BaseClass::S10' has different definitions in different modules; first difference is definition in module 'SecondModule' found 1st base class 'BaseClass::B10a' with protected access specifier}}
// expected-note@first.h:* {{but in 'FirstModule' found 1st base class 'BaseClass::B10a' with no access specifier}}
#endif
} // namespace BaseClass
// Interesting cases that should not cause errors. struct S should not error
// while struct T should error at the access specifier mismatch at the end.
namespace AllDecls {