rtrieu updated this revision to Diff 73388.
rtrieu added a comment.

Add a more detailed error message to let users know where the two records 
differ.  This replaces the generic error which only stated that two definitions 
are different without any details.


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,731 @@
+// 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
+
+// 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,714 @@
 
     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();
+        llvm::errs() << FirstII->getName();
+        llvm::errs() << SecondII->getName();
+        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,422 @@
 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) {
+    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) {
+    if (!D)
+      return;
+
+    if (NamedDecl *ND = dyn_cast<NamedDecl>(D))
+      if (IdentifierInfo *II = ND->getIdentifier())
+        ID.AddString(II->getName());
+  }
+
+  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) {
+    if (!NNS) {
+      ID.AddInteger(0);
+      return;
+    }
+    VisitNestedNameSpecifier(NNS->getPrefix());
+    auto Kind = NNS->getKind();
+    ID.AddInteger(Kind);
+    switch (Kind) {
+    case NestedNameSpecifier::Identifier:
+      if (IdentifierInfo *II = NNS->getAsIdentifier()) {
+        ID.AddString(II->getName());
+      }
+      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) {
+    if (II)
+      ID.AddString(II->getName());
+  }
+
+  void VisitTemplateName(const TemplateName &TN) {
+    switch (TN.getKind()) {
+    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();
+    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());
+    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,192 @@
 
   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());
+    if (D->isFixed())
+      D->getIntegerType().ODRHash(ID);
+    ID.AddInteger(D->isScoped());
+    ID.AddInteger(D->isScopedUsingClassTag());
+
+    Inherited::VisitEnumDecl(D);
+  }
+
+  void VisitEnumConstantDecl(const EnumConstantDecl *D) {
+    if (auto *E = D->getInitExpr())
+      VisitStmt(E);
+
+    Inherited::VisitEnumConstantDecl(D);
+  }
+
+  void VisitNamedDecl(const NamedDecl *D) {
+    if (IdentifierInfo *II = D->getIdentifier())
+      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) {
+    if (TypeSourceInfo *TSI = D->getFriendType())
+      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());
+    if (const StringLiteral *SL = D->getMessage())
+      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());
+    ID.AddInteger(D->isBitField());
+    if (D->isBitField())
+      VisitStmt(D->getBitWidth());
+    if (auto Init = D->getInClassInitializer())
+      VisitStmt(Init);
+    Inherited::VisitFieldDecl(D);
+  }
+
+  void VisitTemplateDecl(const TemplateDecl *D) {
+    if (NamedDecl *ND = D->getTemplatedDecl())
+      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) {
+    if (D->hasDefaultArgument())
+      D->getDefaultArgument().ODRHash(ID);
+    Inherited::VisitTemplateTypeParmDecl(D);
+  }
+
+  void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D) {
+    if (D->hasDefaultArgument())
+      VisitStmt(D->getDefaultArgument());
+    Inherited::VisitNonTypeTemplateParmDecl(D);
+  }
+
+  void VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl *D) {
+    if (D->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
+      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

Reply via email to