diff --git a/clang/include/clang/Basic/DiagnosticSerializationKinds.td b/clang/include/clang/Basic/DiagnosticSerializationKinds.td index 250b49f2cac45..03aaaefff2de2 100644 --- a/clang/include/clang/Basic/DiagnosticSerializationKinds.td +++ b/clang/include/clang/Basic/DiagnosticSerializationKinds.td @@ -211,6 +211,12 @@ def err_module_odr_violation_mismatch_decl_diff : Error< "with %ordinal6 parameter with%select{out|}7 a default argument|" "%select{method %5|constructor|destructor}4 " "with %ordinal6 parameter with a default argument|" + "%select{method %5|constructor|destructor}4 " + "with %select{no |}6template arguments|" + "%select{method %5|constructor|destructor}4 " + "with %6 template argument%s6|" + "%select{method %5|constructor|destructor}4 " + "with %6 for %ordinal7 template argument|" "%select{typedef|type alias}4 name %5|" "%select{typedef|type alias}4 %5 with underlying type %6|" "data member with name %4|" @@ -258,6 +264,12 @@ def note_module_odr_violation_mismatch_decl_diff : Note<"but in '%0' found " "with %ordinal4 parameter with%select{out|}5 a default argument|" "%select{method %3|constructor|destructor}2 " "with %ordinal4 parameter with a different default argument|" + "%select{method %3|constructor|destructor}2 " + "with %select{no |}4template arguments|" + "%select{method %3|constructor|destructor}2 " + "with %4 template argument%s4|" + "%select{method %3|constructor|destructor}2 " + "with %4 for %ordinal5 template argument|" "%select{typedef|type alias}2 name %3|" "%select{typedef|type alias}2 %3 with different underlying type %4|" "data member with name %2|" diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index 364fd04b83b44..ee6950655cf4e 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -148,6 +148,8 @@ void ODRHash::AddTemplateArgument(TemplateArgument TA) { AddQualType(TA.getAsType()); break; case TemplateArgument::Declaration: + AddDecl(TA.getAsDecl()); + break; case TemplateArgument::NullPtr: case TemplateArgument::Integral: break; @@ -330,6 +332,15 @@ class ODRDeclVisitor : public ConstDeclVisitor { AddQualType(D->getReturnType()); + const auto* SpecializationArgs = D->getTemplateSpecializationArgs(); + Hash.AddBoolean(SpecializationArgs); + if (SpecializationArgs) { + ID.AddInteger(SpecializationArgs->size()); + for (const TemplateArgument &TA : SpecializationArgs->asArray()) { + Hash.AddTemplateArgument(TA); + } + } + Inherited::VisitFunctionDecl(D); } diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index a8a4f9328d2ef..9d10b9ebfbb8a 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -9450,6 +9450,12 @@ void ASTReader::diagnoseOdrViolations() { return Hash.CalculateHash(); }; + auto ComputeTemplateArgumentODRHash = [&Hash](const TemplateArgument &TA) { + Hash.clear(); + Hash.AddTemplateArgument(TA); + return Hash.CalculateHash(); + }; + // Issue any pending ODR-failure diagnostics. for (auto &Merge : OdrMergeFailures) { // If we've already pointed out a specific problem with this class, don't @@ -9948,6 +9954,9 @@ void ASTReader::diagnoseOdrViolations() { MethodParameterName, MethodParameterSingleDefaultArgument, MethodParameterDifferentDefaultArgument, + MethodNoTemplateArguments, + MethodDifferentNumberTemplateArguments, + MethodDifferentTemplateArgument, TypedefName, TypedefType, VarName, @@ -10370,6 +10379,89 @@ void ASTReader::diagnoseOdrViolations() { break; } + const auto *FirstTemplateArgs = + FirstMethod->getTemplateSpecializationArgs(); + const auto *SecondTemplateArgs = + SecondMethod->getTemplateSpecializationArgs(); + + if ((FirstTemplateArgs && !SecondTemplateArgs) || + (!FirstTemplateArgs && SecondTemplateArgs)) { + ODRDiagError(FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodNoTemplateArguments) + << FirstMethodType << FirstName << (FirstTemplateArgs != nullptr); + ODRDiagNote(SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodNoTemplateArguments) + << SecondMethodType << SecondName + << (SecondTemplateArgs != nullptr); + + Diagnosed = true; + break; + } + + if (FirstTemplateArgs && SecondTemplateArgs) { + // Remove pack expansions from argument list. + auto ExpandTemplateArgumentList = + [](const TemplateArgumentList *TAL) { + llvm::SmallVector ExpandedList; + for (const TemplateArgument &TA : TAL->asArray()) { + if (TA.getKind() != TemplateArgument::Pack) { + ExpandedList.push_back(&TA); + continue; + } + for (const TemplateArgument &PackTA : TA.getPackAsArray()) { + ExpandedList.push_back(&PackTA); + } + } + return ExpandedList; + }; + llvm::SmallVector FirstExpandedList = + ExpandTemplateArgumentList(FirstTemplateArgs); + llvm::SmallVector SecondExpandedList = + ExpandTemplateArgumentList(SecondTemplateArgs); + + if (FirstExpandedList.size() != SecondExpandedList.size()) { + ODRDiagError(FirstMethod->getLocation(), + FirstMethod->getSourceRange(), + MethodDifferentNumberTemplateArguments) + << FirstMethodType << FirstName + << (unsigned)FirstExpandedList.size(); + ODRDiagNote(SecondMethod->getLocation(), + SecondMethod->getSourceRange(), + MethodDifferentNumberTemplateArguments) + << SecondMethodType << SecondName + << (unsigned)SecondExpandedList.size(); + + Diagnosed = true; + break; + } + + bool TemplateArgumentMismatch = false; + for (unsigned i = 0, e = FirstExpandedList.size(); i != e; ++i) { + const TemplateArgument &FirstTA = *FirstExpandedList[i], + &SecondTA = *SecondExpandedList[i]; + if (ComputeTemplateArgumentODRHash(FirstTA) == + ComputeTemplateArgumentODRHash(SecondTA)) { + continue; + } + + ODRDiagError(FirstMethod->getLocation(), + FirstMethod->getSourceRange(), + MethodDifferentTemplateArgument) + << FirstMethodType << FirstName << FirstTA << i + 1; + ODRDiagNote(SecondMethod->getLocation(), + SecondMethod->getSourceRange(), + MethodDifferentTemplateArgument) + << SecondMethodType << SecondName << SecondTA << i + 1; + + TemplateArgumentMismatch = true; + break; + } + + if (TemplateArgumentMismatch) { + Diagnosed = true; + break; + } + } break; } case TypeAlias: diff --git a/clang/test/Modules/odr_hash.cpp b/clang/test/Modules/odr_hash.cpp index 8667a3ad5f3e5..d4c4aceb66708 100644 --- a/clang/test/Modules/odr_hash.cpp +++ b/clang/test/Modules/odr_hash.cpp @@ -1635,6 +1635,96 @@ S6 s6; // expected-note@first.h:* {{but in 'FirstModule' found field 'x'}} #endif +#if defined(FIRST) +struct S7 { + template void run() {} + template<> void run<1>() {} +}; +#elif defined(SECOND) +struct S7 { + template void run() {} + void run() {} +}; +#else +S7 s7; +// expected-error@second.h:* {{'TemplateArgument::S7' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'run' with no template arguments}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'run' with template arguments}} +#endif + +#if defined(FIRST) +struct S8 { + static int a, b; + template void run() {} + template void run() {} + template<> void run() {} +}; +#elif defined(SECOND) +struct S8 { + static int a, b; + template void run() {} + template void run() {} + template<> void run() {} +}; +#else +S8 s8; +// expected-error@second.h:* {{'TemplateArgument::S8' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'run' with 2 template arguments}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'run' with 1 template argument}} +#endif + +#if defined(FIRST) +struct S9 { + static int a, b; + template void run() {} + template<> void run() {} +}; +#elif defined(SECOND) +struct S9 { + static int a, b; + template void run() {} + template<> void run() {} +}; +#else +S9 s9; +// expected-error@second.h:* {{'TemplateArgument::S9' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'run' with 'b' for 1st template argument}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'run' with 'a' for 1st template argument}} +#endif + +#if defined(FIRST) +struct S10 { + static int a, b; + template void run() {} + template<> void run<1, a>() {} +}; +#elif defined(SECOND) +struct S10 { + static int a, b; + template void run() {} + template<> void run<1, b>() {} +}; +#else +S10 s10; +// expected-error@second.h:* {{'TemplateArgument::S10' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'run' with 'b' for 2nd template argument}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'run' with 'a' for 2nd template argument}} +#endif + +#if defined(FIRST) +struct S11 { + static int a, b; + template void run() {} + template<> void run<1, a>() {} +}; +#elif defined(SECOND) +struct S11 { + static int a, b; + template void run() {} + template<> void run<1, a, a>() {} +}; +#else +S11 s11; +// expected-error@second.h:* {{'TemplateArgument::S11' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'run' with 3 template arguments}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'run' with 2 template arguments}} +#endif + #define DECLS \ OneClass a; \ OneInt<1> b; \ @@ -1642,7 +1732,20 @@ S6 s6; using d = OneInt<2>; \ using e = OneInt<2 + 2>; \ OneTemplateClass f; \ - OneTemplateInt g; + OneTemplateInt g; \ + static int i1, i2; \ + template \ + void Function() {} \ + template \ + void Function() {} \ + template <> \ + void Function() {} \ + template <> \ + void Function() {} \ + template <> \ + void Function() {} \ + template <> \ + void Function() {} #if defined(FIRST) || defined(SECOND) template struct OneClass{};