diff options
author | Richard Trieu <rtrieu@google.com> | 2017-08-23 02:43:59 +0000 |
---|---|---|
committer | Richard Trieu <rtrieu@google.com> | 2017-08-23 02:43:59 +0000 |
commit | ce3349f4d78770e87d6ba4fd5ebd644a073b0aab (patch) | |
tree | 9c4347c80cfb592721762569496688bdaa11a5c5 | |
parent | ecd7752cac1f7186d7dd70fba9b32d9938a5ecc5 (diff) | |
download | clang-ce3349f4d78770e87d6ba4fd5ebd644a073b0aab.tar.gz |
[ODRHash] Diagnose differing template parameters.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@311519 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Basic/DiagnosticSerializationKinds.td | 18 | ||||
-rw-r--r-- | lib/AST/ODRHash.cpp | 55 | ||||
-rw-r--r-- | lib/Serialization/ASTReader.cpp | 193 | ||||
-rw-r--r-- | test/Modules/odr_hash.cpp | 77 |
4 files changed, 342 insertions, 1 deletions
diff --git a/include/clang/Basic/DiagnosticSerializationKinds.td b/include/clang/Basic/DiagnosticSerializationKinds.td index 420ccebbfa..ca755bd578 100644 --- a/include/clang/Basic/DiagnosticSerializationKinds.td +++ b/include/clang/Basic/DiagnosticSerializationKinds.td @@ -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 " diff --git a/lib/AST/ODRHash.cpp b/lib/AST/ODRHash.cpp index c968a55adf..fb8eade3c0 100644 --- a/lib/AST/ODRHash.cpp +++ b/lib/AST/ODRHash.cpp @@ -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) { diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index eb49a90d72..55136e3a3c 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -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; diff --git a/test/Modules/odr_hash.cpp b/test/Modules/odr_hash.cpp index 68a988dc8e..a3ac510c32 100644 --- a/test/Modules/odr_hash.cpp +++ b/test/Modules/odr_hash.cpp @@ -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 { |