[ODRHash] Diagnose differing template parameters.
llvm-svn: 311519
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index c968a55..fb8eade 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -158,7 +158,14 @@
}
}
-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 @@
}
}
+ void AddTemplateArgument(TemplateArgument TA) {
+ Hash.AddTemplateArgument(TA);
+ }
+
void Visit(const Decl *D) {
ID.AddInteger(D->getKind());
Inherited::Visit(D);
@@ -343,6 +354,42 @@
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 @@
for (auto SubDecl : Decls) {
AddSubDecl(SubDecl);
}
+
+ const ClassTemplateDecl *TD = Record->getDescribedClassTemplate();
+ AddBoolean(TD);
+ if (TD) {
+ AddTemplateParameterList(TD->getTemplateParameters());
+ }
}
void ODRHash::AddDecl(const Decl *D) {
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index eb49a90..55136e3 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -9274,6 +9274,199 @@
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;