Skip to content

Commit

Permalink
[ODRHash] Hash template arguments of methods.
Browse files Browse the repository at this point in the history
llvm-svn: 330789
  • Loading branch information
Weverything committed Apr 25, 2018
1 parent 61376d9 commit 7282d32
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 1 deletion.
12 changes: 12 additions & 0 deletions clang/include/clang/Basic/DiagnosticSerializationKinds.td
Expand Up @@ -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|"
Expand Down Expand Up @@ -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|"
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/AST/ODRHash.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -330,6 +332,15 @@ class ODRDeclVisitor : public ConstDeclVisitor<ODRDeclVisitor> {

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);
}

Expand Down
92 changes: 92 additions & 0 deletions clang/lib/Serialization/ASTReader.cpp
Expand Up @@ -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
Expand Down Expand Up @@ -9948,6 +9954,9 @@ void ASTReader::diagnoseOdrViolations() {
MethodParameterName,
MethodParameterSingleDefaultArgument,
MethodParameterDifferentDefaultArgument,
MethodNoTemplateArguments,
MethodDifferentNumberTemplateArguments,
MethodDifferentTemplateArgument,
TypedefName,
TypedefType,
VarName,
Expand Down Expand Up @@ -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<const TemplateArgument *, 8> 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<const TemplateArgument *, 8> FirstExpandedList =
ExpandTemplateArgumentList(FirstTemplateArgs);
llvm::SmallVector<const TemplateArgument *, 8> 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:
Expand Down
105 changes: 104 additions & 1 deletion clang/test/Modules/odr_hash.cpp
Expand Up @@ -1635,14 +1635,117 @@ S6 s6;
// expected-note@first.h:* {{but in 'FirstModule' found field 'x'}}
#endif

#if defined(FIRST)
struct S7 {
template<int> void run() {}
template<> void run<1>() {}
};
#elif defined(SECOND)
struct S7 {
template<int> 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<int&> void run() {}
template<int&, int&> void run() {}
template<> void run<a>() {}
};
#elif defined(SECOND)
struct S8 {
static int a, b;
template<int&> void run() {}
template<int&, int&> void run() {}
template<> void run<a, b>() {}
};
#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<int&> void run() {}
template<> void run<a>() {}
};
#elif defined(SECOND)
struct S9 {
static int a, b;
template<int&> void run() {}
template<> void run<b>() {}
};
#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<int, int&...> void run() {}
template<> void run<1, a>() {}
};
#elif defined(SECOND)
struct S10 {
static int a, b;
template<int, int&...> 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<int, int&...> void run() {}
template<> void run<1, a>() {}
};
#elif defined(SECOND)
struct S11 {
static int a, b;
template<int, int&...> 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<int> a; \
OneInt<1> b; \
using c = OneClass<float>; \
using d = OneInt<2>; \
using e = OneInt<2 + 2>; \
OneTemplateClass<OneClass> f; \
OneTemplateInt<OneInt> g;
OneTemplateInt<OneInt> g; \
static int i1, i2; \
template <int &> \
void Function() {} \
template <int &, int &> \
void Function() {} \
template <> \
void Function<i1>() {} \
template <> \
void Function<i2>() {} \
template <> \
void Function<i1, i2>() {} \
template <> \
void Function<i2, i1>() {}

#if defined(FIRST) || defined(SECOND)
template <class> struct OneClass{};
Expand Down

0 comments on commit 7282d32

Please sign in to comment.