[ODRHash] Diagnose differing template parameters.

llvm-svn: 311519
This commit is contained in:
Richard Trieu 2017-08-23 02:43:59 +00:00
parent 8cd2f13938
commit 498117bf11
4 changed files with 342 additions and 1 deletions

View File

@ -122,6 +122,24 @@ 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_template_parameter : Error <
"%q0 has different definitions in different modules; first difference is "
"%select{definition in module '%2'|defined here}1 found "
"%select{"
"unnamed template parameter|"
"template parameter %4|"
"template parameter with %select{no |}4default argument|"
"template parameter with default argument}3">;
def note_module_odr_violation_template_parameter : Note <
"but in '%0' found "
"%select{"
"unnamed template parameter %2|"
"template parameter %2|"
"template parameter with %select{no |}2default argument|"
"template parameter with different default argument}1">;
def err_module_odr_violation_mismatch_decl : Error<
"%q0 has different definitions in different modules; first difference is "
"%select{definition in module '%2'|defined here}1 found "

View File

@ -158,7 +158,14 @@ void ODRHash::AddTemplateArgument(TemplateArgument TA) {
}
}
void ODRHash::AddTemplateParameterList(const TemplateParameterList *TPL) {}
void ODRHash::AddTemplateParameterList(const TemplateParameterList *TPL) {
assert(TPL && "Expecting non-null pointer.");
ID.AddInteger(TPL->size());
for (auto *ND : TPL->asArray()) {
AddSubDecl(ND);
}
}
void ODRHash::clear() {
DeclMap.clear();
@ -236,6 +243,10 @@ public:
}
}
void AddTemplateArgument(TemplateArgument TA) {
Hash.AddTemplateArgument(TA);
}
void Visit(const Decl *D) {
ID.AddInteger(D->getKind());
Inherited::Visit(D);
@ -343,6 +354,42 @@ public:
AddDecl(D->getFriendDecl());
}
}
void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) {
// Only care about default arguments as part of the definition.
const bool hasDefaultArgument =
D->hasDefaultArgument() && !D->defaultArgumentWasInherited();
Hash.AddBoolean(hasDefaultArgument);
if (hasDefaultArgument) {
AddTemplateArgument(D->getDefaultArgument());
}
Inherited::VisitTemplateTypeParmDecl(D);
}
void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D) {
// Only care about default arguments as part of the definition.
const bool hasDefaultArgument =
D->hasDefaultArgument() && !D->defaultArgumentWasInherited();
Hash.AddBoolean(hasDefaultArgument);
if (hasDefaultArgument) {
AddStmt(D->getDefaultArgument());
}
Inherited::VisitNonTypeTemplateParmDecl(D);
}
void VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl *D) {
// Only care about default arguments as part of the definition.
const bool hasDefaultArgument =
D->hasDefaultArgument() && !D->defaultArgumentWasInherited();
Hash.AddBoolean(hasDefaultArgument);
if (hasDefaultArgument) {
AddTemplateArgument(D->getDefaultArgument().getArgument());
}
Inherited::VisitTemplateTemplateParmDecl(D);
}
};
} // namespace
@ -403,6 +450,12 @@ void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) {
for (auto SubDecl : Decls) {
AddSubDecl(SubDecl);
}
const ClassTemplateDecl *TD = Record->getDescribedClassTemplate();
AddBoolean(TD);
if (TD) {
AddTemplateParameterList(TD->getTemplateParameters());
}
}
void ODRHash::AddDecl(const Decl *D) {

View File

@ -9274,6 +9274,199 @@ void ASTReader::diagnoseOdrViolations() {
std::string SecondModule = getOwningModuleNameForDiagnostic(SecondRecord);
using DeclHashes = llvm::SmallVector<std::pair<Decl *, unsigned>, 4>;
const ClassTemplateDecl *FirstTemplate =
FirstRecord->getDescribedClassTemplate();
const ClassTemplateDecl *SecondTemplate =
SecondRecord->getDescribedClassTemplate();
assert(!FirstTemplate == !SecondTemplate &&
"Both pointers should be null or non-null");
enum ODRTemplateDifference {
ParamEmptyName,
ParamName,
ParamSingleDefaultArgument,
ParamDifferentDefaultArgument,
};
if (FirstTemplate && SecondTemplate) {
DeclHashes FirstTemplateHashes;
DeclHashes SecondTemplateHashes;
ODRHash Hash;
auto PopulateTemplateParameterHashs =
[&Hash](DeclHashes &Hashes, const ClassTemplateDecl *TD) {
for (auto *D : TD->getTemplateParameters()->asArray()) {
Hash.clear();
Hash.AddSubDecl(D);
Hashes.emplace_back(D, Hash.CalculateHash());
}
};
PopulateTemplateParameterHashs(FirstTemplateHashes, FirstTemplate);
PopulateTemplateParameterHashs(SecondTemplateHashes, SecondTemplate);
assert(FirstTemplateHashes.size() == SecondTemplateHashes.size() &&
"Number of template parameters should be equal.");
auto FirstIt = FirstTemplateHashes.begin();
auto FirstEnd = FirstTemplateHashes.end();
auto SecondIt = SecondTemplateHashes.begin();
for (; FirstIt != FirstEnd; ++FirstIt, ++SecondIt) {
if (FirstIt->second == SecondIt->second)
continue;
auto ODRDiagError = [FirstRecord, &FirstModule,
this](SourceLocation Loc, SourceRange Range,
ODRTemplateDifference DiffType) {
return Diag(Loc, diag::err_module_odr_violation_template_parameter)
<< FirstRecord << FirstModule.empty() << FirstModule << Range
<< DiffType;
};
auto ODRDiagNote = [&SecondModule,
this](SourceLocation Loc, SourceRange Range,
ODRTemplateDifference DiffType) {
return Diag(Loc, diag::note_module_odr_violation_template_parameter)
<< SecondModule << Range << DiffType;
};
const NamedDecl* FirstDecl = cast<NamedDecl>(FirstIt->first);
const NamedDecl* SecondDecl = cast<NamedDecl>(SecondIt->first);
assert(FirstDecl->getKind() == SecondDecl->getKind() &&
"Parameter Decl's should be the same kind.");
DeclarationName FirstName = FirstDecl->getDeclName();
DeclarationName SecondName = SecondDecl->getDeclName();
if (FirstName != SecondName) {
const bool FirstNameEmpty =
FirstName.isIdentifier() && !FirstName.getAsIdentifierInfo();
const bool SecondNameEmpty =
SecondName.isIdentifier() && !SecondName.getAsIdentifierInfo();
assert((!FirstNameEmpty || !SecondNameEmpty) &&
"Both template parameters cannot be unnamed.");
ODRDiagError(FirstDecl->getLocation(), FirstDecl->getSourceRange(),
FirstNameEmpty ? ParamEmptyName : ParamName)
<< FirstName;
ODRDiagNote(SecondDecl->getLocation(), SecondDecl->getSourceRange(),
SecondNameEmpty ? ParamEmptyName : ParamName)
<< SecondName;
break;
}
switch (FirstDecl->getKind()) {
default:
llvm_unreachable("Invalid template parameter type.");
case Decl::TemplateTypeParm: {
const auto *FirstParam = cast<TemplateTypeParmDecl>(FirstDecl);
const auto *SecondParam = cast<TemplateTypeParmDecl>(SecondDecl);
const bool HasFirstDefaultArgument =
FirstParam->hasDefaultArgument() &&
!FirstParam->defaultArgumentWasInherited();
const bool HasSecondDefaultArgument =
SecondParam->hasDefaultArgument() &&
!SecondParam->defaultArgumentWasInherited();
if (HasFirstDefaultArgument != HasSecondDefaultArgument) {
ODRDiagError(FirstDecl->getLocation(),
FirstDecl->getSourceRange(),
ParamSingleDefaultArgument)
<< HasFirstDefaultArgument;
ODRDiagNote(SecondDecl->getLocation(),
SecondDecl->getSourceRange(),
ParamSingleDefaultArgument)
<< HasSecondDefaultArgument;
break;
}
assert(HasFirstDefaultArgument && HasSecondDefaultArgument &&
"Expecting default arguments.");
ODRDiagError(FirstDecl->getLocation(), FirstDecl->getSourceRange(),
ParamDifferentDefaultArgument);
ODRDiagNote(SecondDecl->getLocation(), SecondDecl->getSourceRange(),
ParamDifferentDefaultArgument);
break;
}
case Decl::NonTypeTemplateParm: {
const auto *FirstParam = cast<NonTypeTemplateParmDecl>(FirstDecl);
const auto *SecondParam = cast<NonTypeTemplateParmDecl>(SecondDecl);
const bool HasFirstDefaultArgument =
FirstParam->hasDefaultArgument() &&
!FirstParam->defaultArgumentWasInherited();
const bool HasSecondDefaultArgument =
SecondParam->hasDefaultArgument() &&
!SecondParam->defaultArgumentWasInherited();
if (HasFirstDefaultArgument != HasSecondDefaultArgument) {
ODRDiagError(FirstDecl->getLocation(),
FirstDecl->getSourceRange(),
ParamSingleDefaultArgument)
<< HasFirstDefaultArgument;
ODRDiagNote(SecondDecl->getLocation(),
SecondDecl->getSourceRange(),
ParamSingleDefaultArgument)
<< HasSecondDefaultArgument;
break;
}
assert(HasFirstDefaultArgument && HasSecondDefaultArgument &&
"Expecting default arguments.");
ODRDiagError(FirstDecl->getLocation(), FirstDecl->getSourceRange(),
ParamDifferentDefaultArgument);
ODRDiagNote(SecondDecl->getLocation(), SecondDecl->getSourceRange(),
ParamDifferentDefaultArgument);
break;
}
case Decl::TemplateTemplateParm: {
const auto *FirstParam = cast<TemplateTemplateParmDecl>(FirstDecl);
const auto *SecondParam =
cast<TemplateTemplateParmDecl>(SecondDecl);
const bool HasFirstDefaultArgument =
FirstParam->hasDefaultArgument() &&
!FirstParam->defaultArgumentWasInherited();
const bool HasSecondDefaultArgument =
SecondParam->hasDefaultArgument() &&
!SecondParam->defaultArgumentWasInherited();
if (HasFirstDefaultArgument != HasSecondDefaultArgument) {
ODRDiagError(FirstDecl->getLocation(),
FirstDecl->getSourceRange(),
ParamSingleDefaultArgument)
<< HasFirstDefaultArgument;
ODRDiagNote(SecondDecl->getLocation(),
SecondDecl->getSourceRange(),
ParamSingleDefaultArgument)
<< HasSecondDefaultArgument;
break;
}
assert(HasFirstDefaultArgument && HasSecondDefaultArgument &&
"Expecting default arguments.");
ODRDiagError(FirstDecl->getLocation(), FirstDecl->getSourceRange(),
ParamDifferentDefaultArgument);
ODRDiagNote(SecondDecl->getLocation(), SecondDecl->getSourceRange(),
ParamDifferentDefaultArgument);
break;
}
}
break;
}
if (FirstIt != FirstEnd) {
Diagnosed = true;
break;
}
}
DeclHashes FirstHashes;
DeclHashes SecondHashes;
ODRHash Hash;

View File

@ -1500,6 +1500,83 @@ S5 s5;
// expected-note@first.h:* {{but in 'FirstModule' found friend function 'T5a'}}
#endif
}
namespace TemplateParameters {
#if defined(FIRST)
template <class A>
struct S1 {};
#elif defined(SECOND)
template <class B>
struct S1 {};
#else
using TemplateParameters::S1;
// expected-error@second.h:* {{'TemplateParameters::S1' has different definitions in different modules; first difference is definition in module 'SecondModule' found template parameter 'B'}}
// expected-note@first.h:* {{but in 'FirstModule' found template parameter 'A'}}
#endif
#if defined(FIRST)
template <class A = double>
struct S2 {};
#elif defined(SECOND)
template <class A = int>
struct S2 {};
#else
using TemplateParameters::S2;
// expected-error@second.h:* {{'TemplateParameters::S2' has different definitions in different modules; first difference is definition in module 'SecondModule' found template parameter with default argument}}
// expected-note@first.h:* {{but in 'FirstModule' found template parameter with different default argument}}
#endif
#if defined(FIRST)
template <class A = int>
struct S3 {};
#elif defined(SECOND)
template <class A>
struct S3 {};
#else
using TemplateParameters::S3;
// expected-error@second.h:* {{'TemplateParameters::S3' has different definitions in different modules; first difference is definition in module 'SecondModule' found template parameter with no default argument}}
// expected-note@first.h:* {{but in 'FirstModule' found template parameter with default argument}}
#endif
#if defined(FIRST)
template <int A>
struct S4 {};
#elif defined(SECOND)
template <int A = 2>
struct S4 {};
#else
using TemplateParameters::S4;
// expected-error@second.h:* {{'TemplateParameters::S4' has different definitions in different modules; first difference is definition in module 'SecondModule' found template parameter with default argument}}
// expected-note@first.h:* {{but in 'FirstModule' found template parameter with no default argument}}
#endif
#if defined(FIRST)
template <int> class S5_first {};
template <template<int> class A = S5_first>
struct S5 {};
#elif defined(SECOND)
template <int> class S5_second {};
template <template<int> class A = S5_second>
struct S5 {};
#else
using TemplateParameters::S5;
// expected-error@second.h:* {{'TemplateParameters::S5' has different definitions in different modules; first difference is definition in module 'SecondModule' found template parameter with default argument}}
// expected-note@first.h:* {{but in 'FirstModule' found template parameter with different default argument}}
#endif
#if defined(FIRST)
template <class A>
struct S6 {};
#elif defined(SECOND)
template <class>
struct S6 {};
#else
using TemplateParameters::S6;
// expected-error@second.h:* {{'TemplateParameters::S6' has different definitions in different modules; first difference is definition in module 'SecondModule' found unnamed template parameter}}
// expected-note@first.h:* {{but in 'FirstModule' found template parameter 'A'}}
#endif
} // namespace TemplateParameters
// 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 {