rtrieu updated this revision to Diff 75970. rtrieu marked 2 inline comments as done.
https://reviews.llvm.org/D21675 Files: include/clang/AST/DeclBase.h include/clang/AST/DeclCXX.h include/clang/AST/Stmt.h include/clang/AST/TemplateBase.h include/clang/AST/Type.h include/clang/Basic/DiagnosticSerializationKinds.td lib/AST/DeclBase.cpp lib/AST/DeclCXX.cpp lib/AST/StmtProfile.cpp lib/AST/TemplateBase.cpp lib/AST/Type.cpp lib/Sema/SemaDecl.cpp lib/Serialization/ASTReader.cpp lib/Serialization/ASTReaderDecl.cpp lib/Serialization/ASTWriter.cpp test/Modules/merge-using-decls.cpp test/Modules/odr_hash.cpp
Index: test/Modules/odr_hash.cpp =================================================================== --- test/Modules/odr_hash.cpp +++ test/Modules/odr_hash.cpp @@ -0,0 +1,752 @@ +// Clear and create directories +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: mkdir %t/cache +// RUN: mkdir %t/Inputs + +// Build first header file +// RUN: echo "#define FIRST" >> %t/Inputs/first.h +// RUN: cat %s >> %t/Inputs/first.h + +// Build second header file +// RUN: echo "#define SECOND" >> %t/Inputs/second.h +// RUN: cat %s >> %t/Inputs/second.h + +// Build module map file +// RUN: echo "module first {" >> %t/Inputs/module.map +// RUN: echo " header \"first.h\"" >> %t/Inputs/module.map +// RUN: echo "}" >> %t/Inputs/module.map +// RUN: echo "module second {" >> %t/Inputs/module.map +// RUN: echo " header \"second.h\"" >> %t/Inputs/module.map +// RUN: echo "}" >> %t/Inputs/module.map + +// Run test +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache -x c++ -I%t/Inputs -verify %s -std=c++11 -fcolor-diagnostics + +#if !defined(FIRST) && !defined(SECOND) +#include "first.h" +#include "second.h" +#endif + +#if defined(FIRST) +struct S1 { + public: +}; +#elif defined(SECOND) +struct S1 { + private: +}; +#else +S1 s1; +// expected-error@first.h:* {{'S1' has different definitions in different modules; first difference is definition in module 'first' found public access specifier}} +// expected-note@second.h:* {{but in 'second' found private access specifier}} +#endif + +#if defined(FIRST) +struct S2Friend2 {}; +struct S2 { + friend S2Friend2; +}; +#elif defined(SECOND) +struct S2Friend1 {}; +struct S2 { + friend S2Friend1; +}; +#else +S2 s2; +// expected-error@first.h:* {{'S2' has different definitions in different modules; first difference is definition in module 'first' found friend 'S2Friend2'}} +// expected-note@second.h:* {{but in 'second' found other friend 'S2Friend1'}} +#endif + +#if defined(FIRST) +template<class> +struct S3Template {}; +struct S3 { + friend S3Template<int>; +}; +#elif defined(SECOND) +template<class> +struct S3Template {}; +struct S3 { + friend S3Template<double>; +}; +#else +S3 s3; +// expected-error@first.h:* {{'S3' has different definitions in different modules; first difference is definition in module 'first' found friend 'S3Template<int>'}} +// expected-note@second.h:* {{but in 'second' found other friend 'S3Template<double>'}} +#endif + +#if defined(FIRST) +struct S4 { + static_assert(1 == 1, "First"); +}; +#elif defined(SECOND) +struct S4 { + static_assert(1 == 1, "Second"); +}; +#else +S4 s4; +// expected-error@first.h:* {{'S4' has different definitions in different modules; first difference is definition in module 'first' found static assert with message}} +// expected-note@second.h:* {{but in 'second' found static assert with different message}} +#endif + +#if defined(FIRST) +struct S5 { + static_assert(1 == 1, "Message"); +}; +#elif defined(SECOND) +struct S5 { + static_assert(2 == 2, "Message"); +}; +#else +S5 s5; +// expected-error@first.h:* {{'S5' has different definitions in different modules; first difference is definition in module 'first' found static assert with condition}} +// expected-note@second.h:* {{but in 'second' found static assert with different condition}} +#endif + +#if defined(FIRST) +struct S6 { + int First(); +}; +#elif defined(SECOND) +struct S6 { + int Second(); +}; +#else +S6 s6; +// expected-error@second.h:* {{'S6::Second' from module 'second' is not present in definition of 'S6' in module 'first'}} +// expected-note@first.h:* {{definition has no member 'Second'}} +#endif + +#if defined(FIRST) +struct S7 { + double foo(); +}; +#elif defined(SECOND) +struct S7 { + int foo(); +}; +#else +S7 s7; +// expected-error@second.h:* {{'S7::foo' from module 'second' is not present in definition of 'S7' in module 'first'}} +// expected-note@first.h:* {{declaration of 'foo' does not match}} +#endif + +#if defined(FIRST) +struct S8 { + void foo(); +}; +#elif defined(SECOND) +struct S8 { + void foo() {} +}; +#else +S8 s8; +// expected-error@first.h:* {{'S8' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S9 { + void foo() { int y = 5; } +}; +#elif defined(SECOND) +struct S9 { + void foo() { int x = 5; } +}; +#else +S9 s9; +// expected-error@first.h:* {{'S9' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S10 { + struct { + int x; + } a; +}; +#elif defined(SECOND) +struct S10 { + struct { + int x; + int y; + } a; +}; +#else +S10 s10; +// expected-error-re@second.h:* {{'S10::(anonymous struct)::y' from module 'second' is not present in definition of 'S10::(anonymous struct at {{.*}}first.h:{{[0-9]*}}:{{[0-9]*}})' in module 'first'}} +// expected-note@first.h:* {{definition has no member 'y'}} + +// expected-error@first.h:* {{'S10' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S11 { + void foo() { int y = sizeof(int); } +}; +#elif defined(SECOND) +struct S11 { + void foo() { int y = sizeof(double); } +}; +#else +S11 s11; +// expected-error@first.h:* {{'S11' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S12 { + int x = sizeof(x); + int y = sizeof(x); +}; +#elif defined(SECOND) +struct S12 { + int x = sizeof(x); + int y = sizeof(y); +}; +#else +S12 s12; +// expected-error@first.h:* {{'S12' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S13 { + template <typename A> void foo(); +}; +#elif defined(SECOND) +struct S13 { + template <typename B> void foo(); +}; +#else +S13 s13; +// expected-error@first.h:* {{'S13' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S14 { + template <typename A, typename B> void foo(); +}; +#elif defined(SECOND) +struct S14 { + template <typename A> void foo(); +}; +#else +S14 s14; +// expected-error@second.h:* {{'S14::foo' from module 'second' is not present in definition of 'S14' in module 'first'}} +// expected-note@first.h:* {{declaration of 'foo' does not match}} +#endif + +#if defined(FIRST) +template <typename T> +struct S15 : T { + void foo() { + int x = __builtin_offsetof(T, first); + } +}; +#elif defined(SECOND) +template <typename T> +struct S15 : T { + void foo() { + int x = __builtin_offsetof(T, second); + } +}; +#else +template <typename T> +void Test15() { + S15<T> s15; +// expected-error@first.h:* {{'S15' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +} +#endif + +#if defined(FIRST) +struct S16 { + template <template<int = 0> class Y> + void foo() { + Y<> y; + } +}; +#elif defined(SECOND) +struct S16 { + template <template<int = 1> class Y> + void foo() { + Y<> y; + } +}; +#else +void TestS16() { + S16 s16; +} +// expected-error@first.h:* {{'S16' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S17 { + template <template <typename> class T> + static int foo(int a = 1); + template <template <typename> class T, template <typename> class U> + using Q_type = T<int>; +}; +#elif defined(SECOND) +struct S17 { + template <template <typename> class T> + static int foo(int a = 1); + template <template <typename> class T, template <typename> class U> + using Q_type = U<int>; +}; +#else +S17 s17; +// expected-error@second.h:* {{'S17::Q_type' from module 'second' is not present in definition of 'S17' in module 'first'}} +// expected-note@first.h:* {{declaration of 'Q_type' does not match}} +#endif + +#if defined(FIRST) +struct S18 { + enum E { X1 }; +}; +#elif defined(SECOND) +struct S18 { + enum X { X1 }; +}; +#else +S18 s18; +// expected-error@second.h:* {{'S18::X' from module 'second' is not present in definition of 'S18' in module 'first'}} +// expected-note@first.h:* {{definition has no member 'X'}} +#endif + +#if defined(FIRST) +struct S19 { + enum E { X1 }; +}; +#elif defined(SECOND) +struct S19 { + enum E { X1, X2 }; +}; +#else +S19 s19; +// expected-error@first.h:* {{'S19' has different definitions in different modules; first difference is definition in module 'first' found enum 'E' has 1 element}} +// expected-note@second.h:* {{but in 'second' found enum 'E' has 2 elements}} +// expected-error@second.h:* {{'S19::E::X2' from module 'second' is not present in definition of 'S19::E' in module 'first'}} +// expected-note@first.h:* {{definition has no member 'X2'}} +#endif + +#if defined(FIRST) +struct S20 { + enum E { X1 = 1 }; +}; +#elif defined(SECOND) +struct S20 { + enum E { X1 = 5}; +}; +#else +S20 s20; +// expected-error@first.h:* {{'S20' has different definitions in different modules; first difference is definition in module 'first' found element 'X1' in enum 'E' with initializer}} +// expected-note@second.h:* {{but in 'second' found element 'X1' in enum 'E' with different initializer}} +#endif + +#if defined(FIRST) +struct S21 { + void foo() { + label: + ; + } +}; +#elif defined(SECOND) +struct S21 { + void foo() { + ; + } +}; +#else +S21 s21; +// expected-error@first.h:* {{'S21' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S22 { + void foo() { + label_first: + ; + } +}; +#elif defined(SECOND) +struct S22 { + void foo() { + label_second: + ; + } +}; +#else +S22 s22; +// expected-error@first.h:* {{'S22' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S23 { + typedef int a; + typedef char b; +}; +#elif defined(SECOND) +struct S23 { + typedef char a; + typedef int b; +}; +#else +S23 s23; +// expected-error@second.h:* {{'S23::a' from module 'second' is not present in definition of 'S23' in module 'first'}} +// expected-note@first.h:* {{declaration of 'a' does not match}} +// expected-error@second.h:* {{'S23::b' from module 'second' is not present in definition of 'S23' in module 'first'}} +// expected-note@first.h:* {{declaration of 'b' does not match}} +#endif + +#if defined(FIRST) +struct S24 { + inline int foo(); +}; +#elif defined(SECOND) +struct S24 { + int foo(); +}; +#else +S24 s24; +// expected-error@first.h:* {{'S24' has different definitions in different modules; first difference is definition in module 'first' found method 'foo' is inline}} +// expected-note@second.h:* {{but in 'second' found method 'foo' is not inline}} +#endif + +#if defined(FIRST) +struct S25 { + int x; + S25() : x(5) {} +}; +#elif defined(SECOND) +struct S25 { + int x; + S25() {} +}; +#else +S25 s25; +// expected-error@first.h:* {{'S25' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S26 { + int x; + S26() : x(5) {} +}; +#elif defined(SECOND) +struct S26 { + int x; + S26() : x(2) {} +}; +#else +S26 s26; +// expected-error@first.h:* {{'S26' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S27 { + explicit S27(int) {} + S27() {} +}; +#elif defined(SECOND) +struct S27 { + S27(int) {} + S27() {} +}; +#else +S27 s27; +// expected-error@first.h:* {{'S27' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) || defined(SECOND) +struct Base1 { + Base1(); + Base1(int); + Base1(double); +}; + +struct Base2 { + Base2(); + Base2(int); + Base2(double); +}; +#endif + +#if defined(FIRST) +struct S28 : public Base1 {}; +#elif defined(SECOND) +struct S28 : public Base2 {}; +#else +S28 s28; +// expected-error@first.h:* {{'S28' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S29 : virtual Base1 {}; +#elif defined(SECOND) +struct S29 : virtual Base2 {}; +#else +S29 s29; +// expected-error@first.h:* {{'S29' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S30 : public Base1 { + S30() : Base1(1) {} +}; +#elif defined(SECOND) +struct S30 : public Base1 { + S30() : Base1(1.0) {} +}; +#else +S30 s30; +// expected-error@first.h:* {{'S30' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S31 : virtual Base1 { + S31() : Base1(1) {} +}; +#elif defined(SECOND) +struct S31 : virtual Base1 { + S31() : Base1(1.0) {} +}; +#else +S31 s31; +// expected-error@first.h:* {{'S31' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S32 : public Base1, Base2 { + S32() : Base1(1), Base2(1.0) {} +}; +#elif defined(SECOND) +struct S32 : public Base2, Base1 { + S32() : Base2(1), Base1(1.0) {} +}; +#else +S32 s32; +// expected-error@first.h:* {{'S32' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S33 { + S33() : S33(5) {} + S33(int) {int a;} +}; +#elif defined(SECOND) +struct S33 { + S33() : S33(5) {} + S33(int) {} +}; +#else +S33 s33; +// expected-error@first.h:* {{'S33' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S34 { + operator bool(); +}; +#elif defined(SECOND) +struct S34 { + operator int(); +}; +#else +S34 s34; +// expected-error@second.h:* {{'S34::operator int' from module 'second' is not present in definition of 'S34' in module 'first'}} +// expected-note@first.h:* {{definition has no member 'operator int'}} +#endif + +#if defined(FIRST) +struct S35 { + explicit operator bool(); +}; +#elif defined(SECOND) +struct S35 { + operator bool(); +}; +#else +S35 s35; +// expected-error@first.h:* {{'S35' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S36 { + int x : 3; +}; +#elif defined(SECOND) +struct S36 { + int x : 4; +}; +#else +S36 s36; +// expected-error@first.h:* {{'S36' has different definitions in different modules; first difference is definition in module 'first' found bitfield 'x'}} +// expected-note@second.h:* {{but in 'second' found bitfield 'x'}} +#endif + +#if defined(FIRST) +struct S37 { + mutable int x; + int y; +}; +#elif defined(SECOND) +struct S37 { + int x; + mutable int y; +}; +#else +S37 s37; +// expected-error@first.h:* {{'S37' has different definitions in different modules; first difference is definition in module 'first' found mutable 'x'}} +// expected-note@second.h:* {{but in 'second' found non-mutable 'x'}} +#endif + +#if defined(FIRST) +template <class X> +struct S38 { }; +#elif defined(SECOND) +template <class Y> +struct S38 { }; +#else +S38<int> s38; +// expected-error@first.h:* {{'S38' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +template <class X = int> +struct S39 { X x; }; +#elif defined(SECOND) +template <class X = double> +struct S39 { X x; }; +#else +S39<> s39; +// expected-error@first.h:* {{'S39' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +template <int X = 5> +struct S40 { int x = X; }; +#elif defined(SECOND) +template <int X = 7> +struct S40 { int x = X; }; +#else +S40<> s40; +// expected-error@first.h:* {{'S40' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} + +#endif + +#if defined(FIRST) +template <int> class T41a{}; +template <template<int> class T = T41a> +struct S41 {}; +#elif defined(SECOND) +template <int> class T41b{}; +template <template<int> class T = T41b> +struct S41 {}; +#else +using ::S41; +// expected-error@first.h:* {{'S41' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S42 { + void foo() const {} + void bar() {} +}; +#elif defined(SECOND) +struct S42 { + void foo() {} + void bar() const {} +}; +#else +S42 s42; +// expected-error@second.h:* {{'S42::bar' from module 'second' is not present in definition of 'S42' in module 'first'}} +// expected-note@first.h:* {{declaration of 'bar' does not match}} +// expected-error@second.h:* {{'S42::foo' from module 'second' is not present in definition of 'S42' in module 'first'}} +// expected-note@first.h:* {{declaration of 'foo' does not match}} +#endif + +#if defined(FIRST) +struct S43 { + static constexpr int x = 1; + int y = 1; +}; +#elif defined(SECOND) +struct S43 { + int x = 1; + static constexpr int y = 1; +}; +#else +S43 s43; +// expected-error@second.h:* {{'S43::x' from module 'second' is not present in definition of 'S43' in module 'first'}} +// expected-note@first.h:* {{declaration of 'x' does not match}} +// expected-error@second.h:* {{'S43::y' from module 'second' is not present in definition of 'S43' in module 'first'}} +// expected-note@first.h:* {{declaration of 'y' does not match}} +//#endif +#endif + +#if defined(FIRST) +void f44(); +struct S44 { + friend void f44(); +}; +#elif defined(SECOND) +void g44(); +struct S44 { + friend void g44(); +}; +#else +S44 s44; +// expected-error@first.h:* {{'S44' has different definitions in different modules; first difference is definition in module 'first' found friend 'f44'}} +// expected-note@second.h:* {{but in 'second' found other friend 'g44'}} +#endif + +#if defined(FIRST) +struct S45 { int n : 1; }; +#elif defined(SECOND) +struct S45 { int n = 1; }; +#else +S45 s45; +// expected-error@first.h:* {{'S45' has different definitions in different modules; first difference is definition in module 'first' found bitfield 'n'}} +// expected-note@second.h:* {{but in 'second' found field 'n'}} +#endif + +// Don't warn on these cases +#if defined(FIRST) +void f01(int = 0); +struct S01 { friend void f01(int); }; +#elif defined(SECOND) +void f01(int); +struct S01 { friend void f01(int); }; +#else +S01 s01; +#endif +// Keep macros contained to one file. + +#ifdef FIRST +#undef FIRST +#endif +#ifdef SECOND +#undef SECOND +#endif Index: test/Modules/merge-using-decls.cpp =================================================================== --- test/Modules/merge-using-decls.cpp +++ test/Modules/merge-using-decls.cpp @@ -37,6 +37,9 @@ // Here, we're instantiating the definition from 'A' and merging the definition // from 'B' into it. +// expected-error@b.h:* {{'D::type' from module 'B' is not present in definition of 'D<T>' in module 'A'}} +// expected-error@b.h:* {{'D::value' from module 'B' is not present in definition of 'D<T>' in module 'A'}} + // expected-error@b.h:* {{'E::value' from module 'B' is not present in definition of 'E<T>' in module 'A'}} // expected-error@b.h:* {{'E::v' from module 'B' is not present in definition of 'E<T>' in module 'A'}} Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -5639,6 +5639,7 @@ Record->push_back(Data.ImplicitCopyAssignmentHasConstParam); Record->push_back(Data.HasDeclaredCopyConstructorWithConstParam); Record->push_back(Data.HasDeclaredCopyAssignmentWithConstParam); + Record->push_back(Data.ODRHash); // IsLambda bit is already saved. Record->push_back(Data.NumBases); Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -1521,6 +1521,7 @@ Data.ImplicitCopyAssignmentHasConstParam = Record[Idx++]; Data.HasDeclaredCopyConstructorWithConstParam = Record[Idx++]; Data.HasDeclaredCopyAssignmentWithConstParam = Record[Idx++]; + Data.ODRHash = Record[Idx++]; Data.NumBases = Record[Idx++]; if (Data.NumBases) @@ -1651,6 +1652,7 @@ OR_FIELD(HasDeclaredCopyConstructorWithConstParam) OR_FIELD(HasDeclaredCopyAssignmentWithConstParam) MATCH_FIELD(IsLambda) + MATCH_FIELD(ODRHash) #undef OR_FIELD #undef MATCH_FIELD Index: lib/Serialization/ASTReader.cpp =================================================================== --- lib/Serialization/ASTReader.cpp +++ lib/Serialization/ASTReader.cpp @@ -8758,23 +8758,712 @@ bool Diagnosed = false; for (auto *RD : Merge.second) { + // Multiple different declarations got merged together; tell the user // where they came from. - if (Merge.first != RD) { - // FIXME: Walk the definition, figure out what's different, - // and diagnose that. - if (!Diagnosed) { - std::string Module = getOwningModuleNameForDiagnostic(Merge.first); - Diag(Merge.first->getLocation(), - diag::err_module_odr_violation_different_definitions) - << Merge.first << Module.empty() << Module; - Diagnosed = true; + if (Merge.first == RD) + continue; + + llvm::SmallVector<std::pair<Decl *, unsigned>, 4> FirstHashes; + llvm::SmallVector<std::pair<Decl *, unsigned>, 4> SecondHashes; + for (auto D : Merge.first->decls()) { + if (D->isImplicit()) + continue; + llvm::FoldingSetNodeID ID; + D->ODRHash(ID); + FirstHashes.emplace_back(D, ID.ComputeHash()); + } + for (auto D : RD->decls()) { + if (D->isImplicit()) + continue; + llvm::FoldingSetNodeID ID; + D->ODRHash(ID); + SecondHashes.emplace_back(D, ID.ComputeHash()); + } + + // Used with err_module_odr_violation_mismatch_decl and + // note_module_odr_violation_mismatch_decl + enum { + EndOfClass, + PublicSpecifer, + PrivateSpecifer, + ProtectedSpecifer, + Friend, + Enum, + StaticAssert, + Typedef, + TypeAlias, + CXXMethod, + CXXConstructor, + CXXDestructor, + CXXConversion, + Field, + Other + } FirstDiffType = Other, + SecondDiffType = Other; + + auto DifferenceSelector = [](Decl *D) { + assert(D && "valid Decl required"); + switch (D->getKind()) { + default: + return Other; + case Decl::AccessSpec: + switch (D->getAccess()) { + case AS_public: + return PublicSpecifer; + case AS_private: + return PrivateSpecifer; + case AS_protected: + return ProtectedSpecifer; + case AS_none: + llvm_unreachable("Invalid access specifier"); + } + case Decl::Friend: + return Friend; + case Decl::Enum: + return Enum; + case Decl::StaticAssert: + return StaticAssert; + case Decl::Typedef: + return Typedef; + case Decl::TypeAlias: + return TypeAlias; + case Decl::CXXMethod: + return CXXMethod; + case Decl::CXXConstructor: + return CXXConstructor; + case Decl::CXXDestructor: + return CXXDestructor; + case Decl::CXXConversion: + return CXXConversion; + case Decl::Field: + return Field; } + }; + + Decl *FirstDecl = nullptr; + Decl *SecondDecl = nullptr; + auto FirstIt = FirstHashes.begin(); + auto SecondIt = SecondHashes.begin(); + + // If there is a diagnoseable difference, FirstDiffType and + // SecondDiffType will not be Other and FirstDecl and SecondDecl will be + // filled in if not EndOfClass. + while (FirstIt != FirstHashes.end() || SecondIt != SecondHashes.end()) { + if (FirstIt->second == SecondIt->second) { + ++FirstIt; + ++SecondIt; + continue; + } + + FirstDecl = FirstIt == FirstHashes.end() ? nullptr : FirstIt->first; + SecondDecl = SecondIt == SecondHashes.end() ? nullptr : SecondIt->first; + + FirstDiffType = FirstDecl ? DifferenceSelector(FirstDecl) : EndOfClass; + SecondDiffType = + SecondDecl ? DifferenceSelector(SecondDecl) : EndOfClass; + + break; + } + + if (FirstDiffType == Other || SecondDiffType == Other) { + // Reaching this point means an unexpected Decl was encountered + // or no difference was detected. This causes a generic error + // message to be emitted. + std::string Module = getOwningModuleNameForDiagnostic(Merge.first); + Diag(Merge.first->getLocation(), + diag::err_module_odr_violation_different_definitions) + << Merge.first << Module.empty() << Module; Diag(RD->getLocation(), diag::note_module_odr_violation_different_definitions) - << getOwningModuleNameForDiagnostic(RD); + << getOwningModuleNameForDiagnostic(RD); + Diagnosed = true; + break; } + + std::string FirstModule = getOwningModuleNameForDiagnostic(Merge.first); + std::string SecondModule = getOwningModuleNameForDiagnostic(RD); + if (FirstDiffType != SecondDiffType) { + SourceLocation FirstLoc; + SourceRange FirstRange; + if (FirstDiffType == EndOfClass) { + FirstLoc = Merge.first->getBraceRange().getEnd(); + } else { + FirstLoc = FirstIt->first->getLocation(); + FirstRange = FirstIt->first->getSourceRange(); + } + Diag(FirstLoc, diag::err_module_odr_violation_mismatch_decl) + << Merge.first << FirstModule.empty() << FirstModule << FirstRange + << FirstDiffType; + + SourceLocation SecondLoc; + SourceRange SecondRange; + if (SecondDiffType == EndOfClass) { + SecondLoc = RD->getBraceRange().getEnd(); + } else { + SecondLoc = SecondDecl->getLocation(); + SecondRange = SecondDecl->getSourceRange(); + } + Diag(SecondLoc, diag::note_module_odr_violation_mismatch_decl) + << SecondModule << SecondRange << SecondDiffType; + Diagnosed = true; + break; + } + + // Used with err_module_odr_violation_mismatch_decl_diff and + // note_module_odr_violation_mismatch_decl_diff + enum { + FriendName, + EnumName, + EnumConstantName, + EnumConstantInit, + EnumConstantNoInit, + EnumConstantDiffInit, + EnumNumberOfConstants, + StaticAssertCondition, + StaticAssertMessage, + StaticAssertOnlyMessage, + TypedefName, + MethodName, + MethodStatic, + MethodInline, + MethodConst, + MethodNumParams, + MethodParamName, + MethodParamType, + MethodDefaultArg, + MethodOnlyDefaultArg, + MethodOnlyBody, + MethodBody, + FieldName, + FieldSingleBitField, + FieldMutable, + }; + + auto ComputeODRHash = [](const Stmt* S) { + assert(S); + llvm::FoldingSetNodeID ID; + S->ODRHash(ID); + return ID.ComputeHash(); + }; + + // At this point, both decls are of the same type. Dive down deeper into + // the Decl to determine where the first difference is located. + switch (FirstDiffType) { + case Friend: { + FriendDecl *FirstFriend = cast<FriendDecl>(FirstDecl); + FriendDecl *SecondFriend = cast<FriendDecl>(SecondDecl); + { + auto D = Diag(FirstFriend->getFriendLoc(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() + << FirstFriend->getSourceRange() << FirstModule + << FriendName; + if (TypeSourceInfo *FirstTSI = FirstFriend->getFriendType()) + D << FirstTSI->getType(); + else + D << FirstFriend->getFriendDecl(); + } + { + auto D = Diag(SecondFriend->getFriendLoc(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondFriend->getSourceRange() + << FriendName; + if (TypeSourceInfo *SecondTSI = SecondFriend->getFriendType()) + D << SecondTSI->getType(); + else + D << SecondFriend->getFriendDecl(); + } + Diagnosed = true; + break; + } + case Enum: { + EnumDecl *FirstEnum = cast<EnumDecl>(FirstDecl); + EnumDecl *SecondEnum = cast<EnumDecl>(SecondDecl); + if (FirstEnum->getName() != SecondEnum->getName()) { + Diag(FirstEnum->getLocStart(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() + << FirstEnum->getSourceRange() << FirstModule << EnumName + << FirstEnum; + Diag(SecondEnum->getLocStart(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondEnum->getSourceRange() << EnumName + << SecondEnum; + Diagnosed = true; + break; + } + + // Don't use EnumDecl::enumerator_{begin,end}. Decl merging can + // cause the iterators from them to be the same for both Decl's. + EnumDecl::enumerator_iterator FirstEnumIt(FirstEnum->decls_begin()); + EnumDecl::enumerator_iterator FirstEnumEnd(FirstEnum->decls_end()); + EnumDecl::enumerator_iterator SecondEnumIt(SecondEnum->decls_begin()); + EnumDecl::enumerator_iterator SecondEnumEnd(SecondEnum->decls_end()); + int NumElements = 0; + for (; FirstEnumIt != FirstEnumEnd && SecondEnumIt != SecondEnumEnd; + ++FirstEnumIt, ++SecondEnumIt, ++NumElements) { + if (FirstEnumIt->getName() != SecondEnumIt->getName()) { + Diag(FirstEnumIt->getLocStart(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() + << FirstEnumIt->getSourceRange() << FirstModule + << EnumConstantName << *FirstEnumIt << FirstEnum; + Diag(SecondEnumIt->getLocStart(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondEnumIt->getSourceRange() + << EnumConstantName << *SecondEnumIt << SecondEnum; + Diagnosed = true; + break; + } + Expr *FirstInit = FirstEnumIt->getInitExpr(); + Expr *SecondInit = SecondEnumIt->getInitExpr(); + + if (FirstInit && !SecondInit) { + Diag(FirstEnumIt->getLocStart(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() + << FirstEnumIt->getSourceRange() << FirstModule + << EnumConstantInit << *FirstEnumIt << FirstEnum; + Diag(SecondEnumIt->getLocStart(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondEnumIt->getSourceRange() + << EnumConstantNoInit << *SecondEnumIt << SecondEnum; + Diagnosed = true; + break; + } + + if (!FirstInit && SecondInit) { + Diag(FirstEnumIt->getLocStart(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() + << FirstEnumIt->getSourceRange() << FirstModule + << EnumConstantNoInit << *FirstEnumIt << FirstEnum; + Diag(SecondEnumIt->getLocStart(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondEnumIt->getSourceRange() + << EnumConstantInit << *SecondEnumIt << SecondEnum; + Diagnosed = true; + break; + } + + if (FirstInit == SecondInit) + continue; + + unsigned FirstODRHash = ComputeODRHash(FirstInit); + unsigned SecondODRHash = ComputeODRHash(SecondInit); + + if (FirstODRHash != SecondODRHash) { + Diag(FirstEnumIt->getLocStart(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() + << FirstEnumIt->getSourceRange() << FirstModule + << EnumConstantDiffInit << *FirstEnumIt << FirstEnum; + Diag(SecondEnumIt->getLocStart(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondEnumIt->getSourceRange() + << EnumConstantDiffInit << *SecondEnumIt << SecondEnum; + Diagnosed = true; + break; + } + } + + if (FirstEnumIt == FirstEnumEnd && SecondEnumIt != SecondEnumEnd) { + unsigned FirstEnumSize = NumElements; + unsigned SecondEnumSize = NumElements; + for (; SecondEnumIt != SecondEnumEnd; ++SecondEnumIt) + ++SecondEnumSize; + Diag(FirstEnum->getLocStart(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() + << FirstEnum->getSourceRange() << FirstModule + << EnumNumberOfConstants << FirstEnum << FirstEnumSize; + Diag(SecondEnum->getLocStart(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondEnum->getSourceRange() + << EnumNumberOfConstants << SecondEnum << SecondEnumSize; + Diagnosed = true; + break; + } + + if (FirstEnumIt != FirstEnumEnd && SecondEnumIt == SecondEnumEnd) { + unsigned FirstEnumSize = NumElements; + unsigned SecondEnumSize = NumElements; + for (; FirstEnumIt != FirstEnumEnd; ++FirstEnumIt) + ++FirstEnumSize; + Diag(FirstEnum->getLocStart(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() + << FirstEnum->getSourceRange() << FirstModule + << EnumNumberOfConstants << FirstEnum << FirstEnumSize; + Diag(SecondEnum->getLocStart(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondEnum->getSourceRange() + << EnumNumberOfConstants << SecondEnum << SecondEnumSize; + Diagnosed = true; + break; + } + + break; + } + case StaticAssert: { + StaticAssertDecl *FirstSA = cast<StaticAssertDecl>(FirstDecl); + StaticAssertDecl *SecondSA = cast<StaticAssertDecl>(SecondDecl); + + Expr *FirstExpr = FirstSA->getAssertExpr(); + Expr *SecondExpr = SecondSA->getAssertExpr(); + unsigned FirstODRHash = ComputeODRHash(FirstExpr); + unsigned SecondODRHash = ComputeODRHash(SecondExpr); + if (FirstODRHash != SecondODRHash) { + Diag(FirstExpr->getLocStart(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() + << FirstExpr->getSourceRange() << FirstModule + << StaticAssertCondition; + Diag(SecondExpr->getLocStart(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondExpr->getSourceRange() + << StaticAssertCondition; + Diagnosed = true; + break; + } + + StringLiteral *FirstStr = FirstSA->getMessage(); + StringLiteral *SecondStr = SecondSA->getMessage(); + if ((FirstStr && !SecondStr) || (!FirstStr && SecondStr)) { + SourceLocation FirstLoc, SecondLoc; + SourceRange FirstRange, SecondRange; + if (FirstStr) { + FirstLoc = FirstStr->getLocStart(); + FirstRange = FirstStr->getSourceRange(); + } else { + FirstLoc = FirstSA->getLocStart(); + FirstRange = FirstSA->getSourceRange(); + } + if (SecondStr) { + SecondLoc = SecondStr->getLocStart(); + SecondRange = SecondStr->getSourceRange(); + } else { + SecondLoc = SecondSA->getLocStart(); + SecondRange = SecondSA->getSourceRange(); + } + Diag(FirstLoc, diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() << FirstRange << FirstModule + << StaticAssertOnlyMessage << (FirstStr == nullptr); + Diag(SecondLoc, diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondRange << StaticAssertOnlyMessage + << (SecondStr == nullptr); + Diagnosed = true; + break; + } + + if (FirstStr && SecondStr && + FirstStr->getString() != SecondStr->getString()) { + Diag(FirstStr->getLocStart(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() + << FirstStr->getSourceRange() << FirstModule + << StaticAssertMessage; + Diag(SecondStr->getLocStart(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondStr->getSourceRange() + << StaticAssertMessage; + Diagnosed = true; + break; + } + break; + } + case Typedef: + case TypeAlias: { + TypedefNameDecl *FirstTD = cast<TypedefNameDecl>(FirstDecl); + TypedefNameDecl *SecondTD = cast<TypedefNameDecl>(SecondDecl); + IdentifierInfo *FirstII = FirstTD->getIdentifier(); + IdentifierInfo *SecondII = SecondTD->getIdentifier(); + if (FirstII && SecondII && FirstII->getName() != SecondII->getName()) { + Diag(FirstTD->getLocation(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() << FirstTD->getLocation() + << FirstModule << TypedefName << (FirstDiffType == TypeAlias) + << FirstII; + Diag(SecondTD->getLocation(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondTD->getLocation() << TypedefName + << (FirstDiffType == TypeAlias) << SecondII; + + Diagnosed = true; + break; + } + break; + } + case CXXMethod: + case CXXConstructor: + case CXXConversion: + case CXXDestructor: { + // TODO: Merge with existing method diff logic. + CXXMethodDecl *FirstMD = cast<CXXMethodDecl>(FirstDecl); + CXXMethodDecl *SecondMD = cast<CXXMethodDecl>(SecondDecl); + IdentifierInfo *FirstII = FirstMD->getIdentifier(); + IdentifierInfo *SecondII = SecondMD->getIdentifier(); + if (FirstII && SecondII && FirstII->getName() != SecondII->getName()) { + Diag(FirstMD->getLocation(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() << FirstMD->getLocation() + << FirstModule << MethodName << FirstII; + Diag(SecondMD->getLocation(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondMD->getLocation() << MethodName + << SecondII; + Diagnosed = true; + break; + } + + bool FirstStatic = FirstMD->getStorageClass() == SC_Static; + bool SecondStatic = SecondMD->getStorageClass() == SC_Static; + if (FirstStatic != SecondStatic) { + Diag(FirstMD->getLocation(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() << FirstModule + << FirstMD->getLocation() << MethodStatic << FirstMD + << FirstStatic; + Diag(SecondMD->getLocation(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondMD->getLocation() << MethodStatic + << SecondMD << SecondStatic; + Diagnosed = true; + break; + } + + bool FirstInline = FirstMD->isInlineSpecified(); + bool SecondInline = SecondMD->isInlineSpecified(); + if (FirstInline != SecondInline) { + Diag(FirstMD->getLocation(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() << FirstModule + << FirstMD->getLocation() << MethodInline << FirstMD + << FirstInline; + Diag(SecondMD->getLocation(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondMD->getLocation() << MethodInline + << SecondMD << SecondInline; + Diagnosed = true; + break; + } + + bool FirstConst = FirstMD->isConst(); + bool SecondConst = SecondMD->isConst(); + if (FirstConst != SecondConst) { + Diag(FirstMD->getLocation(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() << FirstModule + << FirstMD->getLocation() << MethodConst << FirstMD + << FirstInline; + Diag(SecondMD->getLocation(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondMD->getLocation() << MethodConst + << SecondMD << SecondInline; + Diagnosed = true; + break; + } + + if (FirstMD->getNumParams() != SecondMD->getNumParams()) { + Diag(FirstMD->getLocation(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() << FirstModule + << FirstMD->getLocation() << MethodNumParams << SecondMD + << FirstMD->getNumParams(); + Diag(SecondMD->getLocation(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondMD->getLocation() << MethodNumParams + << SecondMD << SecondMD->getNumParams(); + Diagnosed = true; + break; + } + + for (unsigned i = 0, e = FirstMD->getNumParams(); i < e; ++i) { + ParmVarDecl *FirstParam = FirstMD->getParamDecl(i); + ParmVarDecl *SecondParam = SecondMD->getParamDecl(i); + IdentifierInfo *FirstII = FirstParam->getIdentifier(); + IdentifierInfo *SecondII = SecondParam->getIdentifier(); + if ((!FirstII && SecondII) || (FirstII && !SecondII) || + (FirstII && SecondII && + FirstII->getName() != SecondII->getName())) { + Diag(FirstParam->getLocation(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() << FirstModule + << FirstParam->getSourceRange() << MethodParamName << SecondMD + << i + 1 << (FirstII == nullptr) << FirstII; + Diag(SecondParam->getLocation(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondParam->getSourceRange() + << MethodParamName << SecondMD << i + 1 << (SecondII == nullptr) + << SecondII; + + Diagnosed = true; + break; + } + + if (FirstParam->getType() != SecondParam->getType()) { + Diag(FirstParam->getLocation(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() << FirstModule + << FirstParam->getSourceRange() << MethodParamType << SecondMD + << i + 1 << FirstParam->getType(); + Diag(SecondParam->getLocation(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondParam->getSourceRange() + << MethodParamType << SecondMD << i + 1 + << SecondParam->getType(); + + Diagnosed = true; + break; + } + + Expr *FirstDefaultArg = FirstParam->getDefaultArg(); + Expr *SecondDefaultArg = SecondParam->getDefaultArg(); + if ((!FirstDefaultArg && SecondDefaultArg) || + (FirstDefaultArg && !SecondDefaultArg)) { + Diag(FirstParam->getLocation(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() << FirstModule + << FirstParam->getSourceRange() << MethodOnlyDefaultArg + << SecondMD << i + 1 << (FirstDefaultArg != nullptr); + Diag(SecondParam->getLocation(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondParam->getSourceRange() + << MethodOnlyDefaultArg << SecondMD << i + 1 + << (SecondDefaultArg != nullptr); + + Diagnosed = true; + break; + } + + if (FirstDefaultArg && SecondDefaultArg) { + unsigned FirstODRHash = ComputeODRHash(FirstDefaultArg); + unsigned SecondODRHash = ComputeODRHash(SecondDefaultArg); + if (FirstODRHash != SecondODRHash) { + Diag(FirstParam->getLocation(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() << FirstModule + << FirstParam->getSourceRange() << MethodDefaultArg + << SecondMD << i + 1; + Diag(SecondParam->getLocation(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondParam->getSourceRange() + << MethodDefaultArg << SecondMD << i + 1; + + Diagnosed = true; + break; + } + } + } + + // TODO: Figure out how to diagnose different function bodies. + // Deserialization does not import the second function body. + + break; + } + case Field: { + // TODO: Merge with exising field diff logic. + FieldDecl *FirstField = cast<FieldDecl>(FirstDecl); + FieldDecl *SecondField = cast<FieldDecl>(SecondDecl); + IdentifierInfo *FirstII = FirstField->getIdentifier(); + IdentifierInfo *SecondII = SecondField->getIdentifier(); + if (FirstII->getName() != SecondII->getName()) { + Diag(FirstField->getLocation(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() << FirstModule + << FirstField->getSourceRange() << FieldName << FirstII; + Diag(SecondField->getLocation(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondField->getSourceRange() + << FieldName << SecondII; + + Diagnosed = true; + break; + } + + // This case is handled elsewhere. + assert(FirstField->getType() == SecondField->getType()); + + bool FirstBitField = FirstField->isBitField(); + bool SecondBitField = SecondField->isBitField(); + if (FirstBitField != SecondBitField) { + Diag(FirstField->getLocation(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() << FirstModule + << FirstField->getSourceRange() << FieldSingleBitField << FirstII + << FirstBitField; + Diag(SecondField->getLocation(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondField->getSourceRange() + << FieldSingleBitField << SecondII << SecondBitField; + + Diagnosed = true; + break; + } + + if (FirstBitField && SecondBitField) { + Expr* FirstWidth = FirstField->getBitWidth(); + Expr *SecondWidth = SecondField->getBitWidth(); + llvm::FoldingSetNodeID ID; + FirstWidth->ODRHash(ID); + unsigned FirstODRHash = ID.ComputeHash(); + ID.clear(); + SecondWidth->ODRHash(ID); + unsigned SecondODRHash = ID.ComputeHash(); + if (FirstODRHash != SecondODRHash) { + Diag(FirstField->getLocation(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() << FirstModule + << FirstField->getSourceRange() << FieldSingleBitField + << FirstII << FirstBitField; + Diag(SecondField->getLocation(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondField->getSourceRange() + << FieldSingleBitField << SecondII << SecondBitField; + Diagnosed = true; + break; + } + } + + bool FirstMutable = FirstField->isMutable(); + bool SecondMutable = SecondField->isMutable(); + if (FirstMutable != SecondMutable) { + Diag(FirstField->getLocation(), + diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() << FirstModule + << FirstField->getSourceRange() << FieldMutable << FirstII + << FirstMutable; + Diag(SecondField->getLocation(), + diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << SecondField->getSourceRange() << FieldMutable + << SecondII << SecondMutable; + Diagnosed = true; + break; + } + break; + } + case Other: + case EndOfClass: + case PublicSpecifer: + case PrivateSpecifer: + case ProtectedSpecifer: + llvm_unreachable("Invalid diff type"); + } + + if (Diagnosed == true) + continue; + + // Unable to find difference in Decl's, print simple different + // definitions diagnostic. + Diag(Merge.first->getLocation(), + diag::err_module_odr_violation_different_definitions) + << Merge.first << FirstModule.empty() << FirstModule; + Diag(RD->getLocation(), + diag::note_module_odr_violation_different_definitions) + << SecondModule; + Diagnosed = true; } if (!Diagnosed) { Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -13363,8 +13363,11 @@ RD->completeDefinition(); } - if (isa<CXXRecordDecl>(Tag)) + if (auto *RD = dyn_cast<CXXRecordDecl>(Tag)) { FieldCollector->FinishClass(); + if (Context.getLangOpts().Modules) + RD->computeODRHash(); + } // Exit this scope of this tag's definition. PopDeclContext(); Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -3824,3 +3824,426 @@ CXXRecordDecl *MemberPointerType::getMostRecentCXXRecordDecl() const { return getClass()->getAsCXXRecordDecl()->getMostRecentDecl(); } + +static void VisitQualifiers(llvm::FoldingSetNodeID &ID, Qualifiers Quals) { + ID.AddInteger(Quals.getAsOpaqueValue()); +} + +class ODRTypeVisitor : public TypeVisitor<ODRTypeVisitor> { + typedef TypeVisitor<ODRTypeVisitor> Inherited; + llvm::FoldingSetNodeID &ID; + +public: + ODRTypeVisitor(llvm::FoldingSetNodeID &ID) : ID(ID) {} + + void VisitQualType(QualType T) { + ID.AddInteger(T.isNull()); + if (T.isNull()) + return; + SplitQualType split = T.split(); + VisitQualifiers(split.Quals); + Visit(split.Ty); + } + + // This prevents possible recursion between Decl and QaulType ODRHash + // functions. + void VisitDecl(Decl *D) { + NamedDecl *ND = D ? dyn_cast<NamedDecl>(D) : nullptr; + ID.AddBoolean(ND); + if (!ND) + return; + + VisitIdentiferInfo(ND->getIdentifier()); + } + + void VisitTemplateArgument(const TemplateArgument &TA) { + TA.ODRHash(ID); + } + + void VisitQualifiers(Qualifiers Quals) { + ID.AddInteger(Quals.getAsOpaqueValue()); + } + + void VisitStmt(Stmt *S) { + S->ODRHash(ID); + } + + void VisitNestedNameSpecifier(NestedNameSpecifier *NNS) { + ID.AddBoolean(NNS); + if (!NNS) + return; + + VisitNestedNameSpecifier(NNS->getPrefix()); + auto Kind = NNS->getKind(); + ID.AddInteger(Kind); + switch (Kind) { + case NestedNameSpecifier::Identifier: + VisitIdentiferInfo(NNS->getAsIdentifier()); + break; + case NestedNameSpecifier::Namespace: + VisitDecl(NNS->getAsNamespace()); + break; + case NestedNameSpecifier::NamespaceAlias: + VisitDecl(NNS->getAsNamespaceAlias()); + break; + case NestedNameSpecifier::Global: + break; + case NestedNameSpecifier::Super: + break; + case NestedNameSpecifier::TypeSpecWithTemplate: + case NestedNameSpecifier::TypeSpec: + VisitQualType(QualType(NNS->getAsType(), 0)); + break; + } + } + + void VisitIdentiferInfo(const IdentifierInfo *II) { + ID.AddBoolean(II); + if (II) + ID.AddString(II->getName()); + } + + void VisitTemplateName(const TemplateName &TN) { + auto Kind = TN.getKind(); + ID.AddInteger(Kind); + switch (Kind) { + case TemplateName::Template: + VisitDecl(TN.getAsTemplateDecl()); + break; + case TemplateName::OverloadedTemplate: { + OverloadedTemplateStorage *Storage = TN.getAsOverloadedTemplate(); + ID.AddInteger(Storage->size()); + for (NamedDecl *ND : *Storage) + VisitDecl(ND); + break; + } + case TemplateName::QualifiedTemplate: { + QualifiedTemplateName *QTN = TN.getAsQualifiedTemplateName(); + VisitNestedNameSpecifier(QTN->getQualifier()); + ID.AddBoolean(QTN->hasTemplateKeyword()); + VisitDecl(QTN->getTemplateDecl()); + break; + } + case TemplateName::DependentTemplate: { + DependentTemplateName *DTN = TN.getAsDependentTemplateName(); + VisitNestedNameSpecifier(DTN->getQualifier()); + ID.AddBoolean(DTN->isIdentifier()); + if (DTN->isIdentifier()) { + VisitIdentiferInfo(DTN->getIdentifier()); + } else { + ID.AddInteger(DTN->getOperator()); + } + break; + } + case TemplateName::SubstTemplateTemplateParm: { + SubstTemplateTemplateParmStorage *Storage = + TN.getAsSubstTemplateTemplateParm(); + VisitDecl(Storage->getParameter()); + VisitTemplateName(Storage->getReplacement()); + break; + } + case TemplateName::SubstTemplateTemplateParmPack: { + SubstTemplateTemplateParmPackStorage *Storage = + TN.getAsSubstTemplateTemplateParmPack(); + VisitDecl(Storage->getParameterPack()); + VisitTemplateArgument(Storage->getArgumentPack()); + break; + } + } + } + + void VisitType(const Type *T) { ID.AddInteger(T->getTypeClass()); } + + void VisitAdjustedType(const AdjustedType *T) { + VisitQualType(T->getOriginalType()); + VisitQualType(T->getAdjustedType()); + VisitType(T); + } + + void VisitDecayedType(const DecayedType *T) { + VisitQualType(T->getDecayedType()); + VisitQualType(T->getPointeeType()); + VisitAdjustedType(T); + } + + void VisitArrayType(const ArrayType *T) { + VisitQualType(T->getElementType()); + ID.AddInteger(T->getSizeModifier()); + VisitQualifiers(T->getIndexTypeQualifiers()); + VisitType(T); + } + + void VisitConstantArrayType(const ConstantArrayType *T) { + T->getSize().Profile(ID); + VisitArrayType(T); + } + + void VisitDependentSizedArrayType(const DependentSizedArrayType *T) { + VisitStmt(T->getSizeExpr()); + VisitArrayType(T); + } + + void VisitIncompleteArrayType(const IncompleteArrayType *T) { + VisitArrayType(T); + } + + void VisitVariableArrayType(const VariableArrayType *T) { + VisitStmt(T->getSizeExpr()); + VisitArrayType(T); + } + + void VisitAtomicType(const AtomicType *T) { + VisitQualType(T->getValueType()); + VisitType(T); + } + + void VisitAttributedType(const AttributedType *T) { + ID.AddInteger(T->getAttrKind()); + VisitQualType(T->getModifiedType()); + VisitQualType(T->getEquivalentType()); + VisitType(T); + } + + void VisitBlockPointerType(const BlockPointerType *T) { + VisitQualType(T->getPointeeType()); + VisitType(T); + } + + void VisitBuiltinType(const BuiltinType *T) { + ID.AddInteger(T->getKind()); + VisitType(T); + } + + void VisitComplexType(const ComplexType *T) { + VisitQualType(T->getElementType()); + VisitType(T); + } + + void VisitDecltypeType(const DecltypeType *T) { + VisitQualType(T->getUnderlyingType()); + VisitStmt(T->getUnderlyingExpr()); + VisitType(T); + } + + void VisitDependentSizedExtVectorType(const DependentSizedExtVectorType *T) { + VisitQualType(T->getElementType()); + VisitStmt(T->getSizeExpr()); + VisitType(T); + } + + void VisitFunctionType(const FunctionType *T) { + VisitQualType(T->getReturnType()); + T->getExtInfo().Profile(ID); + ID.AddInteger(T->isConst()); + ID.AddInteger(T->isVolatile()); + ID.AddInteger(T->isRestrict()); + VisitType(T); + } + + void VisitFunctionNoProtoType(const FunctionNoProtoType *T) { + VisitFunctionType(T); + } + + void VisitFunctionProtoType(const FunctionProtoType *T) { + ID.AddInteger(T->getNumParams()); + for (auto ParamType : T->getParamTypes()) + VisitQualType(ParamType); + + const auto &epi = T->getExtProtoInfo(); + ID.AddInteger(epi.Variadic); + ID.AddInteger(epi.TypeQuals); + ID.AddInteger(epi.RefQualifier); + ID.AddInteger(epi.ExceptionSpec.Type); + + if (epi.ExceptionSpec.Type == EST_Dynamic) { + for (QualType Ex : epi.ExceptionSpec.Exceptions) + VisitQualType(Ex); + } else if (epi.ExceptionSpec.Type == EST_ComputedNoexcept && + epi.ExceptionSpec.NoexceptExpr) { + VisitStmt(epi.ExceptionSpec.NoexceptExpr); + } else if (epi.ExceptionSpec.Type == EST_Uninstantiated || + epi.ExceptionSpec.Type == EST_Unevaluated) { + VisitDecl(epi.ExceptionSpec.SourceDecl->getCanonicalDecl()); + } + if (epi.ExtParameterInfos) { + for (unsigned i = 0; i != T->getNumParams(); ++i) + ID.AddInteger(epi.ExtParameterInfos[i].getOpaqueValue()); + } + epi.ExtInfo.Profile(ID); + ID.AddBoolean(epi.HasTrailingReturn); + + VisitFunctionType(T); + } + + void VisitInjectedClassNameType(const InjectedClassNameType *T) { + VisitQualType(T->getInjectedSpecializationType()); + VisitDecl(T->getDecl()); + VisitType(T); + } + + void VisitMemberPointerType(const MemberPointerType *T) { + VisitQualType(T->getPointeeType()); + Visit(T->getClass()); + VisitType(T); + } + + void VisitObjCObjectPointerType(const ObjCObjectPointerType *T) { + VisitQualType(T->getPointeeType()); + VisitType(T); + } + + void VisitObjCObjectType(const ObjCObjectType *T) { + QualType Base = T->getBaseType(); + ID.AddBoolean(Base.getTypePtr() != T); + if (Base.getTypePtr() != T) + VisitQualType(Base); + auto TypeArgs = T->getTypeArgsAsWritten(); + ID.AddInteger(TypeArgs.size()); + for (auto TypeArg : TypeArgs) + VisitQualType(TypeArg); + ID.AddInteger(T->getNumProtocols()); + for (auto proto : T->quals()) + VisitDecl(proto); + ID.AddInteger(T->isKindOfTypeAsWritten()); + VisitType(T); + } + + void VisitObjCInterfaceType(const ObjCInterfaceType *T) { + VisitObjCObjectType(T); + } + + void VisitObjCObjectTypeImpl(const ObjCObjectTypeImpl *T) { + VisitObjCObjectType(T); + } + + void VisitPackExpansionType(const PackExpansionType *T) { + VisitQualType(T->getPattern()); + auto NumExpansions = T->getNumExpansions(); + ID.AddBoolean(NumExpansions.hasValue()); + if (NumExpansions) + ID.AddInteger(*NumExpansions); + VisitType(T); + }; + + void VisitPointerType(const PointerType *T) { + VisitQualType(T->getPointeeType()); + VisitType(T); + } + + void VisitReferenceType(const ReferenceType *T) { + VisitQualType(T->getPointeeTypeAsWritten()); + VisitType(T); + } + + void VisitLValueReferenceType(const LValueReferenceType *T) { + VisitReferenceType(T); + } + + void VisitRValueReferenceType(const RValueReferenceType *T) { + VisitReferenceType(T); + } + + void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *T) { + VisitQualType(T->getReplacementType()); + VisitQualType(QualType(T->getReplacedParameter(), 0)); + VisitType(T); + } + + void + VisitSubstTemplateTypeParmPackType(const SubstTemplateTypeParmPackType *T) { + VisitQualType(QualType(T->getReplacedParameter(), 0)); + VisitTemplateArgument(T->getArgumentPack()); + VisitType(T); + } + + void VisitTagType(const TagType *T) { + VisitDecl(T->getDecl()); + ID.AddBoolean(T->isBeingDefined()); + VisitType(T); + } + + void VisitEnumType(const EnumType *T) { + VisitDecl(T->getDecl()); + VisitTagType(T); + } + + void VisitRecordType(const RecordType *T) { + VisitDecl(T->getDecl()); + VisitTagType(T); + } + + void VisitTemplateSpecializationType(const TemplateSpecializationType *T) { + VisitTemplateName(T->getTemplateName()); + ID.AddInteger(T->getNumArgs()); + for (auto I = T->begin(), E = T->end(); I != E; ++I) + VisitTemplateArgument(*I); + VisitType(T); + } + + void VisitTemplateTypeParmType(const TemplateTypeParmType *T) { + ID.AddInteger(T->getDepth()); + ID.AddInteger(T->getIndex()); + ID.AddBoolean(T->isParameterPack()); + VisitDecl(T->getDecl()); + VisitType(T); + } + + void VisitTypedefType(const TypedefType *T) { + VisitDecl(T->getDecl()); + VisitType(T); + } + + void VisitTypeOfExprType(const TypeOfExprType *T) { + VisitStmt(T->getUnderlyingExpr()); + VisitType(T); + } + + void VisitDependentTypeOfExprType(const DependentTypeOfExprType *T) { + VisitTypeOfExprType(T); + } + + void VisitTypeWithKeyword(const TypeWithKeyword *T) { VisitType(T); } + + void VisitElaboratedType(const ElaboratedType *T) { + ID.AddInteger(T->getKeyword()); + VisitNestedNameSpecifier(T->getQualifier()); + VisitQualType(T->getNamedType()); + VisitTypeWithKeyword(T); + } + + void VisitUnaryTransformType(const UnaryTransformType *T) { + VisitQualType(T->getBaseType()); + ID.AddInteger(T->getUTTKind()); + VisitType(T); + } + + void VisitDependentUnaryTransformType(const DependentUnaryTransformType *T) { + VisitUnaryTransformType(T); + } + + void VisitUnresolvedUsingType(const UnresolvedUsingType *T) { + VisitDecl(T->getDecl()); + VisitType(T); + } + + void VisitVectorType(const VectorType *T) { + VisitQualType(T->getElementType()); + ID.AddInteger(T->getNumElements()); + ID.AddInteger(T->getVectorKind()); + VisitType(T); + } + + void VisitExtVectorType(const ExtVectorType *T) { VisitVectorType(T); } +}; + +void Type::ODRHash(llvm::FoldingSetNodeID &ID) const { + ODRTypeVisitor(ID).Visit(this); +} + +void QualType::ODRHash(llvm::FoldingSetNodeID &ID) const { + if (isNull()) + return; + SplitQualType split = this->split(); + VisitQualifiers(ID, split.Quals); + split.Ty->ODRHash(ID); +} Index: lib/AST/TemplateBase.cpp =================================================================== --- lib/AST/TemplateBase.cpp +++ lib/AST/TemplateBase.cpp @@ -296,6 +296,39 @@ } } +void TemplateArgument::ODRHash(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(getKind()); + switch (getKind()) { + case TemplateArgument::Null: + llvm_unreachable("Require valid TemplateArgument"); + case TemplateArgument::Type: + getAsType().ODRHash(ID); + return; + case TemplateArgument::Declaration: + getAsDecl()->ODRHash(ID); + return; + case TemplateArgument::NullPtr: + ID.AddInteger(0); + return; + case TemplateArgument::Integral: + getAsIntegral().Profile(ID); + getIntegralType().ODRHash(ID); + break; + case TemplateArgument::Template: + case TemplateArgument::TemplateExpansion: + getAsTemplateOrTemplatePattern().getAsTemplateDecl()->ODRHash(ID); + break; + case TemplateArgument::Expression: + getAsExpr()->ODRHash(ID); + break; + case TemplateArgument::Pack: + ID.AddInteger(pack_size()); + for (auto SubTA : pack_elements()) + SubTA.ODRHash(ID); + break; + } +} + bool TemplateArgument::structurallyEquals(const TemplateArgument &Other) const { if (getKind() != Other.getKind()) return false; Index: lib/AST/StmtProfile.cpp =================================================================== --- lib/AST/StmtProfile.cpp +++ lib/AST/StmtProfile.cpp @@ -15,6 +15,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/DeclVisitor.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" @@ -25,38 +26,42 @@ namespace { class StmtProfiler : public ConstStmtVisitor<StmtProfiler> { + protected: llvm::FoldingSetNodeID &ID; - const ASTContext &Context; bool Canonical; public: - StmtProfiler(llvm::FoldingSetNodeID &ID, const ASTContext &Context, - bool Canonical) - : ID(ID), Context(Context), Canonical(Canonical) { } + StmtProfiler(llvm::FoldingSetNodeID &ID, bool Canonical) + : ID(ID), Canonical(Canonical) {} + + virtual ~StmtProfiler() {} void VisitStmt(const Stmt *S); #define STMT(Node, Base) void Visit##Node(const Node *S); #include "clang/AST/StmtNodes.inc" /// \brief Visit a declaration that is referenced within an expression /// or statement. - void VisitDecl(const Decl *D); + virtual void VisitDecl(const Decl *D) = 0; /// \brief Visit a type that is referenced within an expression or /// statement. - void VisitType(QualType T); + virtual void VisitType(QualType T) = 0; /// \brief Visit a name that occurs within an expression or statement. - void VisitName(DeclarationName Name); + virtual void VisitName(DeclarationName Name) = 0; + + /// \brief Visit identifiers that are not in Decl's or Type's. + virtual void VisitIdentifierInfo(IdentifierInfo *II) = 0; /// \brief Visit a nested-name-specifier that occurs within an expression /// or statement. - void VisitNestedNameSpecifier(NestedNameSpecifier *NNS); + virtual void VisitNestedNameSpecifier(NestedNameSpecifier *NNS) = 0; /// \brief Visit a template name that occurs within an expression or /// statement. - void VisitTemplateName(TemplateName Name); + virtual void VisitTemplateName(TemplateName Name) = 0; /// \brief Visit template arguments that occur within an expression or /// statement. @@ -66,6 +71,129 @@ /// \brief Visit a single template argument. void VisitTemplateArgument(const TemplateArgument &Arg); }; + + class StmtProfilerWithPointers : public StmtProfiler { + const ASTContext &Context; + + public: + StmtProfilerWithPointers(llvm::FoldingSetNodeID &ID, + const ASTContext &Context, bool Canonical) + : StmtProfiler(ID, Canonical), Context(Context) {} + private: + void VisitDecl(const Decl *D) override { + ID.AddInteger(D ? D->getKind() : 0); + + if (Canonical && D) { + if (const NonTypeTemplateParmDecl *NTTP = + dyn_cast<NonTypeTemplateParmDecl>(D)) { + ID.AddInteger(NTTP->getDepth()); + ID.AddInteger(NTTP->getIndex()); + ID.AddBoolean(NTTP->isParameterPack()); + VisitType(NTTP->getType()); + return; + } + + if (const ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D)) { + // The Itanium C++ ABI uses the type, scope depth, and scope + // index of a parameter when mangling expressions that involve + // function parameters, so we will use the parameter's type for + // establishing function parameter identity. That way, our + // definition of "equivalent" (per C++ [temp.over.link]) is at + // least as strong as the definition of "equivalent" used for + // name mangling. + VisitType(Parm->getType()); + ID.AddInteger(Parm->getFunctionScopeDepth()); + ID.AddInteger(Parm->getFunctionScopeIndex()); + return; + } + + if (const TemplateTypeParmDecl *TTP = + dyn_cast<TemplateTypeParmDecl>(D)) { + ID.AddInteger(TTP->getDepth()); + ID.AddInteger(TTP->getIndex()); + ID.AddBoolean(TTP->isParameterPack()); + return; + } + + if (const TemplateTemplateParmDecl *TTP = + dyn_cast<TemplateTemplateParmDecl>(D)) { + ID.AddInteger(TTP->getDepth()); + ID.AddInteger(TTP->getIndex()); + ID.AddBoolean(TTP->isParameterPack()); + return; + } + } + + ID.AddPointer(D ? D->getCanonicalDecl() : nullptr); + } + + void VisitType(QualType T) override { + if (Canonical) + T = Context.getCanonicalType(T); + + ID.AddPointer(T.getAsOpaquePtr()); + } + + void VisitName(DeclarationName Name) override { + ID.AddPointer(Name.getAsOpaquePtr()); + } + + void VisitIdentifierInfo(IdentifierInfo *II) override { + ID.AddPointer(II); + } + + void VisitNestedNameSpecifier(NestedNameSpecifier *NNS) override { + if (Canonical) + NNS = Context.getCanonicalNestedNameSpecifier(NNS); + ID.AddPointer(NNS); + } + + void VisitTemplateName(TemplateName Name) override { + if (Canonical) + Name = Context.getCanonicalTemplateName(Name); + + Name.Profile(ID); + } + }; + + class StmtProfilerWithoutPointers : public StmtProfiler { + public: + StmtProfilerWithoutPointers(llvm::FoldingSetNodeID &ID) + : StmtProfiler(ID, false) {} + + private: + void VisitType(QualType T) override { + T.ODRHash(ID); + } + void VisitName(DeclarationName Name) override { + if (Name.isEmpty()) { + ID.AddInteger(0); + return; + } + auto Kind = Name.getNameKind(); + ID.AddInteger(Kind); + // TODO: Hash the relevant parts of DeclarationName instead of using + // the string. + SmallString<64> Buffer; + llvm::raw_svector_ostream StrOS(Buffer); + Name.print(StrOS, PrintingPolicy(LangOptions())); + ID.AddString(Buffer); + } + void VisitIdentifierInfo(IdentifierInfo *II) override { + ID.AddString(II->getName()); + } + void VisitDecl(const Decl *D) override { + ID.AddInteger(D ? D->getKind() : 0); + if (!D) { + return; + } + if (auto *ND = dyn_cast<NamedDecl>(D)) { + ID.AddString(ND->getNameAsString()); + } + } + void VisitTemplateName(TemplateName Name) override { } + void VisitNestedNameSpecifier(NestedNameSpecifier *NNS) override { } + }; } void StmtProfiler::VisitStmt(const Stmt *S) { @@ -809,15 +937,15 @@ break; case OffsetOfNode::Identifier: - ID.AddPointer(ON.getFieldName()); + VisitIdentifierInfo(ON.getFieldName()); break; case OffsetOfNode::Base: // These nodes are implicit, and therefore don't need profiling. break; } } - + VisitExpr(S); } @@ -1393,7 +1521,7 @@ if (S->getDestroyedTypeInfo()) VisitType(S->getDestroyedType()); else - ID.AddPointer(S->getDestroyedTypeIdentifier()); + VisitIdentifierInfo(S->getDestroyedTypeIdentifier()); } void StmtProfiler::VisitOverloadExpr(const OverloadExpr *S) { @@ -1643,77 +1771,6 @@ VisitExpr(S); } -void StmtProfiler::VisitDecl(const Decl *D) { - ID.AddInteger(D? D->getKind() : 0); - - if (Canonical && D) { - if (const NonTypeTemplateParmDecl *NTTP = - dyn_cast<NonTypeTemplateParmDecl>(D)) { - ID.AddInteger(NTTP->getDepth()); - ID.AddInteger(NTTP->getIndex()); - ID.AddBoolean(NTTP->isParameterPack()); - VisitType(NTTP->getType()); - return; - } - - if (const ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D)) { - // The Itanium C++ ABI uses the type, scope depth, and scope - // index of a parameter when mangling expressions that involve - // function parameters, so we will use the parameter's type for - // establishing function parameter identity. That way, our - // definition of "equivalent" (per C++ [temp.over.link]) is at - // least as strong as the definition of "equivalent" used for - // name mangling. - VisitType(Parm->getType()); - ID.AddInteger(Parm->getFunctionScopeDepth()); - ID.AddInteger(Parm->getFunctionScopeIndex()); - return; - } - - if (const TemplateTypeParmDecl *TTP = - dyn_cast<TemplateTypeParmDecl>(D)) { - ID.AddInteger(TTP->getDepth()); - ID.AddInteger(TTP->getIndex()); - ID.AddBoolean(TTP->isParameterPack()); - return; - } - - if (const TemplateTemplateParmDecl *TTP = - dyn_cast<TemplateTemplateParmDecl>(D)) { - ID.AddInteger(TTP->getDepth()); - ID.AddInteger(TTP->getIndex()); - ID.AddBoolean(TTP->isParameterPack()); - return; - } - } - - ID.AddPointer(D? D->getCanonicalDecl() : nullptr); -} - -void StmtProfiler::VisitType(QualType T) { - if (Canonical) - T = Context.getCanonicalType(T); - - ID.AddPointer(T.getAsOpaquePtr()); -} - -void StmtProfiler::VisitName(DeclarationName Name) { - ID.AddPointer(Name.getAsOpaquePtr()); -} - -void StmtProfiler::VisitNestedNameSpecifier(NestedNameSpecifier *NNS) { - if (Canonical) - NNS = Context.getCanonicalNestedNameSpecifier(NNS); - ID.AddPointer(NNS); -} - -void StmtProfiler::VisitTemplateName(TemplateName Name) { - if (Canonical) - Name = Context.getCanonicalTemplateName(Name); - - Name.Profile(ID); -} - void StmtProfiler::VisitTemplateArguments(const TemplateArgumentLoc *Args, unsigned NumArgs) { ID.AddInteger(NumArgs); @@ -1763,6 +1820,11 @@ void Stmt::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context, bool Canonical) const { - StmtProfiler Profiler(ID, Context, Canonical); + StmtProfilerWithPointers Profiler(ID, Context, Canonical); + Profiler.Visit(this); +} + +void Stmt::ODRHash(llvm::FoldingSetNodeID &ID) const { + StmtProfilerWithoutPointers Profiler(ID); Profiler.Visit(this); } Index: lib/AST/DeclCXX.cpp =================================================================== --- lib/AST/DeclCXX.cpp +++ lib/AST/DeclCXX.cpp @@ -16,9 +16,11 @@ #include "clang/AST/ASTMutationListener.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/DeclVisitor.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/TypeLoc.h" +#include "clang/AST/TypeVisitor.h" #include "clang/Basic/IdentifierTable.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" @@ -71,8 +73,8 @@ ImplicitCopyAssignmentHasConstParam(true), HasDeclaredCopyConstructorWithConstParam(false), HasDeclaredCopyAssignmentWithConstParam(false), IsLambda(false), - IsParsingBaseSpecifiers(false), NumBases(0), NumVBases(0), Bases(), - VBases(), Definition(D), FirstFriend() {} + IsParsingBaseSpecifiers(false), ODRHash(0), NumBases(0), NumVBases(0), + Bases(), VBases(), Definition(D), FirstFriend() {} CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const { return Bases.get(Definition->getASTContext().getExternalSource()); @@ -371,6 +373,28 @@ data().IsParsingBaseSpecifiers = false; } +void CXXRecordDecl::computeODRHash() { + if (!DefinitionData) + return; + llvm::FoldingSetNodeID ID; + for (auto *D : decls()) { + D->ODRHash(ID); + } + + for (auto base : bases()) { + ID.AddInteger(base.isVirtual()); + base.getType().ODRHash(ID); + } + + if (ClassTemplateDecl *TD = getDescribedClassTemplate()) { + TemplateParameterList *TPL = TD->getTemplateParameters(); + for (NamedDecl *ND : TPL->asArray()) + ND->ODRHash(ID); + } + + DefinitionData->ODRHash = ID.ComputeHash(); +} + void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) { // C++11 [class.copy]p11: // A defaulted copy/move constructor for a class X is defined as Index: lib/AST/DeclBase.cpp =================================================================== --- lib/AST/DeclBase.cpp +++ lib/AST/DeclBase.cpp @@ -22,12 +22,14 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclOpenMP.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/DeclVisitor.h" #include "clang/AST/DependentDiagnostic.h" #include "clang/AST/ExternalASTSource.h" #include "clang/AST/Stmt.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/Type.h" #include "clang/Basic/TargetInfo.h" +#include "llvm/ADT/FoldingSet.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> using namespace clang; @@ -1766,3 +1768,219 @@ return DD; } + +class ODRDeclVisitor : public ConstDeclVisitor<ODRDeclVisitor> { + typedef ConstDeclVisitor<ODRDeclVisitor> Inherited; + llvm::FoldingSetNodeID &ID; + +public: + ODRDeclVisitor(llvm::FoldingSetNodeID &ID) : ID(ID) {} + + void VisitDecl(const Decl *D) { + if (D->isImplicit()) + return; + if (D->isInvalidDecl()) + return; + ID.AddInteger(D->getKind()); + ID.AddInteger(D->hasAttrs()); + + if (auto *DC = dyn_cast<DeclContext>(D)) + for (auto *SubD : DC->decls()) + Visit(SubD); + + Inherited::VisitDecl(D); + } + + void VisitLabelDecl(const LabelDecl *D) { + Inherited::VisitLabelDecl(D); + } + + void VisitEnumDecl(const EnumDecl *D) { + ID.AddInteger(D->isFixed()); + const bool isFixed = D->isFixed(); + ID.AddBoolean(isFixed); + if (isFixed) + D->getIntegerType().ODRHash(ID); + ID.AddInteger(D->isScoped()); + ID.AddInteger(D->isScopedUsingClassTag()); + + Inherited::VisitEnumDecl(D); + } + + void VisitEnumConstantDecl(const EnumConstantDecl *D) { + auto *E = D->getInitExpr(); + ID.AddBoolean(E); + if (E) + VisitStmt(E); + + Inherited::VisitEnumConstantDecl(D); + } + + void VisitNamedDecl(const NamedDecl *D) { + IdentifierInfo *II = D->getIdentifier(); + ID.AddBoolean(II); + if (II) + ID.AddString(II->getName()); + Inherited::VisitNamedDecl(D); + } + + void VisitValueDecl(const ValueDecl *D) { + D->getType().ODRHash(ID); + Inherited::VisitValueDecl(D); + } + + void VisitParmVarDecl(const ParmVarDecl *D) { + VisitStmt(D->getDefaultArg()); + Inherited::VisitParmVarDecl(D); + } + + void VisitAccessSpecDecl(const AccessSpecDecl *D) { + ID.AddInteger(D->getAccess()); + Inherited::VisitAccessSpecDecl(D); + } + + void VisitFriendDecl(const FriendDecl *D) { + TypeSourceInfo *TSI = D->getFriendType(); + ID.AddBoolean(TSI); + if (TSI) + TSI->getType().ODRHash(ID); + else + Visit(D->getFriendDecl()); + + unsigned NumLists = D->getFriendTypeNumTemplateParameterLists(); + ID.AddInteger(NumLists); + for (unsigned i = 0; i < NumLists; ++i) + VisitTemplateParameterList(D->getFriendTypeTemplateParameterList(i)); + Inherited::VisitFriendDecl(D); + } + + void VisitStaticAssertDecl(const StaticAssertDecl *D) { + VisitStmt(D->getAssertExpr()); + + const StringLiteral *SL = D->getMessage(); + ID.AddBoolean(SL); + if (SL) + ID.AddString(D->getMessage()->getString()); + + Inherited::VisitStaticAssertDecl(D); + } + + void VisitTypedefNameDecl(const TypedefNameDecl *D) { + D->getUnderlyingType().ODRHash(ID); + + Inherited::VisitTypedefNameDecl(D); + } + + void VisitFunctionDecl(const FunctionDecl *D) { + VisitStmt(D->getBody()); + + ID.AddInteger(D->getStorageClass()); + ID.AddInteger(D->isInlineSpecified()); + ID.AddInteger(D->isVirtualAsWritten()); + ID.AddInteger(D->isPure()); + ID.AddInteger(D->isDeletedAsWritten()); + + Inherited::VisitFunctionDecl(D); + } + + void VisitCXXMethodDecl(const CXXMethodDecl *D) { + ID.AddInteger(D->isStatic()); + ID.AddInteger(D->isInstance()); + ID.AddInteger(D->isConst()); + ID.AddInteger(D->isVolatile()); + Inherited::VisitCXXMethodDecl(D); + } + + void VisitCXXConstructorDecl(const CXXConstructorDecl *D) { + ID.AddInteger(D->isExplicitSpecified()); + ID.AddInteger(D->getNumCtorInitializers()); + for (auto Initializer : D->inits()) + if (Initializer->isWritten()) + VisitStmt(Initializer->getInit()); + + Inherited::VisitCXXConstructorDecl(D); + } + + void VisitCXXConversionDecl(const CXXConversionDecl *D) { + D->getConversionType().ODRHash(ID); + ID.AddInteger(D->isExplicitSpecified()); + Inherited::VisitCXXConversionDecl(D); + } + + void VisitFieldDecl(const FieldDecl *D) { + ID.AddInteger(D->isMutable()); + + const bool isBitField = D->isBitField(); + ID.AddBoolean(isBitField); + if (isBitField) + VisitStmt(D->getBitWidth()); + + auto Init = D->getInClassInitializer(); + ID.AddBoolean(Init); + if (Init) + VisitStmt(Init); + Inherited::VisitFieldDecl(D); + } + + void VisitTemplateDecl(const TemplateDecl *D) { + NamedDecl *ND = D->getTemplatedDecl(); + ID.AddBoolean(ND); + if (ND) + Visit(ND); + + auto *Parameters = D->getTemplateParameters(); + ID.AddInteger(Parameters->size()); + for (auto *ND : *Parameters) + Visit(ND); + Inherited::VisitTemplateDecl(D); + } + + void VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) { + Inherited::VisitFunctionTemplateDecl(D); + } + + void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) { + const bool hasDefaultArgument = D->hasDefaultArgument(); + ID.AddBoolean(hasDefaultArgument); + if (hasDefaultArgument) + D->getDefaultArgument().ODRHash(ID); + Inherited::VisitTemplateTypeParmDecl(D); + } + + void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D) { + const bool hasDefaultArgument = D->hasDefaultArgument(); + ID.AddBoolean(hasDefaultArgument); + if (hasDefaultArgument) + VisitStmt(D->getDefaultArgument()); + Inherited::VisitNonTypeTemplateParmDecl(D); + } + + void VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl *D) { + const bool hasDefaultArgument = D->hasDefaultArgument(); + ID.AddBoolean(hasDefaultArgument); + if (hasDefaultArgument) + D->getDefaultArgument().getArgument().ODRHash(ID); + Inherited::VisitTemplateTemplateParmDecl(D); + } + + void VisitStmt(const Stmt *S) { + if (!S) + ID.AddInteger(0); + else + S->ODRHash(ID); + } + + void VisitTemplateParameterList(TemplateParameterList *TPL) { + if (!TPL) { + ID.AddInteger(0); + } else { + ID.AddInteger(TPL->size()); + for (auto *ND : TPL->asArray()) + Visit(ND); + } + } +}; + +void Decl::ODRHash(llvm::FoldingSetNodeID &ID) const { + ODRDeclVisitor(ID).Visit(this); +} Index: include/clang/Basic/DiagnosticSerializationKinds.td =================================================================== --- include/clang/Basic/DiagnosticSerializationKinds.td +++ include/clang/Basic/DiagnosticSerializationKinds.td @@ -111,6 +111,70 @@ def err_module_odr_violation_different_instantiations : Error< "instantiation of %q0 is different in different modules">; +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 " + "%select{end of class|public access specifier|private access specifier|" + "protected access specifier|friend declaration|enum|" + "static assert|typedef|type alias|method|constructor|destructor|" + "conversion operator|field|other}3">; +def note_module_odr_violation_mismatch_decl : Note<"but in '%0' found " + "%select{end of class|public access specifier|private access specifier|" + "protected access specifier|friend declaration|enum|" + "static assert|typedef|type alias|method|constructor|destructor|" + "conversion operator|field|other}1">; + +def err_module_odr_violation_mismatch_decl_diff : Error< + "%q0 has different definitions in different modules; first difference is " + "%select{definition in module '%2'|defined here}1 found " + "%select{friend %4|enum %4|element %4 in enum %5|" + "element %4 in enum %5 with initializer|" + "element %4 in enum %5 with no initializer|" + "element %4 in enum %5 with initializer|" + "enum %4 has %5 element%s5|" + "static assert with condition|" + "static assert with message|" + "static assert with %select{|no}4 message|" + "%select{typedef|type alias}4 name %5|" + "method named %4|" + "method %4 is %select{non-|}5static|" + "method %4 is %select{not |}5inline|" + "method %4 is %select{not |}5const|" + "method %4 has %5 parameter%s5|" + "method %4 has %ordinal5 parameter %select{named %7|with no name}6|" + "method %4 has %ordinal5 parameter with type %6|" + "method %4 has %ordinal5 parameter with default argument|" + "method %4 has %ordinal5 parameter with %select{no |}6 default argument|" + "method %4 has %select{|no }5body|" + "method %4 has different body|" + "field %4|" + "%select{field|bitfield}5 %4|" + "%select{non-mutable|mutable}5 %4}3">; +def note_module_odr_violation_mismatch_decl_diff : Note<"but in '%0' found " + "%select{other friend %2|other enum %2|different element %2 in enum %3|" + "element %2 in enum %3 with initializer|" + "element %2 in enum %3 with no initializer|" + "element %2 in enum %3 with different initializer|" + "enum %2 has %3 element%s3|" + "static assert with different condition|" + "static assert with different message|" + "static assert with %select{|no}2 message|" + "different %select{typedef|type alias}2 name %3|" + "method named %2|" + "method %2 is %select{non-|}3static|" + "method %2 is %select{not |}3inline|" + "method %2 is %select{not |}3const|" + "method %2 has %3 parameter%s3|" + "method %2 has %ordinal3 parameter %select{named %5|with no name}4|" + "method %2 has %ordinal3 parameter with type %4|" + "method %2 has %ordinal3 parameter with different default argument|" + "method %2 has %ordinal3 parameter with %select{no |}4default argument|" + "method %2 has %select{|no }3body|" + "method %2 has different body|" + "field %2|" + "%select{field|bitfield}3 %2|" + "%select{non-mutable|mutable}3 %2}1">; + def warn_module_uses_date_time : Warning< "%select{precompiled header|module}0 uses __DATE__ or __TIME__">, InGroup<DiagGroup<"pch-date-time">>; Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -1094,6 +1094,8 @@ /// Remove all qualifiers including _Atomic. QualType getAtomicUnqualifiedType() const; + void ODRHash(llvm::FoldingSetNodeID &ID) const; + private: // These methods are implemented in a separate translation unit; // "static"-ize them to avoid creating temporary QualTypes in the @@ -2005,6 +2007,8 @@ CanQualType getCanonicalTypeUnqualified() const; // in CanonicalType.h void dump() const; + void ODRHash(llvm::FoldingSetNodeID &ID) const; + friend class ASTReader; friend class ASTWriter; }; Index: include/clang/AST/TemplateBase.h =================================================================== --- include/clang/AST/TemplateBase.h +++ include/clang/AST/TemplateBase.h @@ -362,6 +362,9 @@ /// \brief Used to insert TemplateArguments into FoldingSets. void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) const; + + /// \brief Calculate an independent hash value of the TemplateArgument. + void ODRHash(llvm::FoldingSetNodeID &ID) const; }; /// Location information for a TemplateArgument. Index: include/clang/AST/Stmt.h =================================================================== --- include/clang/AST/Stmt.h +++ include/clang/AST/Stmt.h @@ -433,6 +433,12 @@ /// written in the source. void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context, bool Canonical) const; + + /// Unlike Profile, ODRHash does not compute values that are dependent on + /// the value of pointers, making it stable across runs. To achieve this, + /// ODRHash dives deeper into AST nodes and includes more values into ID + /// than Profile. + void ODRHash(llvm::FoldingSetNodeID &ID) const; }; /// DeclStmt - Adaptor class for mixing declarations with statements and Index: include/clang/AST/DeclCXX.h =================================================================== --- include/clang/AST/DeclCXX.h +++ include/clang/AST/DeclCXX.h @@ -458,6 +458,9 @@ /// \brief Whether we are currently parsing base specifiers. unsigned IsParsingBaseSpecifiers : 1; + /// \brief A hash of parts of the class to help in ODR checking. + unsigned ODRHash; + /// \brief The number of base class specifiers in Bases. unsigned NumBases; @@ -703,6 +706,9 @@ return data().IsParsingBaseSpecifiers; } + void computeODRHash(); + unsigned getODRHash() { return data().ODRHash; } + /// \brief Sets the base classes of this struct or class. void setBases(CXXBaseSpecifier const * const *Bases, unsigned NumBases); Index: include/clang/AST/DeclBase.h =================================================================== --- include/clang/AST/DeclBase.h +++ include/clang/AST/DeclBase.h @@ -18,6 +18,7 @@ #include "clang/AST/DeclarationName.h" #include "clang/Basic/Specifiers.h" #include "clang/Basic/VersionTuple.h" +#include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/iterator.h" #include "llvm/ADT/iterator_range.h" @@ -1033,6 +1034,9 @@ /// have a FunctionType. const FunctionType *getFunctionType(bool BlocksToo = true) const; + /// Adds the Decl specific values for the ODR hash to ID. + void ODRHash(llvm::FoldingSetNodeID &ID) const; + private: void setAttrsImpl(const AttrVec& Attrs, ASTContext &Ctx); void setDeclContextsImpl(DeclContext *SemaDC, DeclContext *LexicalDC,
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits