<div dir="ltr">Hi Richard,<div><br><div>We're seeing lots of dubious errors from this internally. (I'll send you repro instructions separately). I'm reverting because these are hard errors - if this could be made into a warning then we could silence it while we clean up the code or fix the checks.</div></div><div><br></div><div>Cheers,</div><div>Sam</div></div><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Jan 31, 2017 at 2:44 AM, Richard Trieu via cfe-commits <span dir="ltr"><<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: rtrieu<br>
Date: Mon Jan 30 19:44:15 2017<br>
New Revision: 293585<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=293585&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project?rev=293585&view=rev</a><br>
Log:<br>
Add better ODR checking for modules.<br>
<br>
When objects are imported for modules, there is a chance that a name collision<br>
will cause an ODR violation.  Previously, only a small number of such<br>
violations were detected.  This patch provides a stronger check based on<br>
AST nodes.<br>
<br>
The information needed to uniquely identify an object is taked from the AST and<br>
put into a one-dimensional byte stream.  This stream is then hashed to give<br>
a value to represent the object, which is stored with the other object data<br>
in the module.<br>
<br>
When modules are loaded, and Decl's are merged, the hash values of the two<br>
Decl's are compared.  Only Decl's with matched hash values will be merged.<br>
Mismatch hashes will generate a module error, and if possible, point to the<br>
first difference between the two objects.<br>
<br>
The transform from AST to byte stream is a modified depth first algorithm.<br>
Due to references between some AST nodes, a pure depth first algorithm could<br>
generate loops.  For Stmt nodes, a straight depth first processing occurs.<br>
For Type and Decl nodes, they are replaced with an index number and only on<br>
first visit will these nodes be processed.  As an optimization, boolean<br>
values are saved and stored together in reverse order at the end of the<br>
byte stream to lower the ammount of data that needs to be hashed.<br>
<br>
Compile time impact was measured at 1.5-2.0% during module building, and<br>
negligible during builds without module building.<br>
<br>
Differential Revision: <a href="https://reviews.llvm.org/D21675" rel="noreferrer" target="_blank">https://reviews.llvm.org/<wbr>D21675</a><br>
<br>
Added:<br>
    cfe/trunk/include/clang/AST/<wbr>ODRHash.h<br>
    cfe/trunk/lib/AST/ODRHash.cpp<br>
    cfe/trunk/test/Modules/odr_<wbr>hash.cpp<br>
Modified:<br>
    cfe/trunk/include/clang/AST/<wbr>DeclCXX.h<br>
    cfe/trunk/include/clang/AST/<wbr>Stmt.h<br>
    cfe/trunk/include/clang/Basic/<wbr>DiagnosticSerializationKinds.<wbr>td<br>
    cfe/trunk/lib/AST/CMakeLists.<wbr>txt<br>
    cfe/trunk/lib/AST/DeclCXX.cpp<br>
    cfe/trunk/lib/AST/StmtProfile.<wbr>cpp<br>
    cfe/trunk/lib/Sema/SemaDecl.<wbr>cpp<br>
    cfe/trunk/lib/Serialization/<wbr>ASTReader.cpp<br>
    cfe/trunk/lib/Serialization/<wbr>ASTReaderDecl.cpp<br>
    cfe/trunk/lib/Serialization/<wbr>ASTWriter.cpp<br>
    cfe/trunk/test/Modules/merge-<wbr>using-decls.cpp<br>
<br>
Modified: cfe/trunk/include/clang/AST/<wbr>DeclCXX.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=293585&r1=293584&r2=293585&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/include/<wbr>clang/AST/DeclCXX.h?rev=<wbr>293585&r1=293584&r2=293585&<wbr>view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/include/clang/AST/<wbr>DeclCXX.h (original)<br>
+++ cfe/trunk/include/clang/AST/<wbr>DeclCXX.h Mon Jan 30 19:44:15 2017<br>
@@ -458,6 +458,9 @@ class CXXRecordDecl : public RecordDecl<br>
     /// \brief Whether we are currently parsing base specifiers.<br>
     unsigned IsParsingBaseSpecifiers : 1;<br>
<br>
+    /// \brief A hash of parts of the class to help in ODR checking.<br>
+    unsigned ODRHash;<br>
+<br>
     /// \brief The number of base class specifiers in Bases.<br>
     unsigned NumBases;<br>
<br>
@@ -703,6 +706,9 @@ public:<br>
     return data().<wbr>IsParsingBaseSpecifiers;<br>
   }<br>
<br>
+  void computeODRHash();<br>
+  unsigned getODRHash() const { return data().ODRHash; }<br>
+<br>
   /// \brief Sets the base classes of this struct or class.<br>
   void setBases(CXXBaseSpecifier const * const *Bases, unsigned NumBases);<br>
<br>
<br>
Added: cfe/trunk/include/clang/AST/<wbr>ODRHash.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ODRHash.h?rev=293585&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/include/<wbr>clang/AST/ODRHash.h?rev=<wbr>293585&view=auto</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/include/clang/AST/<wbr>ODRHash.h (added)<br>
+++ cfe/trunk/include/clang/AST/<wbr>ODRHash.h Mon Jan 30 19:44:15 2017<br>
@@ -0,0 +1,80 @@<br>
+//===-- ODRHash.h - Hashing to diagnose ODR failures ------------*- C++ -*-===//<br>
+//<br>
+//                     The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===------------------------<wbr>------------------------------<wbr>----------------===//<br>
+///<br>
+/// \file<br>
+/// This file contains the declaration of the ODRHash class, which calculates<br>
+/// a hash based on AST nodes, which is stable across different runs.<br>
+///<br>
+//===------------------------<wbr>------------------------------<wbr>----------------===//<br>
+<br>
+#include "clang/AST/DeclarationName.h"<br>
+#include "clang/AST/Type.h"<br>
+#include "clang/AST/TemplateBase.h"<br>
+#include "llvm/ADT/DenseMap.h"<br>
+#include "llvm/ADT/FoldingSet.h"<br>
+#include "llvm/ADT/PointerUnion.h"<br>
+#include "llvm/ADT/SmallVector.h"<br>
+<br>
+namespace clang {<br>
+<br>
+class Decl;<br>
+class IdentifierInfo;<br>
+class NestedNameSpecifer;<br>
+class Stmt;<br>
+class TemplateParameterList;<br>
+<br>
+// ODRHash is used to calculate a hash based on AST node contents that<br>
+// does not rely on pointer addresses.  This allows the hash to not vary<br>
+// between runs and is usable to detect ODR problems in modules.  To use,<br>
+// construct an ODRHash object, then call Add* methods over the nodes that<br>
+// need to be hashed.  Then call CalculateHash to get the hash value.<br>
+// Typically, only one Add* call is needed.  clear can be called to reuse the<br>
+// object.<br>
+class ODRHash {<br>
+  // Use DenseMaps to convert between Decl and Type pointers and an index value.<br>
+  llvm::DenseMap<const Decl*, unsigned> DeclMap;<br>
+  llvm::DenseMap<const Type*, unsigned> TypeMap;<br>
+<br>
+  // Save space by processing bools at the end.<br>
+  llvm::SmallVector<bool, 128> Bools;<br>
+<br>
+  llvm::FoldingSetNodeID ID;<br>
+<br>
+public:<br>
+  ODRHash() {}<br>
+<br>
+  // Use this for ODR checking classes between modules.  This method compares<br>
+  // more information than the AddDecl class.<br>
+  void AddCXXRecordDecl(const CXXRecordDecl *Record);<br>
+<br>
+  // Add AST nodes that need to be processes.  Some nodes are processed<br>
+  // immediately while others are queued and processed later.<br>
+  void AddDecl(const Decl *D);<br>
+  void AddType(const Type *T);<br>
+  void AddQualType(QualType T);<br>
+  void AddStmt(const Stmt *S);<br>
+  void AddIdentifierInfo(const IdentifierInfo *II);<br>
+  void AddNestedNameSpecifier(const NestedNameSpecifier *NNS);<br>
+  void AddTemplateName(TemplateName Name);<br>
+  void AddDeclarationName(<wbr>DeclarationName Name);<br>
+  void AddTemplateArgument(<wbr>TemplateArgument TA);<br>
+  void AddTemplateParameterList(const TemplateParameterList *TPL);<br>
+<br>
+  // Reset the object for reuse.<br>
+  void clear();<br>
+<br>
+  // Processes any unvisited nodes in Pointers and computes the final hash<br>
+  // value.<br>
+  unsigned CalculateHash();<br>
+<br>
+  // Save booleans until the end to lower the size of data to process.<br>
+  void AddBoolean(bool value);<br>
+};<br>
+<br>
+}  // end namespace clang<br>
<br>
Modified: cfe/trunk/include/clang/AST/<wbr>Stmt.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Stmt.h?rev=293585&r1=293584&r2=293585&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/include/<wbr>clang/AST/Stmt.h?rev=293585&<wbr>r1=293584&r2=293585&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/include/clang/AST/<wbr>Stmt.h (original)<br>
+++ cfe/trunk/include/clang/AST/<wbr>Stmt.h Mon Jan 30 19:44:15 2017<br>
@@ -39,6 +39,7 @@ namespace clang {<br>
   class Expr;<br>
   class IdentifierInfo;<br>
   class LabelDecl;<br>
+  class ODRHash;<br>
   class ParmVarDecl;<br>
   class PrinterHelper;<br>
   struct PrintingPolicy;<br>
@@ -436,6 +437,8 @@ public:<br>
   /// written in the source.<br>
   void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,<br>
                bool Canonical) const;<br>
+<br>
+  void ProcessODRHash(llvm::<wbr>FoldingSetNodeID &ID, ODRHash& Hash) const;<br>
 };<br>
<br>
 /// DeclStmt - Adaptor class for mixing declarations with statements and<br>
<br>
Modified: cfe/trunk/include/clang/Basic/<wbr>DiagnosticSerializationKinds.<wbr>td<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td?rev=293585&r1=293584&r2=293585&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/include/<wbr>clang/Basic/<wbr>DiagnosticSerializationKinds.<wbr>td?rev=293585&r1=293584&r2=<wbr>293585&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/include/clang/Basic/<wbr>DiagnosticSerializationKinds.<wbr>td (original)<br>
+++ cfe/trunk/include/clang/Basic/<wbr>DiagnosticSerializationKinds.<wbr>td Mon Jan 30 19:44:15 2017<br>
@@ -117,6 +117,70 @@ def note_module_odr_violation_<wbr>different_<br>
 def err_module_odr_violation_<wbr>different_instantiations : Error<<br>
   "instantiation of %q0 is different in different modules">;<br>
<br>
+def err_module_odr_violation_<wbr>mismatch_decl : Error<<br>
+  "%q0 has different definitions in different modules; first difference is "<br>
+  "%select{definition in module '%2'|defined here}1 found "<br>
+  "%select{end of class|public access specifier|private access specifier|"<br>
+  "protected access specifier|friend declaration|enum|"<br>
+  "static assert|typedef|type alias|method|constructor|<wbr>destructor|"<br>
+  "conversion operator|field|other}3">;<br>
+def note_module_odr_violation_<wbr>mismatch_decl : Note<"but in '%0' found "<br>
+  "%select{end of class|public access specifier|private access specifier|"<br>
+  "protected access specifier|friend declaration|enum|"<br>
+  "static assert|typedef|type alias|method|constructor|<wbr>destructor|"<br>
+  "conversion operator|field|other}1">;<br>
+<br>
+def err_module_odr_violation_<wbr>mismatch_decl_diff : Error<<br>
+  "%q0 has different definitions in different modules; first difference is "<br>
+  "%select{definition in module '%2'|defined here}1 found "<br>
+  "%select{friend %4|enum %4|element %4 in enum %5|"<br>
+  "element %4 in enum %5 with initializer|"<br>
+  "element %4 in enum %5 with no initializer|"<br>
+  "element %4 in enum %5 with initializer|"<br>
+  "enum %4 has %5 element%s5|"<br>
+  "static assert with condition|"<br>
+  "static assert with message|"<br>
+  "static assert with %select{|no}4 message|"<br>
+  "%select{typedef|type alias}4 name %5|"<br>
+  "method named %4|"<br>
+  "method %4 is %select{non-|}5static|"<br>
+  "method %4 is %select{not |}5inline|"<br>
+  "method %4 is %select{not |}5const|"<br>
+  "method %4 has %5 parameter%s5|"<br>
+  "method %4 has %ordinal5 parameter %select{named %7|with no name}6|"<br>
+  "method %4 has %ordinal5 parameter with type %6|"<br>
+  "method %4 has %ordinal5 parameter with default argument|"<br>
+  "method %4 has %ordinal5 parameter with %select{no |}6 default argument|"<br>
+  "method %4 has %select{|no }5body|"<br>
+  "method %4 has different body|"<br>
+  "field %4|"<br>
+  "%select{field|bitfield}5 %4|"<br>
+  "%select{non-mutable|mutable}5 %4}3">;<br>
+def note_module_odr_violation_<wbr>mismatch_decl_diff : Note<"but in '%0' found "<br>
+  "%select{other friend %2|other enum %2|different element %2 in enum %3|"<br>
+  "element %2 in enum %3 with initializer|"<br>
+  "element %2 in enum %3 with no initializer|"<br>
+  "element %2 in enum %3 with different initializer|"<br>
+  "enum %2 has %3 element%s3|"<br>
+  "static assert with different condition|"<br>
+  "static assert with different message|"<br>
+  "static assert with %select{|no}2 message|"<br>
+  "different %select{typedef|type alias}2 name %3|"<br>
+  "method named %2|"<br>
+  "method %2 is %select{non-|}3static|"<br>
+  "method %2 is %select{not |}3inline|"<br>
+  "method %2 is %select{not |}3const|"<br>
+  "method %2 has %3 parameter%s3|"<br>
+  "method %2 has %ordinal3 parameter %select{named %5|with no name}4|"<br>
+  "method %2 has %ordinal3 parameter with type %4|"<br>
+  "method %2 has %ordinal3 parameter with different default argument|"<br>
+  "method %2 has %ordinal3 parameter with %select{no |}4default argument|"<br>
+  "method %2 has %select{|no }3body|"<br>
+  "method %2 has different body|"<br>
+  "field %2|"<br>
+  "%select{field|bitfield}3 %2|"<br>
+  "%select{non-mutable|mutable}3 %2}1">;<br>
+<br>
 def warn_module_uses_date_time : Warning<<br>
   "%select{precompiled header|module}0 uses __DATE__ or __TIME__">,<br>
   InGroup<DiagGroup<"pch-date-<wbr>time">>;<br>
<br>
Modified: cfe/trunk/lib/AST/CMakeLists.<wbr>txt<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/CMakeLists.txt?rev=293585&r1=293584&r2=293585&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/lib/AST/<wbr>CMakeLists.txt?rev=293585&r1=<wbr>293584&r2=293585&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/lib/AST/CMakeLists.<wbr>txt (original)<br>
+++ cfe/trunk/lib/AST/CMakeLists.<wbr>txt Mon Jan 30 19:44:15 2017<br>
@@ -40,6 +40,7 @@ add_clang_library(clangAST<br>
   MicrosoftMangle.cpp<br>
   NestedNameSpecifier.cpp<br>
   NSAPI.cpp<br>
+  ODRHash.cpp<br>
   OpenMPClause.cpp<br>
   ParentMap.cpp<br>
   RawCommentList.cpp<br>
<br>
Modified: cfe/trunk/lib/AST/DeclCXX.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclCXX.cpp?rev=293585&r1=293584&r2=293585&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/lib/AST/<wbr>DeclCXX.cpp?rev=293585&r1=<wbr>293584&r2=293585&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/lib/AST/DeclCXX.cpp (original)<br>
+++ cfe/trunk/lib/AST/DeclCXX.cpp Mon Jan 30 19:44:15 2017<br>
@@ -18,6 +18,7 @@<br>
 #include "clang/AST/DeclTemplate.h"<br>
 #include "clang/AST/Expr.h"<br>
 #include "clang/AST/ExprCXX.h"<br>
+#include "clang/AST/ODRHash.h"<br>
 #include "clang/AST/TypeLoc.h"<br>
 #include "clang/Basic/IdentifierTable.<wbr>h"<br>
 #include "llvm/ADT/STLExtras.h"<br>
@@ -71,8 +72,8 @@ CXXRecordDecl::DefinitionData:<wbr>:Definitio<br>
       ImplicitCopyAssignmentHasConst<wbr>Param(true),<br>
       HasDeclaredCopyConstructorWith<wbr>ConstParam(false),<br>
       HasDeclaredCopyAssignmentWithC<wbr>onstParam(false), IsLambda(false),<br>
-      IsParsingBaseSpecifiers(false)<wbr>, NumBases(0), NumVBases(0), Bases(),<br>
-      VBases(), Definition(D), FirstFriend() {}<br>
+      IsParsingBaseSpecifiers(false)<wbr>, ODRHash(0), NumBases(0), NumVBases(0),<br>
+      Bases(), VBases(), Definition(D), FirstFriend() {}<br>
<br>
 CXXBaseSpecifier *CXXRecordDecl::<wbr>DefinitionData::<wbr>getBasesSlowCase() const {<br>
   return Bases.get(Definition-><wbr>getASTContext().<wbr>getExternalSource());<br>
@@ -371,6 +372,16 @@ CXXRecordDecl::setBases(<wbr>CXXBaseSpecifier<br>
   data().IsParsingBaseSpecifiers = false;<br>
 }<br>
<br>
+void CXXRecordDecl::computeODRHash(<wbr>) {<br>
+  if (!DefinitionData)<br>
+    return;<br>
+<br>
+  ODRHash Hash;<br>
+  Hash.AddCXXRecordDecl(this);<br>
+<br>
+  DefinitionData->ODRHash = Hash.CalculateHash();<br>
+}<br>
+<br>
 void CXXRecordDecl::<wbr>addedClassSubobject(<wbr>CXXRecordDecl *Subobj) {<br>
   // C++11 [class.copy]p11:<br>
   //   A defaulted copy/move constructor for a class X is defined as<br>
<br>
Added: cfe/trunk/lib/AST/ODRHash.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ODRHash.cpp?rev=293585&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/lib/AST/<wbr>ODRHash.cpp?rev=293585&view=<wbr>auto</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/lib/AST/ODRHash.cpp (added)<br>
+++ cfe/trunk/lib/AST/ODRHash.cpp Mon Jan 30 19:44:15 2017<br>
@@ -0,0 +1,892 @@<br>
+//===-- ODRHash.cpp - Hashing to diagnose ODR failures ----------*- C++ -*-===//<br>
+//<br>
+//                     The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===------------------------<wbr>------------------------------<wbr>----------------===//<br>
+///<br>
+/// \file<br>
+/// This file implements the ODRHash class, which calculates a hash based<br>
+/// on AST nodes, which is stable across different runs.<br>
+///<br>
+//===------------------------<wbr>------------------------------<wbr>----------------===//<br>
+<br>
+#include "clang/AST/ODRHash.h"<br>
+<br>
+#include "clang/AST/DeclVisitor.h"<br>
+#include "clang/AST/<wbr>NestedNameSpecifier.h"<br>
+#include "clang/AST/StmtVisitor.h"<br>
+#include "clang/AST/TypeVisitor.h"<br>
+<br>
+using namespace clang;<br>
+<br>
+// This method adds more information that AddDecl does.<br>
+void ODRHash::AddCXXRecordDecl(<wbr>const CXXRecordDecl *Record) {<br>
+  assert(Record && Record->hasDefinition() &&<br>
+         "Expected non-null record to be a definition.");<br>
+  AddDecl(Record);<br>
+<br>
+  // Additional information that is not needed in AddDecl.  Do not move this<br>
+  // to ODRTypeVisitor.<br>
+  ID.AddInteger(Record-><wbr>getNumBases());<br>
+  for (auto base : Record->bases()) {<br>
+    AddBoolean(base.isVirtual());<br>
+    AddQualType(base.getType());<br>
+  }<br>
+<br>
+  const ClassTemplateDecl *TD = Record-><wbr>getDescribedClassTemplate();<br>
+  AddBoolean(TD);<br>
+  if (TD) {<br>
+    AddTemplateParameterList(TD-><wbr>getTemplateParameters());<br>
+  }<br>
+}<br>
+<br>
+// Hashing for Stmt is with Stmt::Profile, since they derive from the same base<br>
+// class.<br>
+void ODRHash::AddStmt(const Stmt *S) {<br>
+  assert(S && "Expecting non-null pointer.");<br>
+  S->ProcessODRHash(ID, *this);<br>
+}<br>
+<br>
+void ODRHash::AddIdentifierInfo(<wbr>const IdentifierInfo *II) {<br>
+  assert(II && "Expecting non-null pointer.");<br>
+  ID.AddString(II->getName());<br>
+}<br>
+<br>
+void ODRHash::<wbr>AddNestedNameSpecifier(const NestedNameSpecifier *NNS) {<br>
+  assert(NNS && "Expecting non-null pointer.");<br>
+  const auto *Prefix = NNS->getPrefix();<br>
+  AddBoolean(Prefix);<br>
+  if (Prefix) {<br>
+    AddNestedNameSpecifier(Prefix)<wbr>;<br>
+  }<br>
+<br>
+  auto Kind = NNS->getKind();<br>
+  ID.AddInteger(Kind);<br>
+  switch (Kind) {<br>
+  case NestedNameSpecifier::<wbr>Identifier:<br>
+    AddIdentifierInfo(NNS-><wbr>getAsIdentifier());<br>
+    break;<br>
+  case NestedNameSpecifier::<wbr>Namespace:<br>
+    AddDecl(NNS->getAsNamespace())<wbr>;<br>
+    break;<br>
+  case NestedNameSpecifier::<wbr>NamespaceAlias:<br>
+    AddDecl(NNS-><wbr>getAsNamespaceAlias());<br>
+    break;<br>
+  case NestedNameSpecifier::TypeSpec:<br>
+  case NestedNameSpecifier::<wbr>TypeSpecWithTemplate:<br>
+    AddType(NNS->getAsType());<br>
+    break;<br>
+  case NestedNameSpecifier::Global:<br>
+  case NestedNameSpecifier::Super:<br>
+    break;<br>
+  }<br>
+}<br>
+<br>
+void ODRHash::AddTemplateName(<wbr>TemplateName Name) {<br>
+  const auto Kind = Name.getKind();<br>
+  ID.AddInteger(Kind);<br>
+  AddBoolean(Name.isDependent())<wbr>;<br>
+  AddBoolean(Name.<wbr>isInstantiationDependent());<br>
+  switch (Kind) {<br>
+  case TemplateName::Template:<br>
+    AddDecl(Name.<wbr>getAsTemplateDecl());<br>
+    break;<br>
+  case TemplateName::<wbr>OverloadedTemplate: {<br>
+    const auto *Storage = Name.getAsOverloadedTemplate()<wbr>;<br>
+    ID.AddInteger(Storage->size())<wbr>;<br>
+    for (const auto *ND : *Storage) {<br>
+      AddDecl(ND);<br>
+    }<br>
+    break;<br>
+  }<br>
+  case TemplateName::<wbr>QualifiedTemplate: {<br>
+    const auto *QTN = Name.<wbr>getAsQualifiedTemplateName();<br>
+    AddNestedNameSpecifier(QTN-><wbr>getQualifier());<br>
+    AddBoolean(QTN-><wbr>hasTemplateKeyword());<br>
+    AddDecl(QTN->getDecl());<br>
+    break;<br>
+  }<br>
+  case TemplateName::<wbr>DependentTemplate: {<br>
+    const auto *DTN = Name.<wbr>getAsDependentTemplateName();<br>
+    AddBoolean(DTN->isIdentifier()<wbr>);<br>
+    if (DTN->isIdentifier()) {<br>
+      AddIdentifierInfo(DTN-><wbr>getIdentifier());<br>
+    } else {<br>
+      ID.AddInteger(DTN-><wbr>getOperator());<br>
+    }<br>
+    break;<br>
+  }<br>
+  case TemplateName::<wbr>SubstTemplateTemplateParm: {<br>
+    const auto *Storage = Name.<wbr>getAsSubstTemplateTemplateParm<wbr>();<br>
+    AddDecl(Storage->getParameter(<wbr>));<br>
+    AddTemplateName(Storage-><wbr>getReplacement());<br>
+    break;<br>
+  }<br>
+  case TemplateName::<wbr>SubstTemplateTemplateParmPack: {<br>
+    const auto *Storage = Name.<wbr>getAsSubstTemplateTemplateParm<wbr>Pack();<br>
+    AddDecl(Storage-><wbr>getParameterPack());<br>
+    AddTemplateArgument(Storage-><wbr>getArgumentPack());<br>
+    break;<br>
+  }<br>
+  }<br>
+}<br>
+<br>
+void ODRHash::AddDeclarationName(<wbr>DeclarationName Name) {<br>
+  AddBoolean(Name.isEmpty());<br>
+  if (Name.isEmpty()) {<br>
+    return;<br>
+  }<br>
+<br>
+  auto Kind = Name.getNameKind();<br>
+  ID.AddInteger(Kind);<br>
+  switch (Kind) {<br>
+  case DeclarationName::Identifier:<br>
+    AddIdentifierInfo(Name.<wbr>getAsIdentifierInfo());<br>
+    break;<br>
+  case DeclarationName::<wbr>ObjCZeroArgSelector:<br>
+  case DeclarationName::<wbr>ObjCOneArgSelector:<br>
+  case DeclarationName::<wbr>ObjCMultiArgSelector: {<br>
+    Selector S = Name.getObjCSelector();<br>
+    AddBoolean(S.isNull());<br>
+    AddBoolean(S.<wbr>isKeywordSelector());<br>
+    AddBoolean(S.isUnarySelector()<wbr>);<br>
+    unsigned NumArgs = S.getNumArgs();<br>
+    for (unsigned i = 0; i < NumArgs; ++i) {<br>
+      AddIdentifierInfo(S.<wbr>getIdentifierInfoForSlot(i));<br>
+    }<br>
+    break;<br>
+  }<br>
+  case DeclarationName::<wbr>CXXConstructorName:<br>
+  case DeclarationName::<wbr>CXXDestructorName:<br>
+    AddQualType(Name.<wbr>getCXXNameType());<br>
+    break;<br>
+  case DeclarationName::<wbr>CXXOperatorName:<br>
+    ID.AddInteger(Name.<wbr>getCXXOverloadedOperator());<br>
+    break;<br>
+  case DeclarationName::<wbr>CXXLiteralOperatorName:<br>
+    AddIdentifierInfo(Name.<wbr>getCXXLiteralIdentifier());<br>
+    break;<br>
+  case DeclarationName::<wbr>CXXConversionFunctionName:<br>
+    AddQualType(Name.<wbr>getCXXNameType());<br>
+    break;<br>
+  case DeclarationName::<wbr>CXXUsingDirective:<br>
+    break;<br>
+  }<br>
+}<br>
+<br>
+void ODRHash::AddTemplateArgument(<wbr>TemplateArgument TA) {<br>
+  const auto Kind = TA.getKind();<br>
+  ID.AddInteger(Kind);<br>
+  switch (Kind) {<br>
+  case TemplateArgument::Null:<br>
+    llvm_unreachable("Require valid TemplateArgument");<br>
+  case TemplateArgument::Type:<br>
+    AddQualType(TA.getAsType());<br>
+    break;<br>
+  case TemplateArgument::Declaration:<br>
+    AddDecl(TA.getAsDecl());<br>
+    break;<br>
+  case TemplateArgument::NullPtr:<br>
+    AddQualType(TA.getNullPtrType(<wbr>));<br>
+    break;<br>
+  case TemplateArgument::Integral:<br>
+    TA.getAsIntegral().Profile(ID)<wbr>;<br>
+    AddQualType(TA.<wbr>getIntegralType());<br>
+    break;<br>
+  case TemplateArgument::Template:<br>
+  case TemplateArgument::<wbr>TemplateExpansion:<br>
+    AddTemplateName(TA.<wbr>getAsTemplateOrTemplatePattern<wbr>());<br>
+    break;<br>
+  case TemplateArgument::Expression:<br>
+    AddStmt(TA.getAsExpr());<br>
+    break;<br>
+  case TemplateArgument::Pack:<br>
+    ID.AddInteger(TA.pack_size());<br>
+    for (auto SubTA : TA.pack_elements()) {<br>
+      AddTemplateArgument(SubTA);<br>
+    }<br>
+    break;<br>
+  }<br>
+}<br>
+<br>
+void ODRHash::<wbr>AddTemplateParameterList(const TemplateParameterList *TPL) {<br>
+  assert(TPL && "Expecting non-null pointer.");<br>
+  ID.AddInteger(TPL->size());<br>
+  for (auto *ND : TPL->asArray()) {<br>
+    AddDecl(ND);<br>
+  }<br>
+}<br>
+<br>
+void ODRHash::clear() {<br>
+  DeclMap.clear();<br>
+  TypeMap.clear();<br>
+  Bools.clear();<br>
+  ID.clear();<br>
+}<br>
+<br>
+unsigned ODRHash::CalculateHash() {<br>
+  // Append the bools to the end of the data segment backwards.  This allows<br>
+  // for the bools data to be compressed 32 times smaller compared to using<br>
+  // ID.AddBoolean<br>
+  const unsigned unsigned_bits = sizeof(unsigned) * CHAR_BIT;<br>
+  const unsigned size = Bools.size();<br>
+  const unsigned remainder = size % unsigned_bits;<br>
+  const unsigned loops = size / unsigned_bits;<br>
+  auto I = Bools.rbegin();<br>
+  unsigned value = 0;<br>
+  for (unsigned i = 0; i < remainder; ++i) {<br>
+    value <<= 1;<br>
+    value |= *I;<br>
+    ++I;<br>
+  }<br>
+  ID.AddInteger(value);<br>
+<br>
+  for (unsigned i = 0; i < loops; ++i) {<br>
+    value = 0;<br>
+    for (unsigned j = 0; j < unsigned_bits; ++j) {<br>
+      value <<= 1;<br>
+      value |= *I;<br>
+      ++I;<br>
+    }<br>
+    ID.AddInteger(value);<br>
+  }<br>
+<br>
+  assert(I == Bools.rend());<br>
+  return ID.ComputeHash();<br>
+}<br>
+<br>
+// Process a Decl pointer.  Add* methods call back into ODRHash while Visit*<br>
+// methods process the relevant parts of the Decl.<br>
+class ODRDeclVisitor : public ConstDeclVisitor<<wbr>ODRDeclVisitor> {<br>
+  typedef ConstDeclVisitor<<wbr>ODRDeclVisitor> Inherited;<br>
+  llvm::FoldingSetNodeID &ID;<br>
+  ODRHash &Hash;<br>
+<br>
+public:<br>
+  ODRDeclVisitor(llvm::<wbr>FoldingSetNodeID &ID, ODRHash &Hash)<br>
+      : ID(ID), Hash(Hash) {}<br>
+<br>
+  void AddDecl(const Decl *D) {<br>
+    Hash.AddBoolean(D);<br>
+    if (D) {<br>
+      Hash.AddDecl(D);<br>
+    }<br>
+  }<br>
+<br>
+  void AddStmt(const Stmt *S) {<br>
+    Hash.AddBoolean(S);<br>
+    if (S) {<br>
+      Hash.AddStmt(S);<br>
+    }<br>
+  }<br>
+<br>
+  void AddQualType(QualType T) {<br>
+    Hash.AddQualType(T);<br>
+  }<br>
+<br>
+  void AddIdentifierInfo(const IdentifierInfo *II) {<br>
+    Hash.AddBoolean(II);<br>
+    if (II) {<br>
+      Hash.AddIdentifierInfo(II);<br>
+    }<br>
+  }<br>
+<br>
+  void AddTemplateParameterList(<wbr>TemplateParameterList *TPL) {<br>
+    Hash.AddBoolean(TPL);<br>
+    if (TPL) {<br>
+      Hash.AddTemplateParameterList(<wbr>TPL);<br>
+    }<br>
+  }<br>
+<br>
+  void AddTemplateArgument(<wbr>TemplateArgument TA) {<br>
+    Hash.AddTemplateArgument(TA);<br>
+  }<br>
+<br>
+  void VisitDecl(const Decl *D) {<br>
+    if (!D) {<br>
+      return;<br>
+    }<br>
+    if (D->isImplicit()) {<br>
+      return;<br>
+    }<br>
+    if (D->isInvalidDecl()) {<br>
+      return;<br>
+    }<br>
+    ID.AddInteger(D->getKind());<br>
+    Hash.AddBoolean(D->hasAttrs())<wbr>;<br>
+<br>
+    if (auto *DC = dyn_cast<DeclContext>(D)) {<br>
+      llvm::SmallVector<const Decl *, 16> Decls;<br>
+      for (const Decl* D : DC->decls()) {<br>
+        if (!D->isImplicit()) {<br>
+          Decls.push_back(D);<br>
+        }<br>
+      }<br>
+      ID.AddInteger(Decls.size());<br>
+      for (auto SubDecl : Decls) {<br>
+        AddDecl(SubDecl);<br>
+      }<br>
+    }<br>
+<br>
+    Inherited::VisitDecl(D);<br>
+  }<br>
+<br>
+  void VisitLabelDecl(const LabelDecl *D) {<br>
+    Inherited::VisitLabelDecl(D);<br>
+  }<br>
+<br>
+  void VisitEnumDecl(const EnumDecl *D) {<br>
+    const bool isFixed = D->isFixed();<br>
+    Hash.AddBoolean(isFixed);<br>
+    if (isFixed) {<br>
+      AddQualType(D->getIntegerType(<wbr>));<br>
+    }<br>
+    Hash.AddBoolean(D->isScoped())<wbr>;<br>
+    Hash.AddBoolean(D-><wbr>isScopedUsingClassTag());<br>
+<br>
+    Inherited::VisitEnumDecl(D);<br>
+  }<br>
+<br>
+  void VisitEnumConstantDecl(const EnumConstantDecl *D) {<br>
+    auto *E = D->getInitExpr();<br>
+    AddStmt(E);<br>
+<br>
+    Inherited::<wbr>VisitEnumConstantDecl(D);<br>
+  }<br>
+<br>
+  void VisitNamedDecl(const NamedDecl *D) {<br>
+    AddIdentifierInfo(D-><wbr>getIdentifier());<br>
+    Inherited::VisitNamedDecl(D);<br>
+  }<br>
+<br>
+  void VisitValueDecl(const ValueDecl *D) {<br>
+    AddQualType(D->getType());<br>
+    Inherited::VisitValueDecl(D);<br>
+  }<br>
+<br>
+  void VisitParmVarDecl(const ParmVarDecl *D) {<br>
+    AddStmt(D->getDefaultArg());<br>
+    Inherited::VisitParmVarDecl(D)<wbr>;<br>
+  }<br>
+<br>
+  void VisitAccessSpecDecl(const AccessSpecDecl *D) {<br>
+    ID.AddInteger(D->getAccess());<br>
+    Inherited::<wbr>VisitAccessSpecDecl(D);<br>
+  }<br>
+<br>
+  void VisitFriendDecl(const FriendDecl *D) {<br>
+    TypeSourceInfo *TSI = D->getFriendType();<br>
+    Hash.AddBoolean(TSI);<br>
+    if (TSI) {<br>
+      AddQualType(TSI->getType());<br>
+    } else {<br>
+      AddDecl(D->getFriendDecl());<br>
+    }<br>
+<br>
+    unsigned NumLists = D-><wbr>getFriendTypeNumTemplateParame<wbr>terLists();<br>
+    ID.AddInteger(NumLists);<br>
+    for (unsigned i = 0; i < NumLists; ++i) {<br>
+      AddTemplateParameterList(D-><wbr>getFriendTypeTemplateParameter<wbr>List(i));<br>
+    }<br>
+<br>
+    Inherited::VisitFriendDecl(D);<br>
+  }<br>
+<br>
+  void VisitStaticAssertDecl(const StaticAssertDecl *D) {<br>
+    AddStmt(D->getAssertExpr());<br>
+    AddStmt(D->getMessage());<br>
+<br>
+    Inherited::<wbr>VisitStaticAssertDecl(D);<br>
+  }<br>
+<br>
+  void VisitTypedefNameDecl(const TypedefNameDecl *D) {<br>
+    AddQualType(D-><wbr>getUnderlyingType());<br>
+<br>
+    Inherited::<wbr>VisitTypedefNameDecl(D);<br>
+  }<br>
+<br>
+  void VisitFunctionDecl(const FunctionDecl *D) {<br>
+    AddStmt(D->getBody());<br>
+<br>
+    ID.AddInteger(D-><wbr>getStorageClass());<br>
+    Hash.AddBoolean(D-><wbr>isInlineSpecified());<br>
+    Hash.AddBoolean(D-><wbr>isVirtualAsWritten());<br>
+    Hash.AddBoolean(D->isPure());<br>
+    Hash.AddBoolean(D-><wbr>isDeletedAsWritten());<br>
+    ID.AddInteger(D-><wbr>getOverloadedOperator());<br>
+    Inherited::VisitFunctionDecl(<wbr>D);<br>
+  }<br>
+<br>
+  void VisitCXXMethodDecl(const CXXMethodDecl *D) {<br>
+    Hash.AddBoolean(D->isStatic())<wbr>;<br>
+    Hash.AddBoolean(D->isInstance(<wbr>));<br>
+    Hash.AddBoolean(D->isConst());<br>
+    Hash.AddBoolean(D->isVolatile(<wbr>));<br>
+    Inherited::VisitCXXMethodDecl(<wbr>D);<br>
+  }<br>
+<br>
+  void VisitCXXConstructorDecl(const CXXConstructorDecl *D) {<br>
+    Hash.AddBoolean(D-><wbr>isExplicitSpecified());<br>
+    unsigned NumCtorInits = 0;<br>
+    llvm::SmallVector<<wbr>CXXCtorInitializer *, 4> Initializers;<br>
+    ID.AddInteger(D-><wbr>getNumCtorInitializers());<br>
+    for (auto Initializer : D->inits()) {<br>
+      if (Initializer->isWritten()) {<br>
+        ++NumCtorInits;<br>
+        Initializers.push_back(<wbr>Initializer);<br>
+      }<br>
+    }<br>
+    for (auto Initializer : Initializers) {<br>
+      AddStmt(Initializer->getInit()<wbr>);<br>
+    }<br>
+<br>
+    Inherited::<wbr>VisitCXXConstructorDecl(D);<br>
+  }<br>
+<br>
+  void VisitCXXConversionDecl(const CXXConversionDecl *D) {<br>
+    AddQualType(D-><wbr>getConversionType());<br>
+    Hash.AddBoolean(D-><wbr>isExplicitSpecified());<br>
+    Inherited::<wbr>VisitCXXConversionDecl(D);<br>
+  }<br>
+<br>
+  void VisitFieldDecl(const FieldDecl *D) {<br>
+    Hash.AddBoolean(D->isMutable()<wbr>);<br>
+<br>
+    const bool isBitField = D->isBitField();<br>
+    Hash.AddBoolean(isBitField);<br>
+    if (isBitField) {<br>
+      AddStmt(D->getBitWidth());<br>
+    }<br>
+<br>
+    AddStmt(D-><wbr>getInClassInitializer());<br>
+<br>
+    Inherited::VisitFieldDecl(D);<br>
+  }<br>
+<br>
+  void VisitTemplateDecl(const TemplateDecl *D) {<br>
+    AddDecl(D->getTemplatedDecl())<wbr>;<br>
+<br>
+    auto *Parameters = D->getTemplateParameters();<br>
+    ID.AddInteger(Parameters-><wbr>size());<br>
+    for (auto *ND : *Parameters) {<br>
+      AddDecl(ND);<br>
+    }<br>
+<br>
+    Inherited::VisitTemplateDecl(<wbr>D);<br>
+  }<br>
+<br>
+  void VisitFunctionTemplateDecl(<wbr>const FunctionTemplateDecl *D) {<br>
+    Inherited::<wbr>VisitFunctionTemplateDecl(D);<br>
+  }<br>
+<br>
+  void VisitTemplateTypeParmDecl(<wbr>const TemplateTypeParmDecl *D) {<br>
+    const bool hasDefaultArgument = D->hasDefaultArgument();<br>
+    Hash.AddBoolean(<wbr>hasDefaultArgument);<br>
+    if (hasDefaultArgument) {<br>
+      AddTemplateArgument(D-><wbr>getDefaultArgument());<br>
+    }<br>
+<br>
+    Inherited::<wbr>VisitTemplateTypeParmDecl(D);<br>
+  }<br>
+<br>
+  void VisitNonTypeTemplateParmDecl(<wbr>const NonTypeTemplateParmDecl *D) {<br>
+    AddStmt(D->hasDefaultArgument(<wbr>) ? D->getDefaultArgument() : nullptr);<br>
+<br>
+    Inherited::<wbr>VisitNonTypeTemplateParmDecl(<wbr>D);<br>
+  }<br>
+<br>
+  void VisitTemplateTemplateParmDecl(<wbr>const TemplateTemplateParmDecl *D) {<br>
+    const bool hasDefaultArgument = D->hasDefaultArgument();<br>
+    Hash.AddBoolean(<wbr>hasDefaultArgument);<br>
+    if (hasDefaultArgument) {<br>
+      AddTemplateArgument(D-><wbr>getDefaultArgument().<wbr>getArgument());<br>
+    }<br>
+<br>
+    Inherited::<wbr>VisitTemplateTemplateParmDecl(<wbr>D);<br>
+  }<br>
+};<br>
+<br>
+void ODRHash::AddDecl(const Decl *D) {<br>
+  assert(D && "Expecting non-null pointer.");<br>
+  auto Result = DeclMap.insert(std::make_pair(<wbr>D, DeclMap.size()));<br>
+  ID.AddInteger(Result.first-><wbr>second);<br>
+  // On first encounter of a Decl pointer, process it.  Every time afterwards,<br>
+  // only the index value is needed.<br>
+  if (Result.second) {<br>
+    ODRDeclVisitor(ID, *this).Visit(D);<br>
+  }<br>
+}<br>
+<br>
+// Process a Type pointer.  Add* methods call back into ODRHash while Visit*<br>
+// methods process the relevant parts of the Type.<br>
+class ODRTypeVisitor : public TypeVisitor<ODRTypeVisitor> {<br>
+  typedef TypeVisitor<ODRTypeVisitor> Inherited;<br>
+  llvm::FoldingSetNodeID &ID;<br>
+  ODRHash &Hash;<br>
+<br>
+public:<br>
+  ODRTypeVisitor(llvm::<wbr>FoldingSetNodeID &ID, ODRHash &Hash)<br>
+      : ID(ID), Hash(Hash) {}<br>
+<br>
+  void AddQualType(QualType T) {<br>
+    Hash.AddQualType(T);<br>
+  }<br>
+<br>
+  void AddDecl(Decl *D) {<br>
+    Hash.AddBoolean(D);<br>
+    if (D) {<br>
+      Hash.AddDecl(D);<br>
+    }<br>
+  }<br>
+<br>
+  void AddTemplateArgument(<wbr>TemplateArgument TA) {<br>
+    Hash.AddTemplateArgument(TA);<br>
+  }<br>
+<br>
+  void AddStmt(Stmt *S) {<br>
+    Hash.AddBoolean(S);<br>
+    if (S) {<br>
+      Hash.AddStmt(S);<br>
+    }<br>
+  }<br>
+<br>
+  void AddNestedNameSpecifier(<wbr>NestedNameSpecifier *NNS) {<br>
+    Hash.AddBoolean(NNS);<br>
+    if (NNS) {<br>
+      Hash.AddNestedNameSpecifier(<wbr>NNS);<br>
+    }<br>
+  }<br>
+  void AddIdentiferInfo(const IdentifierInfo *II) {<br>
+    Hash.AddBoolean(II);<br>
+    if (II) {<br>
+      Hash.AddIdentifierInfo(II);<br>
+    }<br>
+  }<br>
+<br>
+  void AddTemplateName(TemplateName TN) {<br>
+    Hash.AddTemplateName(TN);<br>
+  }<br>
+<br>
+  void VisitQualifiers(Qualifiers Quals) {<br>
+    ID.AddInteger(Quals.<wbr>getAsOpaqueValue());<br>
+  }<br>
+<br>
+  void VisitType(const Type *T) { ID.AddInteger(T->getTypeClass(<wbr>)); }<br>
+<br>
+  void VisitAdjustedType(const AdjustedType *T) {<br>
+    AddQualType(T-><wbr>getOriginalType());<br>
+    AddQualType(T-><wbr>getAdjustedType());<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitDecayedType(const DecayedType *T) {<br>
+    AddQualType(T->getDecayedType(<wbr>));<br>
+    AddQualType(T->getPointeeType(<wbr>));<br>
+    VisitAdjustedType(T);<br>
+  }<br>
+<br>
+  void VisitArrayType(const ArrayType *T) {<br>
+    AddQualType(T->getElementType(<wbr>));<br>
+    ID.AddInteger(T-><wbr>getSizeModifier());<br>
+    VisitQualifiers(T-><wbr>getIndexTypeQualifiers());<br>
+    VisitType(T);<br>
+  }<br>
+  void VisitConstantArrayType(const ConstantArrayType *T) {<br>
+    T->getSize().Profile(ID);<br>
+    VisitArrayType(T);<br>
+  }<br>
+<br>
+  void VisitDependentSizedArrayType(<wbr>const DependentSizedArrayType *T) {<br>
+    AddStmt(T->getSizeExpr());<br>
+    VisitArrayType(T);<br>
+  }<br>
+<br>
+  void VisitIncompleteArrayType(const IncompleteArrayType *T) {<br>
+    VisitArrayType(T);<br>
+  }<br>
+<br>
+  void VisitVariableArrayType(const VariableArrayType *T) {<br>
+    AddStmt(T->getSizeExpr());<br>
+    VisitArrayType(T);<br>
+  }<br>
+<br>
+  void VisitAtomicType(const AtomicType *T) {<br>
+    AddQualType(T->getValueType())<wbr>;<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitAttributedType(const AttributedType *T) {<br>
+    ID.AddInteger(T->getAttrKind()<wbr>);<br>
+    AddQualType(T-><wbr>getModifiedType());<br>
+    AddQualType(T-><wbr>getEquivalentType());<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitBlockPointerType(const BlockPointerType *T) {<br>
+    AddQualType(T->getPointeeType(<wbr>));<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitBuiltinType(const BuiltinType *T) {<br>
+    ID.AddInteger(T->getKind());<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitComplexType(const ComplexType *T) {<br>
+    AddQualType(T->getElementType(<wbr>));<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitDecltypeType(const DecltypeType *T) {<br>
+    AddQualType(T-><wbr>getUnderlyingType());<br>
+    AddStmt(T->getUnderlyingExpr()<wbr>);<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitDependentSizedExtVectorTy<wbr>pe(const DependentSizedExtVectorType *T) {<br>
+    AddQualType(T->getElementType(<wbr>));<br>
+    AddStmt(T->getSizeExpr());<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitFunctionType(const FunctionType *T) {<br>
+    AddQualType(T->getReturnType()<wbr>);<br>
+    T->getExtInfo().Profile(ID);<br>
+    Hash.AddBoolean(T->isConst());<br>
+    Hash.AddBoolean(T->isVolatile(<wbr>));<br>
+    Hash.AddBoolean(T->isRestrict(<wbr>));<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitFunctionNoProtoType(const FunctionNoProtoType *T) {<br>
+    VisitFunctionType(T);<br>
+  }<br>
+<br>
+  void VisitFunctionProtoType(const FunctionProtoType *T) {<br>
+    ID.AddInteger(T->getNumParams(<wbr>));<br>
+    for (auto ParamType : T->getParamTypes()) {<br>
+      AddQualType(ParamType);<br>
+    }<br>
+<br>
+    const auto &epi = T->getExtProtoInfo();<br>
+    ID.AddInteger(epi.Variadic);<br>
+    ID.AddInteger(epi.TypeQuals);<br>
+    ID.AddInteger(epi.<wbr>RefQualifier);<br>
+    ID.AddInteger(epi.<wbr>ExceptionSpec.Type);<br>
+<br>
+    if (epi.ExceptionSpec.Type == EST_Dynamic) {<br>
+      for (QualType Ex : epi.ExceptionSpec.Exceptions) {<br>
+        AddQualType(Ex);<br>
+      }<br>
+    } else if (epi.ExceptionSpec.Type == EST_ComputedNoexcept &&<br>
+               epi.ExceptionSpec.<wbr>NoexceptExpr) {<br>
+      AddStmt(epi.ExceptionSpec.<wbr>NoexceptExpr);<br>
+    } else if (epi.ExceptionSpec.Type == EST_Uninstantiated ||<br>
+               epi.ExceptionSpec.Type == EST_Unevaluated) {<br>
+      AddDecl(epi.ExceptionSpec.<wbr>SourceDecl->getCanonicalDecl()<wbr>);<br>
+    }<br>
+    if (epi.ExtParameterInfos) {<br>
+      for (unsigned i = 0; i != T->getNumParams(); ++i) {<br>
+        ID.AddInteger(epi.<wbr>ExtParameterInfos[i].<wbr>getOpaqueValue());<br>
+      }<br>
+    }<br>
+    epi.ExtInfo.Profile(ID);<br>
+    Hash.AddBoolean(epi.<wbr>HasTrailingReturn);<br>
+<br>
+    VisitFunctionType(T);<br>
+  }<br>
+<br>
+  void VisitInjectedClassNameType(<wbr>const InjectedClassNameType *T) {<br>
+    AddQualType(T-><wbr>getInjectedSpecializationType(<wbr>));<br>
+    AddDecl(T->getDecl());<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitMemberPointerType(const MemberPointerType *T) {<br>
+    AddQualType(T->getPointeeType(<wbr>));<br>
+    Visit(T->getClass());<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitObjCObjectPointerType(<wbr>const ObjCObjectPointerType *T) {<br>
+    AddQualType(T->getPointeeType(<wbr>));<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitObjCObjectType(const ObjCObjectType *T) {<br>
+    QualType Base = T->getBaseType();<br>
+    const bool SameType = Base.getTypePtr() == T;<br>
+    Hash.AddBoolean(SameType);<br>
+    if (!SameType) {<br>
+      AddQualType(Base);<br>
+    }<br>
+    auto TypeArgs = T->getTypeArgsAsWritten();<br>
+    ID.AddInteger(TypeArgs.size())<wbr>;<br>
+    for (auto TypeArg : TypeArgs) {<br>
+      AddQualType(TypeArg);<br>
+    }<br>
+    ID.AddInteger(T-><wbr>getNumProtocols());<br>
+    for (auto proto : T->quals()) {<br>
+      AddDecl(proto);<br>
+    }<br>
+    ID.AddInteger(T-><wbr>isKindOfTypeAsWritten());<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitObjCInterfaceType(const ObjCInterfaceType *T) {<br>
+    VisitObjCObjectType(T);<br>
+  }<br>
+<br>
+  void VisitObjCObjectTypeImpl(const ObjCObjectTypeImpl *T) {<br>
+    VisitObjCObjectType(T);<br>
+  }<br>
+<br>
+  void VisitPackExpansionType(const PackExpansionType *T) {<br>
+    AddQualType(T->getPattern());<br>
+    auto NumExpansions = T->getNumExpansions();<br>
+    Hash.AddBoolean(NumExpansions.<wbr>hasValue());<br>
+    if (NumExpansions) {<br>
+      ID.AddInteger(*NumExpansions);<br>
+    }<br>
+    VisitType(T);<br>
+  };<br>
+<br>
+  void VisitPointerType(const PointerType *T) {<br>
+    AddQualType(T->getPointeeType(<wbr>));<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitReferenceType(const ReferenceType *T) {<br>
+    AddQualType(T-><wbr>getPointeeTypeAsWritten());<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitLValueReferenceType(const LValueReferenceType *T) {<br>
+    VisitReferenceType(T);<br>
+  }<br>
+<br>
+  void VisitRValueReferenceType(const RValueReferenceType *T) {<br>
+    VisitReferenceType(T);<br>
+  }<br>
+<br>
+  void VisitSubstTemplateTypeParmType<wbr>(const SubstTemplateTypeParmType *T) {<br>
+    AddQualType(T-><wbr>getReplacementType());<br>
+    AddQualType(QualType(T-><wbr>getReplacedParameter(), 0));<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void<br>
+  VisitSubstTemplateTypeParmPack<wbr>Type(const SubstTemplateTypeParmPackType *T) {<br>
+    AddQualType(QualType(T-><wbr>getReplacedParameter(), 0));<br>
+    AddTemplateArgument(T-><wbr>getArgumentPack());<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitTagType(const TagType *T) {<br>
+    AddDecl(T->getDecl());<br>
+    Hash.AddBoolean(T-><wbr>isBeingDefined());<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitEnumType(const EnumType *T) {<br>
+    AddDecl(T->getDecl());<br>
+    VisitTagType(T);<br>
+  }<br>
+<br>
+  void VisitRecordType(const RecordType *T) {<br>
+    AddDecl(T->getDecl());<br>
+    VisitTagType(T);<br>
+  }<br>
+<br>
+  void VisitTemplateSpecializationTyp<wbr>e(const TemplateSpecializationType *T) {<br>
+    AddTemplateName(T-><wbr>getTemplateName());<br>
+    ID.AddInteger(T->getNumArgs())<wbr>;<br>
+    for (auto I = T->begin(), E = T->end(); I != E; ++I) {<br>
+      AddTemplateArgument(*I);<br>
+    }<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitTemplateTypeParmType(<wbr>const TemplateTypeParmType *T) {<br>
+    ID.AddInteger(T->getDepth());<br>
+    ID.AddInteger(T->getIndex());<br>
+    Hash.AddBoolean(T-><wbr>isParameterPack());<br>
+    AddDecl(T->getDecl());<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitTypedefType(const TypedefType *T) {<br>
+    AddDecl(T->getDecl());<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitTypeOfExprType(const TypeOfExprType *T) {<br>
+    AddStmt(T->getUnderlyingExpr()<wbr>);<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitDependentTypeOfExprType(<wbr>const DependentTypeOfExprType *T) {<br>
+    VisitTypeOfExprType(T);<br>
+  }<br>
+<br>
+  void VisitTypeWithKeyword(const TypeWithKeyword *T) { VisitType(T); }<br>
+<br>
+  void VisitElaboratedType(const ElaboratedType *T) {<br>
+    ID.AddInteger(T->getKeyword())<wbr>;<br>
+    AddNestedNameSpecifier(T-><wbr>getQualifier());<br>
+    AddQualType(T->getNamedType())<wbr>;<br>
+    VisitTypeWithKeyword(T);<br>
+  }<br>
+<br>
+  void VisitUnaryTransformType(const UnaryTransformType *T) {<br>
+    AddQualType(T->getBaseType());<br>
+    ID.AddInteger(T->getUTTKind())<wbr>;<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitDependentUnaryTransformTy<wbr>pe(const DependentUnaryTransformType *T) {<br>
+    VisitUnaryTransformType(T);<br>
+  }<br>
+<br>
+  void VisitUnresolvedUsingType(const UnresolvedUsingType *T) {<br>
+    AddDecl(T->getDecl());<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitVectorType(const VectorType *T) {<br>
+    AddQualType(T->getElementType(<wbr>));<br>
+    ID.AddInteger(T-><wbr>getNumElements());<br>
+    ID.AddInteger(T-><wbr>getVectorKind());<br>
+    VisitType(T);<br>
+  }<br>
+<br>
+  void VisitExtVectorType(const ExtVectorType *T) { VisitVectorType(T); }<br>
+};<br>
+<br>
+void ODRHash::AddType(const Type *T) {<br>
+  assert(T && "Expecting non-null pointer.");<br>
+  auto Result = TypeMap.insert(std::make_pair(<wbr>T, TypeMap.size()));<br>
+  ID.AddInteger(Result.first-><wbr>second);<br>
+  // On first encounter of a Type pointer, process it.  Every time afterwards,<br>
+  // only the index value is needed.<br>
+  if (Result.second) {<br>
+    ODRTypeVisitor(ID, *this).Visit(T);<br>
+  }<br>
+}<br>
+<br>
+void ODRHash::AddQualType(QualType T) {<br>
+  AddBoolean(T.isNull());<br>
+  if (T.isNull()) {<br>
+    return;<br>
+  }<br>
+  SplitQualType split = T.split();<br>
+  ID.AddInteger(split.Quals.<wbr>getAsOpaqueValue());<br>
+  AddType(split.Ty);<br>
+}<br>
+<br>
+void ODRHash::AddBoolean(bool Value) {<br>
+  Bools.push_back(Value);<br>
+}<br>
<br>
Modified: cfe/trunk/lib/AST/StmtProfile.<wbr>cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/StmtProfile.cpp?rev=293585&r1=293584&r2=293585&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/lib/AST/<wbr>StmtProfile.cpp?rev=293585&r1=<wbr>293584&r2=293585&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/lib/AST/StmtProfile.<wbr>cpp (original)<br>
+++ cfe/trunk/lib/AST/StmtProfile.<wbr>cpp Mon Jan 30 19:44:15 2017<br>
@@ -19,20 +19,22 @@<br>
 #include "clang/AST/ExprCXX.h"<br>
 #include "clang/AST/ExprObjC.h"<br>
 #include "clang/AST/ExprOpenMP.h"<br>
+#include "clang/AST/ODRHash.h"<br>
 #include "clang/AST/StmtVisitor.h"<br>
 #include "llvm/ADT/FoldingSet.h"<br>
 using namespace clang;<br>
<br>
 namespace {<br>
   class StmtProfiler : public ConstStmtVisitor<StmtProfiler> {<br>
+   protected:<br>
     llvm::FoldingSetNodeID &ID;<br>
-    const ASTContext &Context;<br>
     bool Canonical;<br>
<br>
   public:<br>
-    StmtProfiler(llvm::<wbr>FoldingSetNodeID &ID, const ASTContext &Context,<br>
-                 bool Canonical)<br>
-      : ID(ID), Context(Context), Canonical(Canonical) { }<br>
+    StmtProfiler(llvm::<wbr>FoldingSetNodeID &ID, bool Canonical)<br>
+        : ID(ID), Canonical(Canonical) {}<br>
+<br>
+    virtual ~StmtProfiler() {}<br>
<br>
     void VisitStmt(const Stmt *S);<br>
<br>
@@ -41,22 +43,25 @@ namespace {<br>
<br>
     /// \brief Visit a declaration that is referenced within an expression<br>
     /// or statement.<br>
-    void VisitDecl(const Decl *D);<br>
+    virtual void VisitDecl(const Decl *D) = 0;<br>
<br>
     /// \brief Visit a type that is referenced within an expression or<br>
     /// statement.<br>
-    void VisitType(QualType T);<br>
+    virtual void VisitType(QualType T) = 0;<br>
<br>
     /// \brief Visit a name that occurs within an expression or statement.<br>
-    void VisitName(DeclarationName Name);<br>
+    virtual void VisitName(DeclarationName Name) = 0;<br>
+<br>
+    /// \brief Visit identifiers that are not in Decl's or Type's.<br>
+    virtual void VisitIdentifierInfo(<wbr>IdentifierInfo *II) = 0;<br>
<br>
     /// \brief Visit a nested-name-specifier that occurs within an expression<br>
     /// or statement.<br>
-    void VisitNestedNameSpecifier(<wbr>NestedNameSpecifier *NNS);<br>
+    virtual void VisitNestedNameSpecifier(<wbr>NestedNameSpecifier *NNS) = 0;<br>
<br>
     /// \brief Visit a template name that occurs within an expression or<br>
     /// statement.<br>
-    void VisitTemplateName(TemplateName Name);<br>
+    virtual void VisitTemplateName(TemplateName Name) = 0;<br>
<br>
     /// \brief Visit template arguments that occur within an expression or<br>
     /// statement.<br>
@@ -66,6 +71,127 @@ namespace {<br>
     /// \brief Visit a single template argument.<br>
     void VisitTemplateArgument(const TemplateArgument &Arg);<br>
   };<br>
+<br>
+  class StmtProfilerWithPointers : public StmtProfiler {<br>
+    const ASTContext &Context;<br>
+<br>
+  public:<br>
+    StmtProfilerWithPointers(llvm:<wbr>:FoldingSetNodeID &ID,<br>
+                             const ASTContext &Context, bool Canonical)<br>
+        : StmtProfiler(ID, Canonical), Context(Context) {}<br>
+  private:<br>
+    void VisitDecl(const Decl *D) override {<br>
+      ID.AddInteger(D ? D->getKind() : 0);<br>
+<br>
+      if (Canonical && D) {<br>
+        if (const NonTypeTemplateParmDecl *NTTP =<br>
+                dyn_cast<<wbr>NonTypeTemplateParmDecl>(D)) {<br>
+          ID.AddInteger(NTTP->getDepth()<wbr>);<br>
+          ID.AddInteger(NTTP->getIndex()<wbr>);<br>
+          ID.AddBoolean(NTTP-><wbr>isParameterPack());<br>
+          VisitType(NTTP->getType());<br>
+          return;<br>
+        }<br>
+<br>
+        if (const ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D)) {<br>
+          // The Itanium C++ ABI uses the type, scope depth, and scope<br>
+          // index of a parameter when mangling expressions that involve<br>
+          // function parameters, so we will use the parameter's type for<br>
+          // establishing function parameter identity. That way, our<br>
+          // definition of "equivalent" (per C++ [temp.over.link]) is at<br>
+          // least as strong as the definition of "equivalent" used for<br>
+          // name mangling.<br>
+          VisitType(Parm->getType());<br>
+          ID.AddInteger(Parm-><wbr>getFunctionScopeDepth());<br>
+          ID.AddInteger(Parm-><wbr>getFunctionScopeIndex());<br>
+          return;<br>
+        }<br>
+<br>
+        if (const TemplateTypeParmDecl *TTP =<br>
+                dyn_cast<TemplateTypeParmDecl><wbr>(D)) {<br>
+          ID.AddInteger(TTP->getDepth())<wbr>;<br>
+          ID.AddInteger(TTP->getIndex())<wbr>;<br>
+          ID.AddBoolean(TTP-><wbr>isParameterPack());<br>
+          return;<br>
+        }<br>
+<br>
+        if (const TemplateTemplateParmDecl *TTP =<br>
+                dyn_cast<<wbr>TemplateTemplateParmDecl>(D)) {<br>
+          ID.AddInteger(TTP->getDepth())<wbr>;<br>
+          ID.AddInteger(TTP->getIndex())<wbr>;<br>
+          ID.AddBoolean(TTP-><wbr>isParameterPack());<br>
+          return;<br>
+        }<br>
+      }<br>
+<br>
+      ID.AddPointer(D ? D->getCanonicalDecl() : nullptr);<br>
+    }<br>
+<br>
+    void VisitType(QualType T) override {<br>
+      if (Canonical)<br>
+        T = Context.getCanonicalType(T);<br>
+<br>
+      ID.AddPointer(T.<wbr>getAsOpaquePtr());<br>
+    }<br>
+<br>
+    void VisitName(DeclarationName Name) override {<br>
+      ID.AddPointer(Name.<wbr>getAsOpaquePtr());<br>
+    }<br>
+<br>
+    void VisitIdentifierInfo(<wbr>IdentifierInfo *II) override {<br>
+      ID.AddPointer(II);<br>
+    }<br>
+<br>
+    void VisitNestedNameSpecifier(<wbr>NestedNameSpecifier *NNS) override {<br>
+      if (Canonical)<br>
+        NNS = Context.<wbr>getCanonicalNestedNameSpecifie<wbr>r(NNS);<br>
+      ID.AddPointer(NNS);<br>
+    }<br>
+<br>
+    void VisitTemplateName(TemplateName Name) override {<br>
+      if (Canonical)<br>
+        Name = Context.<wbr>getCanonicalTemplateName(Name)<wbr>;<br>
+<br>
+      Name.Profile(ID);<br>
+    }<br>
+  };<br>
+<br>
+  class StmtProfilerWithoutPointers : public StmtProfiler {<br>
+    ODRHash &Hash;<br>
+  public:<br>
+    StmtProfilerWithoutPointers(<wbr>llvm::FoldingSetNodeID &ID, ODRHash &Hash)<br>
+        : StmtProfiler(ID, false), Hash(Hash) {}<br>
+<br>
+  private:<br>
+    void VisitType(QualType T) override {<br>
+      Hash.AddQualType(T);<br>
+    }<br>
+<br>
+    void VisitName(DeclarationName Name) override {<br>
+      Hash.AddDeclarationName(Name);<br>
+    }<br>
+    void VisitIdentifierInfo(<wbr>IdentifierInfo *II) override {<br>
+      ID.AddBoolean(II);<br>
+      if (II) {<br>
+        Hash.AddIdentifierInfo(II);<br>
+      }<br>
+    }<br>
+    void VisitDecl(const Decl *D) override {<br>
+      ID.AddBoolean(D);<br>
+      if (D) {<br>
+        Hash.AddDecl(D);<br>
+      }<br>
+    }<br>
+    void VisitTemplateName(TemplateName Name) override {<br>
+      Hash.AddTemplateName(Name);<br>
+    }<br>
+    void VisitNestedNameSpecifier(<wbr>NestedNameSpecifier *NNS) override {<br>
+      ID.AddBoolean(NNS);<br>
+      if (NNS) {<br>
+        Hash.AddNestedNameSpecifier(<wbr>NNS);<br>
+      }<br>
+    }<br>
+  };<br>
 }<br>
<br>
 void StmtProfiler::VisitStmt(const Stmt *S) {<br>
@@ -853,7 +979,7 @@ void StmtProfiler::<wbr>VisitOffsetOfExpr(con<br>
       break;<br>
<br>
     case OffsetOfNode::Identifier:<br>
-      ID.AddPointer(ON.getFieldName(<wbr>));<br>
+      VisitIdentifierInfo(ON.<wbr>getFieldName());<br>
       break;<br>
<br>
     case OffsetOfNode::Base:<br>
@@ -861,7 +987,7 @@ void StmtProfiler::<wbr>VisitOffsetOfExpr(con<br>
       break;<br>
     }<br>
   }<br>
-<br>
+<br>
   VisitExpr(S);<br>
 }<br>
<br>
@@ -1451,7 +1577,7 @@ StmtProfiler::<wbr>VisitCXXPseudoDestructorEx<br>
   if (S->getDestroyedTypeInfo())<br>
     VisitType(S->getDestroyedType(<wbr>));<br>
   else<br>
-    ID.AddPointer(S-><wbr>getDestroyedTypeIdentifier());<br>
+    VisitIdentifierInfo(S-><wbr>getDestroyedTypeIdentifier());<br>
 }<br>
<br>
 void StmtProfiler::<wbr>VisitOverloadExpr(const OverloadExpr *S) {<br>
@@ -1701,77 +1827,6 @@ void StmtProfiler::<wbr>VisitObjCAvailability<br>
   VisitExpr(S);<br>
 }<br>
<br>
-void StmtProfiler::VisitDecl(const Decl *D) {<br>
-  ID.AddInteger(D? D->getKind() : 0);<br>
-<br>
-  if (Canonical && D) {<br>
-    if (const NonTypeTemplateParmDecl *NTTP =<br>
-          dyn_cast<<wbr>NonTypeTemplateParmDecl>(D)) {<br>
-      ID.AddInteger(NTTP->getDepth()<wbr>);<br>
-      ID.AddInteger(NTTP->getIndex()<wbr>);<br>
-      ID.AddBoolean(NTTP-><wbr>isParameterPack());<br>
-      VisitType(NTTP->getType());<br>
-      return;<br>
-    }<br>
-<br>
-    if (const ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D)) {<br>
-      // The Itanium C++ ABI uses the type, scope depth, and scope<br>
-      // index of a parameter when mangling expressions that involve<br>
-      // function parameters, so we will use the parameter's type for<br>
-      // establishing function parameter identity. That way, our<br>
-      // definition of "equivalent" (per C++ [temp.over.link]) is at<br>
-      // least as strong as the definition of "equivalent" used for<br>
-      // name mangling.<br>
-      VisitType(Parm->getType());<br>
-      ID.AddInteger(Parm-><wbr>getFunctionScopeDepth());<br>
-      ID.AddInteger(Parm-><wbr>getFunctionScopeIndex());<br>
-      return;<br>
-    }<br>
-<br>
-    if (const TemplateTypeParmDecl *TTP =<br>
-          dyn_cast<TemplateTypeParmDecl><wbr>(D)) {<br>
-      ID.AddInteger(TTP->getDepth())<wbr>;<br>
-      ID.AddInteger(TTP->getIndex())<wbr>;<br>
-      ID.AddBoolean(TTP-><wbr>isParameterPack());<br>
-      return;<br>
-    }<br>
-<br>
-    if (const TemplateTemplateParmDecl *TTP =<br>
-          dyn_cast<<wbr>TemplateTemplateParmDecl>(D)) {<br>
-      ID.AddInteger(TTP->getDepth())<wbr>;<br>
-      ID.AddInteger(TTP->getIndex())<wbr>;<br>
-      ID.AddBoolean(TTP-><wbr>isParameterPack());<br>
-      return;<br>
-    }<br>
-  }<br>
-<br>
-  ID.AddPointer(D? D->getCanonicalDecl() : nullptr);<br>
-}<br>
-<br>
-void StmtProfiler::VisitType(<wbr>QualType T) {<br>
-  if (Canonical)<br>
-    T = Context.getCanonicalType(T);<br>
-<br>
-  ID.AddPointer(T.<wbr>getAsOpaquePtr());<br>
-}<br>
-<br>
-void StmtProfiler::VisitName(<wbr>DeclarationName Name) {<br>
-  ID.AddPointer(Name.<wbr>getAsOpaquePtr());<br>
-}<br>
-<br>
-void StmtProfiler::<wbr>VisitNestedNameSpecifier(<wbr>NestedNameSpecifier *NNS) {<br>
-  if (Canonical)<br>
-    NNS = Context.<wbr>getCanonicalNestedNameSpecifie<wbr>r(NNS);<br>
-  ID.AddPointer(NNS);<br>
-}<br>
-<br>
-void StmtProfiler::<wbr>VisitTemplateName(TemplateName Name) {<br>
-  if (Canonical)<br>
-    Name = Context.<wbr>getCanonicalTemplateName(Name)<wbr>;<br>
-<br>
-  Name.Profile(ID);<br>
-}<br>
-<br>
 void StmtProfiler::<wbr>VisitTemplateArguments(const TemplateArgumentLoc *Args,<br>
                                           unsigned NumArgs) {<br>
   ID.AddInteger(NumArgs);<br>
@@ -1821,6 +1876,12 @@ void StmtProfiler::<wbr>VisitTemplateArgument<br>
<br>
 void Stmt::Profile(llvm::<wbr>FoldingSetNodeID &ID, const ASTContext &Context,<br>
                    bool Canonical) const {<br>
-  StmtProfiler Profiler(ID, Context, Canonical);<br>
+  StmtProfilerWithPointers Profiler(ID, Context, Canonical);<br>
+  Profiler.Visit(this);<br>
+}<br>
+<br>
+void Stmt::ProcessODRHash(llvm::<wbr>FoldingSetNodeID &ID,<br>
+                          class ODRHash &Hash) const {<br>
+  StmtProfilerWithoutPointers Profiler(ID, Hash);<br>
   Profiler.Visit(this);<br>
 }<br>
<br>
Modified: cfe/trunk/lib/Sema/SemaDecl.<wbr>cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=293585&r1=293584&r2=293585&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/lib/Sema/<wbr>SemaDecl.cpp?rev=293585&r1=<wbr>293584&r2=293585&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/lib/Sema/SemaDecl.<wbr>cpp (original)<br>
+++ cfe/trunk/lib/Sema/SemaDecl.<wbr>cpp Mon Jan 30 19:44:15 2017<br>
@@ -13705,8 +13705,11 @@ void Sema::<wbr>ActOnTagFinishDefinition(Scop<br>
       RD->completeDefinition();<br>
   }<br>
<br>
-  if (isa<CXXRecordDecl>(Tag))<br>
+  if (auto *RD = dyn_cast<CXXRecordDecl>(Tag)) {<br>
     FieldCollector->FinishClass();<br>
+    if (Context.getLangOpts().<wbr>Modules)<br>
+      RD->computeODRHash();<br>
+  }<br>
<br>
   // Exit this scope of this tag's definition.<br>
   PopDeclContext();<br>
<br>
Modified: cfe/trunk/lib/Serialization/<wbr>ASTReader.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReader.cpp?rev=293585&r1=293584&r2=293585&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/lib/<wbr>Serialization/ASTReader.cpp?<wbr>rev=293585&r1=293584&r2=<wbr>293585&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/lib/Serialization/<wbr>ASTReader.cpp (original)<br>
+++ cfe/trunk/lib/Serialization/<wbr>ASTReader.cpp Mon Jan 30 19:44:15 2017<br>
@@ -26,6 +26,7 @@<br>
 #include "clang/AST/Expr.h"<br>
 #include "clang/AST/ExprCXX.h"<br>
 #include "clang/AST/<wbr>NestedNameSpecifier.h"<br>
+#include "clang/AST/ODRHash.h"<br>
 #include "clang/AST/RawCommentList.h"<br>
 #include "clang/AST/Type.h"<br>
 #include "clang/AST/TypeLocVisitor.h"<br>
@@ -8884,21 +8885,638 @@ void ASTReader::<wbr>diagnoseOdrViolations()<br>
     for (auto *RD : Merge.second) {<br>
       // Multiple different declarations got merged together; tell the user<br>
       // where they came from.<br>
-      if (Merge.first != RD) {<br>
-        // FIXME: Walk the definition, figure out what's different,<br>
-        // and diagnose that.<br>
-        if (!Diagnosed) {<br>
-          std::string Module = getOwningModuleNameForDiagnost<wbr>ic(Merge.first);<br>
-          Diag(Merge.first->getLocation(<wbr>),<br>
-               diag::err_module_odr_<wbr>violation_different_<wbr>definitions)<br>
-            << Merge.first << Module.empty() << Module;<br>
-          Diagnosed = true;<br>
+      if (Merge.first == RD)<br>
+        continue;<br>
+<br>
+      llvm::SmallVector<std::pair<<wbr>Decl *, unsigned>, 4> FirstHashes;<br>
+      llvm::SmallVector<std::pair<<wbr>Decl *, unsigned>, 4> SecondHashes;<br>
+      ODRHash Hash;<br>
+      for (auto D : Merge.first->decls()) {<br>
+        if (D->isImplicit())<br>
+          continue;<br>
+        Hash.clear();<br>
+        Hash.AddDecl(D);<br>
+        FirstHashes.emplace_back(D, Hash.CalculateHash());<br>
+      }<br>
+      for (auto D : RD->decls()) {<br>
+        if (D->isImplicit())<br>
+          continue;<br>
+        Hash.clear();<br>
+        Hash.AddDecl(D);<br>
+        SecondHashes.emplace_back(D, Hash.CalculateHash());<br>
+      }<br>
+<br>
+      // Used with err_module_odr_violation_<wbr>mismatch_decl and<br>
+      // note_module_odr_violation_<wbr>mismatch_decl<br>
+      enum {<br>
+        EndOfClass,<br>
+        PublicSpecifer,<br>
+        PrivateSpecifer,<br>
+        ProtectedSpecifer,<br>
+        Friend,<br>
+        Enum,<br>
+        StaticAssert,<br>
+        Typedef,<br>
+        TypeAlias,<br>
+        CXXMethod,<br>
+        CXXConstructor,<br>
+        CXXDestructor,<br>
+        CXXConversion,<br>
+        Field,<br>
+        Other<br>
+      } FirstDiffType = Other,<br>
+        SecondDiffType = Other;<br>
+<br>
+      auto DifferenceSelector = [](Decl *D) {<br>
+        assert(D && "valid Decl required");<br>
+        switch (D->getKind()) {<br>
+        default:<br>
+          return Other;<br>
+        case Decl::AccessSpec:<br>
+          switch (D->getAccess()) {<br>
+          case AS_public:<br>
+            return PublicSpecifer;<br>
+          case AS_private:<br>
+            return PrivateSpecifer;<br>
+          case AS_protected:<br>
+            return ProtectedSpecifer;<br>
+          case AS_none:<br>
+            llvm_unreachable("Invalid access specifier");<br>
+          }<br>
+        case Decl::Friend:<br>
+          return Friend;<br>
+        case Decl::Enum:<br>
+          return Enum;<br>
+        case Decl::StaticAssert:<br>
+          return StaticAssert;<br>
+        case Decl::Typedef:<br>
+          return Typedef;<br>
+        case Decl::TypeAlias:<br>
+          return TypeAlias;<br>
+        case Decl::CXXMethod:<br>
+          return CXXMethod;<br>
+        case Decl::CXXConstructor:<br>
+          return CXXConstructor;<br>
+        case Decl::CXXDestructor:<br>
+          return CXXDestructor;<br>
+        case Decl::CXXConversion:<br>
+          return CXXConversion;<br>
+        case Decl::Field:<br>
+          return Field;<br>
         }<br>
+      };<br>
+      Decl *FirstDecl = nullptr;<br>
+      Decl *SecondDecl = nullptr;<br>
+      auto FirstIt = FirstHashes.begin();<br>
+      auto SecondIt = SecondHashes.begin();<br>
+<br>
+      // If there is a diagnoseable difference, FirstDiffType and<br>
+      // SecondDiffType will not be Other and FirstDecl and SecondDecl will be<br>
+      // filled in if not EndOfClass.<br>
+      while (FirstIt != FirstHashes.end() || SecondIt != SecondHashes.end()) {<br>
+        if (FirstIt->second == SecondIt->second) {<br>
+          ++FirstIt;<br>
+          ++SecondIt;<br>
+          continue;<br>
+        }<br>
+<br>
+        FirstDecl = FirstIt == FirstHashes.end() ? nullptr : FirstIt->first;<br>
+        SecondDecl = SecondIt == SecondHashes.end() ? nullptr : SecondIt->first;<br>
+<br>
+        FirstDiffType = FirstDecl ? DifferenceSelector(FirstDecl) : EndOfClass;<br>
+        SecondDiffType =<br>
+            SecondDecl ? DifferenceSelector(SecondDecl) : EndOfClass;<br>
+<br>
+        break;<br>
+      }<br>
+<br>
+      if (FirstDiffType == Other || SecondDiffType == Other) {<br>
+        // Reaching this point means an unexpected Decl was encountered<br>
+        // or no difference was detected.  This causes a generic error<br>
+        // message to be emitted.<br>
+        std::string Module = getOwningModuleNameForDiagnost<wbr>ic(Merge.first);<br>
+        Diag(Merge.first->getLocation(<wbr>),<br>
+             diag::err_module_odr_<wbr>violation_different_<wbr>definitions)<br>
+            << Merge.first << Module.empty() << Module;<br>
+<br>
+<br>
<br>
         Diag(RD->getLocation(),<br>
              diag::note_module_odr_<wbr>violation_different_<wbr>definitions)<br>
-          << getOwningModuleNameForDiagnost<wbr>ic(RD);<br>
+            << getOwningModuleNameForDiagnost<wbr>ic(RD);<br>
+        Diagnosed = true;<br>
+        break;<br>
+      }<br>
+<br>
+      std::string FirstModule = getOwningModuleNameForDiagnost<wbr>ic(Merge.first);<br>
+      std::string SecondModule = getOwningModuleNameForDiagnost<wbr>ic(RD);<br>
+<br>
+      if (FirstDiffType != SecondDiffType) {<br>
+        SourceLocation FirstLoc;<br>
+        SourceRange FirstRange;<br>
+        if (FirstDiffType == EndOfClass) {<br>
+          FirstLoc = Merge.first->getBraceRange().<wbr>getEnd();<br>
+        } else {<br>
+          FirstLoc = FirstIt->first->getLocation();<br>
+          FirstRange = FirstIt->first-><wbr>getSourceRange();<br>
+        }<br>
+        Diag(FirstLoc, diag::err_module_odr_<wbr>violation_mismatch_decl)<br>
+            << Merge.first << FirstModule.empty() << FirstModule << FirstRange<br>
+            << FirstDiffType;<br>
+<br>
+        SourceLocation SecondLoc;<br>
+        SourceRange SecondRange;<br>
+        if (SecondDiffType == EndOfClass) {<br>
+          SecondLoc = RD->getBraceRange().getEnd();<br>
+        } else {<br>
+          SecondLoc = SecondDecl->getLocation();<br>
+          SecondRange = SecondDecl->getSourceRange();<br>
+        }<br>
+        Diag(SecondLoc, diag::note_module_odr_<wbr>violation_mismatch_decl)<br>
+            << SecondModule << SecondRange << SecondDiffType;<br>
+        Diagnosed = true;<br>
+        break;<br>
+      }<br>
+<br>
+      // Used with err_module_odr_violation_<wbr>mismatch_decl_diff and<br>
+      // note_module_odr_violation_<wbr>mismatch_decl_diff<br>
+      enum ODRDeclDifference{<br>
+        FriendName,<br>
+        EnumName,<br>
+        EnumConstantName,<br>
+        EnumConstantInit,<br>
+        EnumConstantNoInit,<br>
+        EnumConstantDiffInit,<br>
+        EnumNumberOfConstants,<br>
+        StaticAssertCondition,<br>
+        StaticAssertMessage,<br>
+        StaticAssertOnlyMessage,<br>
+        TypedefName,<br>
+        MethodName,<br>
+        MethodStatic,<br>
+        MethodInline,<br>
+        MethodConst,<br>
+        MethodNumParams,<br>
+        MethodParamName,<br>
+        MethodParamType,<br>
+        MethodDefaultArg,<br>
+        MethodOnlyDefaultArg,<br>
+        MethodOnlyBody,<br>
+        MethodBody,<br>
+        FieldName,<br>
+        FieldSingleBitField,<br>
+        FieldMutable,<br>
+      };<br>
+<br>
+      // These lambdas have the common portions of the ODR diagnostics.  This<br>
+      // has the same return as Diag(), so addition parameters can be passed<br>
+      // in with operator<<<br>
+      auto ODRDiagError = [&Merge, &FirstModule, this](<br>
+          SourceLocation Loc, SourceRange Range, ODRDeclDifference DiffType) {<br>
+        return Diag(Loc, diag::err_module_odr_<wbr>violation_mismatch_decl_diff)<br>
+               << Merge.first << FirstModule.empty() << FirstModule << Range<br>
+               << DiffType;<br>
+      };<br>
+      auto ODRDiagNote = [&SecondModule, this](<br>
+          SourceLocation Loc, SourceRange Range, ODRDeclDifference DiffType) {<br>
+        return Diag(Loc, diag::note_module_odr_<wbr>violation_mismatch_decl_diff)<br>
+               << SecondModule << Range << DiffType;<br>
+      };<br>
+<br>
+      auto ComputeODRHash = [&Hash](const Stmt* S) {<br>
+        assert(S);<br>
+        Hash.clear();<br>
+        Hash.AddStmt(S);<br>
+        return Hash.CalculateHash();<br>
+      };<br>
+<br>
+      // At this point, both decls are of the same type.  Dive down deeper into<br>
+      // the Decl to determine where the first difference is located.<br>
+      switch (FirstDiffType) {<br>
+      case Friend: {<br>
+        FriendDecl *FirstFriend = cast<FriendDecl>(FirstDecl);<br>
+        FriendDecl *SecondFriend = cast<FriendDecl>(SecondDecl);<br>
+        {<br>
+          auto D = ODRDiagError(FirstFriend-><wbr>getFriendLoc(),<br>
+                                FirstFriend->getSourceRange(), FriendName);<br>
+          if (TypeSourceInfo *FirstTSI = FirstFriend->getFriendType())<br>
+            D << FirstTSI->getType();<br>
+          else<br>
+            D << FirstFriend->getFriendDecl();<br>
+        }<br>
+        {<br>
+          auto D = ODRDiagNote(SecondFriend-><wbr>getFriendLoc(),<br>
+                               SecondFriend->getSourceRange()<wbr>, FriendName);<br>
+          if (TypeSourceInfo *SecondTSI = SecondFriend->getFriendType())<br>
+            D << SecondTSI->getType();<br>
+          else<br>
+            D << SecondFriend->getFriendDecl();<br>
+        }<br>
+        Diagnosed = true;<br>
+        break;<br>
+      }<br>
+      case Enum: {<br>
+        EnumDecl *FirstEnum = cast<EnumDecl>(FirstDecl);<br>
+        EnumDecl *SecondEnum = cast<EnumDecl>(SecondDecl);<br>
+        if (FirstEnum->getName() != SecondEnum->getName()) {<br>
+          ODRDiagError(FirstEnum-><wbr>getLocStart(), FirstEnum->getSourceRange(),<br>
+                       EnumName)<br>
+              << FirstEnum;<br>
+          ODRDiagNote(SecondEnum-><wbr>getLocStart(), SecondEnum->getSourceRange(),<br>
+                      EnumName)<br>
+              << SecondEnum;<br>
+          Diagnosed = true;<br>
+          break;<br>
+        }<br>
+<br>
+        // Don't use EnumDecl::enumerator_{begin,<wbr>end}.  Decl merging can<br>
+        // cause the iterators from them to be the same for both Decl's.<br>
+        EnumDecl::enumerator_iterator FirstEnumIt(FirstEnum->decls_<wbr>begin());<br>
+        EnumDecl::enumerator_iterator FirstEnumEnd(FirstEnum->decls_<wbr>end());<br>
+        EnumDecl::enumerator_iterator SecondEnumIt(SecondEnum-><wbr>decls_begin());<br>
+        EnumDecl::enumerator_iterator SecondEnumEnd(SecondEnum-><wbr>decls_end());<br>
+        int NumElements = 0;<br>
+        for (; FirstEnumIt != FirstEnumEnd && SecondEnumIt != SecondEnumEnd;<br>
+             ++FirstEnumIt, ++SecondEnumIt, ++NumElements) {<br>
+          if (FirstEnumIt->getName() != SecondEnumIt->getName()) {<br>
+            ODRDiagError(FirstEnumIt-><wbr>getLocStart(),<br>
+                         FirstEnumIt->getSourceRange(), EnumConstantName)<br>
+                << *FirstEnumIt << FirstEnum;<br>
+            ODRDiagNote(SecondEnumIt-><wbr>getLocStart(),<br>
+                        SecondEnumIt->getSourceRange()<wbr>, EnumConstantName)<br>
+                << *SecondEnumIt << SecondEnum;<br>
+            Diagnosed = true;<br>
+            break;<br>
+          }<br>
+          Expr *FirstInit = FirstEnumIt->getInitExpr();<br>
+          Expr *SecondInit = SecondEnumIt->getInitExpr();<br>
+<br>
+          if (FirstInit && !SecondInit) {<br>
+            ODRDiagError(FirstEnumIt-><wbr>getLocStart(),<br>
+                         FirstEnumIt->getSourceRange(), EnumConstantInit)<br>
+                << *FirstEnumIt << FirstEnum;<br>
+<br>
+            ODRDiagNote(SecondEnumIt-><wbr>getLocStart(),<br>
+                        SecondEnumIt->getSourceRange()<wbr>, EnumConstantNoInit)<br>
+                << *SecondEnumIt << SecondEnum;<br>
+            Diagnosed = true;<br>
+            break;<br>
+          }<br>
+<br>
+          if (!FirstInit && SecondInit) {<br>
+            ODRDiagError(FirstEnumIt-><wbr>getLocStart(),<br>
+                         FirstEnumIt->getSourceRange(), EnumConstantNoInit)<br>
+                << *FirstEnumIt << FirstEnum;<br>
+            ODRDiagNote(SecondEnumIt-><wbr>getLocStart(),<br>
+                        SecondEnumIt->getSourceRange()<wbr>, EnumConstantInit)<br>
+                << *SecondEnumIt << SecondEnum;<br>
+            Diagnosed = true;<br>
+            break;<br>
+          }<br>
+<br>
+          if (FirstInit == SecondInit)<br>
+            continue;<br>
+<br>
+          unsigned FirstODRHash = ComputeODRHash(FirstInit);<br>
+          unsigned SecondODRHash = ComputeODRHash(SecondInit);<br>
+<br>
+          if (FirstODRHash != SecondODRHash) {<br>
+            ODRDiagError(FirstEnumIt-><wbr>getLocStart(),<br>
+                         FirstEnumIt->getSourceRange(), EnumConstantDiffInit)<br>
+                << *FirstEnumIt << FirstEnum;<br>
+            ODRDiagNote(SecondEnumIt-><wbr>getLocStart(),<br>
+                        SecondEnumIt->getSourceRange()<wbr>, EnumConstantDiffInit)<br>
+                << *SecondEnumIt << SecondEnum;<br>
+            Diagnosed = true;<br>
+            break;<br>
+          }<br>
+        }<br>
+<br>
+        if (FirstEnumIt == FirstEnumEnd && SecondEnumIt != SecondEnumEnd) {<br>
+          unsigned FirstEnumSize = NumElements;<br>
+          unsigned SecondEnumSize = NumElements;<br>
+          for (; SecondEnumIt != SecondEnumEnd; ++SecondEnumIt)<br>
+            ++SecondEnumSize;<br>
+          ODRDiagError(FirstEnum-><wbr>getLocStart(), FirstEnum->getSourceRange(),<br>
+                       EnumNumberOfConstants)<br>
+              << FirstEnum << FirstEnumSize;<br>
+          ODRDiagNote(SecondEnum-><wbr>getLocStart(), SecondEnum->getSourceRange(),<br>
+                      EnumNumberOfConstants)<br>
+              << SecondEnum << SecondEnumSize;<br>
+          Diagnosed = true;<br>
+          break;<br>
+        }<br>
+<br>
+        if (FirstEnumIt != FirstEnumEnd && SecondEnumIt == SecondEnumEnd) {<br>
+          unsigned FirstEnumSize = NumElements;<br>
+          unsigned SecondEnumSize = NumElements;<br>
+          for (; FirstEnumIt != FirstEnumEnd; ++FirstEnumIt)<br>
+            ++FirstEnumSize;<br>
+          ODRDiagError(FirstEnum-><wbr>getLocStart(), FirstEnum->getSourceRange(),<br>
+                       EnumNumberOfConstants)<br>
+              << FirstEnum << FirstEnumSize;<br>
+          ODRDiagNote(SecondEnum-><wbr>getLocStart(), SecondEnum->getSourceRange(),<br>
+                      EnumNumberOfConstants)<br>
+              << SecondEnum << SecondEnumSize;<br>
+          Diagnosed = true;<br>
+          break;<br>
+        }<br>
+<br>
+        break;<br>
+      }<br>
+      case StaticAssert: {<br>
+        StaticAssertDecl *FirstSA = cast<StaticAssertDecl>(<wbr>FirstDecl);<br>
+        StaticAssertDecl *SecondSA = cast<StaticAssertDecl>(<wbr>SecondDecl);<br>
+<br>
+        Expr *FirstExpr = FirstSA->getAssertExpr();<br>
+        Expr *SecondExpr = SecondSA->getAssertExpr();<br>
+        unsigned FirstODRHash = ComputeODRHash(FirstExpr);<br>
+        unsigned SecondODRHash = ComputeODRHash(SecondExpr);<br>
+        if (FirstODRHash != SecondODRHash) {<br>
+          ODRDiagError(FirstExpr-><wbr>getLocStart(), FirstExpr->getSourceRange(),<br>
+                       StaticAssertCondition);<br>
+          ODRDiagNote(SecondExpr-><wbr>getLocStart(),<br>
+                      SecondExpr->getSourceRange(), StaticAssertCondition);<br>
+          Diagnosed = true;<br>
+          break;<br>
+        }<br>
+<br>
+        StringLiteral *FirstStr = FirstSA->getMessage();<br>
+        StringLiteral *SecondStr = SecondSA->getMessage();<br>
+        if ((FirstStr && !SecondStr) || (!FirstStr && SecondStr)) {<br>
+          SourceLocation FirstLoc, SecondLoc;<br>
+          SourceRange FirstRange, SecondRange;<br>
+          if (FirstStr) {<br>
+            FirstLoc = FirstStr->getLocStart();<br>
+            FirstRange = FirstStr->getSourceRange();<br>
+          } else {<br>
+            FirstLoc = FirstSA->getLocStart();<br>
+            FirstRange = FirstSA->getSourceRange();<br>
+          }<br>
+          if (SecondStr) {<br>
+            SecondLoc = SecondStr->getLocStart();<br>
+            SecondRange = SecondStr->getSourceRange();<br>
+          } else {<br>
+            SecondLoc = SecondSA->getLocStart();<br>
+            SecondRange = SecondSA->getSourceRange();<br>
+          }<br>
+          ODRDiagError(FirstLoc, FirstRange, StaticAssertOnlyMessage)<br>
+              << (FirstStr == nullptr);<br>
+          ODRDiagNote(SecondLoc, SecondRange, StaticAssertOnlyMessage)<br>
+              << (SecondStr == nullptr);<br>
+          Diagnosed = true;<br>
+          break;<br>
+        }<br>
+<br>
+        if (FirstStr && SecondStr &&<br>
+            FirstStr->getString() != SecondStr->getString()) {<br>
+          ODRDiagError(FirstStr-><wbr>getLocStart(), FirstStr->getSourceRange(),<br>
+                       StaticAssertMessage);<br>
+          ODRDiagNote(SecondStr-><wbr>getLocStart(), SecondStr->getSourceRange(),<br>
+                      StaticAssertMessage);<br>
+          Diagnosed = true;<br>
+          break;<br>
+        }<br>
+        break;<br>
+      }<br>
+      case Typedef:<br>
+      case TypeAlias: {<br>
+        TypedefNameDecl *FirstTD = cast<TypedefNameDecl>(<wbr>FirstDecl);<br>
+        TypedefNameDecl *SecondTD = cast<TypedefNameDecl>(<wbr>SecondDecl);<br>
+        IdentifierInfo *FirstII = FirstTD->getIdentifier();<br>
+        IdentifierInfo *SecondII = SecondTD->getIdentifier();<br>
+        if (FirstII && SecondII && FirstII->getName() != SecondII->getName()) {<br>
+          ODRDiagError(FirstTD-><wbr>getLocation(), FirstTD->getSourceRange(),<br>
+                       TypedefName)<br>
+              << (FirstDiffType == TypeAlias) << FirstII;<br>
+          ODRDiagNote(SecondTD-><wbr>getLocation(), SecondTD->getSourceRange(),<br>
+                      TypedefName)<br>
+              << (FirstDiffType == TypeAlias) << SecondII;<br>
+          Diagnosed = true;<br>
+          break;<br>
+        }<br>
+        break;<br>
+      }<br>
+      case CXXMethod:<br>
+      case CXXConstructor:<br>
+      case CXXConversion:<br>
+      case CXXDestructor: {<br>
+        // TODO: Merge with existing method diff logic.<br>
+        CXXMethodDecl *FirstMD = cast<CXXMethodDecl>(FirstDecl)<wbr>;<br>
+        CXXMethodDecl *SecondMD = cast<CXXMethodDecl>(<wbr>SecondDecl);<br>
+        IdentifierInfo *FirstII = FirstMD->getIdentifier();<br>
+        IdentifierInfo *SecondII = SecondMD->getIdentifier();<br>
+        if (FirstII && SecondII && FirstII->getName() != SecondII->getName()) {<br>
+          ODRDiagError(FirstMD-><wbr>getLocation(), FirstMD->getSourceRange(),<br>
+                       MethodName)<br>
+              << FirstII;<br>
+          ODRDiagNote(SecondMD-><wbr>getLocation(), SecondMD->getSourceRange(),<br>
+                      MethodName)<br>
+              << SecondII;<br>
+          Diagnosed = true;<br>
+          break;<br>
+        }<br>
+<br>
+        bool FirstStatic = FirstMD->getStorageClass() == SC_Static;<br>
+        bool SecondStatic = SecondMD->getStorageClass() == SC_Static;<br>
+        if (FirstStatic != SecondStatic) {<br>
+          ODRDiagError(FirstMD-><wbr>getLocation(), FirstMD->getSourceRange(),<br>
+                       MethodStatic)<br>
+              << FirstMD << FirstStatic;<br>
+          ODRDiagNote(SecondMD-><wbr>getLocation(), SecondMD->getSourceRange(),<br>
+                      MethodStatic)<br>
+              << SecondMD << SecondStatic;<br>
+          Diagnosed = true;<br>
+          break;<br>
+        }<br>
+<br>
+        bool FirstInline = FirstMD->isInlineSpecified();<br>
+        bool SecondInline = SecondMD->isInlineSpecified();<br>
+        if (FirstInline != SecondInline) {<br>
+          ODRDiagError(FirstMD-><wbr>getLocation(), FirstMD->getSourceRange(),<br>
+                       MethodInline)<br>
+              << FirstMD << FirstInline;<br>
+          ODRDiagNote(SecondMD-><wbr>getLocation(), SecondMD->getSourceRange(),<br>
+                      MethodInline)<br>
+              << SecondMD << SecondInline;<br>
+          Diagnosed = true;<br>
+          break;<br>
+        }<br>
+<br>
+        bool FirstConst = FirstMD->isConst();<br>
+        bool SecondConst = SecondMD->isConst();<br>
+        if (FirstConst != SecondConst) {<br>
+          ODRDiagError(FirstMD-><wbr>getLocation(), FirstMD->getSourceRange(),<br>
+                       MethodConst)<br>
+              << FirstMD << FirstInline;<br>
+          ODRDiagNote(SecondMD-><wbr>getLocation(), SecondMD->getSourceRange(),<br>
+                      MethodConst)<br>
+              << SecondMD << SecondInline;<br>
+          Diagnosed = true;<br>
+          break;<br>
+        }<br>
+<br>
+        if (FirstMD->getNumParams() != SecondMD->getNumParams()) {<br>
+          ODRDiagError(FirstMD-><wbr>getLocation(), FirstMD->getSourceRange(),<br>
+                       MethodNumParams)<br>
+              << SecondMD << FirstMD->getNumParams();<br>
+          ODRDiagNote(SecondMD-><wbr>getLocation(), SecondMD->getSourceRange(),<br>
+                      MethodNumParams)<br>
+              << SecondMD << SecondMD->getNumParams();<br>
+          Diagnosed = true;<br>
+          break;<br>
+        }<br>
+<br>
+        for (unsigned i = 0, e = FirstMD->getNumParams(); i < e; ++i) {<br>
+          ParmVarDecl *FirstParam = FirstMD->getParamDecl(i);<br>
+          ParmVarDecl *SecondParam = SecondMD->getParamDecl(i);<br>
+          IdentifierInfo *FirstII = FirstParam->getIdentifier();<br>
+          IdentifierInfo *SecondII = SecondParam->getIdentifier();<br>
+          if ((!FirstII && SecondII) || (FirstII && !SecondII) ||<br>
+              (FirstII && SecondII &&<br>
+               FirstII->getName() != SecondII->getName())) {<br>
+            ODRDiagError(FirstParam-><wbr>getLocation(),<br>
+                         FirstParam->getSourceRange(), MethodParamName)<br>
+                << SecondMD << i + 1 << (FirstII == nullptr) << FirstII;<br>
+            ODRDiagNote(SecondParam-><wbr>getLocation(),<br>
+                        SecondParam->getSourceRange(), MethodParamName)<br>
+                << SecondMD << i + 1 << (SecondII == nullptr) << SecondII;<br>
+            Diagnosed = true;<br>
+            break;<br>
+          }<br>
+<br>
+          if (FirstParam->getType() != SecondParam->getType()) {<br>
+            ODRDiagError(FirstParam-><wbr>getLocation(),<br>
+                         FirstParam->getSourceRange(), MethodParamType)<br>
+                << SecondMD << i + 1 << FirstParam->getType();<br>
+            ODRDiagNote(SecondParam-><wbr>getLocation(),<br>
+                        SecondParam->getSourceRange(), MethodParamType)<br>
+                << SecondMD << i + 1 << SecondParam->getType();<br>
+            Diagnosed = true;<br>
+            break;<br>
+          }<br>
+<br>
+          Expr *FirstDefaultArg = FirstParam->getDefaultArg();<br>
+          Expr *SecondDefaultArg = SecondParam->getDefaultArg();<br>
+          if ((!FirstDefaultArg && SecondDefaultArg) ||<br>
+              (FirstDefaultArg && !SecondDefaultArg)) {<br>
+            ODRDiagError(FirstParam-><wbr>getLocation(),<br>
+                         FirstParam->getSourceRange(), MethodOnlyDefaultArg)<br>
+                << SecondMD << i + 1 << (FirstDefaultArg != nullptr);<br>
+            ODRDiagNote(SecondParam-><wbr>getLocation(),<br>
+                        SecondParam->getSourceRange(), MethodOnlyDefaultArg)<br>
+                << SecondMD << i + 1 << (SecondDefaultArg != nullptr);<br>
+            Diagnosed = true;<br>
+            break;<br>
+          }<br>
+<br>
+          if (FirstDefaultArg && SecondDefaultArg) {<br>
+            unsigned FirstODRHash = ComputeODRHash(<wbr>FirstDefaultArg);<br>
+            unsigned SecondODRHash = ComputeODRHash(<wbr>SecondDefaultArg);<br>
+            if (FirstODRHash != SecondODRHash) {<br>
+              ODRDiagError(FirstParam-><wbr>getLocation(),<br>
+                           FirstParam->getSourceRange(), MethodDefaultArg)<br>
+                  << SecondMD << i + 1;<br>
+              ODRDiagNote(SecondParam-><wbr>getLocation(),<br>
+                          SecondParam->getSourceRange(), MethodDefaultArg)<br>
+                  << SecondMD << i + 1;<br>
+              Diagnosed = true;<br>
+              break;<br>
+            }<br>
+          }<br>
+        }<br>
+<br>
+        // TODO: Figure out how to diagnose different function bodies.<br>
+        // Deserialization does not import the second function body.<br>
+<br>
+        break;<br>
       }<br>
+      case Field: {<br>
+        // TODO: Merge with exising field diff logic.<br>
+        FieldDecl *FirstField = cast<FieldDecl>(FirstDecl);<br>
+        FieldDecl *SecondField = cast<FieldDecl>(SecondDecl);<br>
+        IdentifierInfo *FirstII = FirstField->getIdentifier();<br>
+        IdentifierInfo *SecondII = SecondField->getIdentifier();<br>
+        if (FirstII->getName() != SecondII->getName()) {<br>
+          ODRDiagError(FirstField-><wbr>getLocation(), FirstField->getSourceRange(),<br>
+                       FieldName)<br>
+              << FirstII;<br>
+          ODRDiagNote(SecondField-><wbr>getLocation(), SecondField->getSourceRange(),<br>
+                      FieldName)<br>
+              << SecondII;<br>
+          Diagnosed = true;<br>
+          break;<br>
+        }<br>
+<br>
+        // This case is handled elsewhere.<br>
+        if (FirstField->getType() != SecondField->getType()) {<br>
+          break;<br>
+        }<br>
+<br>
+        bool FirstBitField = FirstField->isBitField();<br>
+        bool SecondBitField = SecondField->isBitField();<br>
+        if (FirstBitField != SecondBitField) {<br>
+          ODRDiagError(FirstField-><wbr>getLocation(), FirstField->getSourceRange(),<br>
+                       FieldSingleBitField)<br>
+              << FirstII << FirstBitField;<br>
+          ODRDiagNote(SecondField-><wbr>getLocation(), SecondField->getSourceRange(),<br>
+                      FieldSingleBitField)<br>
+              << SecondII << SecondBitField;<br>
+          Diagnosed = true;<br>
+          break;<br>
+        }<br>
+<br>
+        if (FirstBitField && SecondBitField) {<br>
+          Expr* FirstWidth = FirstField->getBitWidth();<br>
+          Expr *SecondWidth = SecondField->getBitWidth();<br>
+          unsigned FirstODRHash = ComputeODRHash(FirstWidth);<br>
+          unsigned SecondODRHash = ComputeODRHash(SecondWidth);<br>
+          if (FirstODRHash != SecondODRHash) {<br>
+            ODRDiagError(FirstField-><wbr>getLocation(),<br>
+                         FirstField->getSourceRange(), FieldSingleBitField)<br>
+                << FirstII << FirstBitField;<br>
+            ODRDiagNote(SecondField-><wbr>getLocation(),<br>
+                        SecondField->getSourceRange(), FieldSingleBitField)<br>
+                << SecondII << SecondBitField;<br>
+            Diagnosed = true;<br>
+            break;<br>
+          }<br>
+        }<br>
+<br>
+        bool FirstMutable = FirstField->isMutable();<br>
+        bool SecondMutable = SecondField->isMutable();<br>
+        if (FirstMutable != SecondMutable) {<br>
+          ODRDiagError(FirstField-><wbr>getLocation(), FirstField->getSourceRange(),<br>
+                       FieldMutable)<br>
+              << FirstII << FirstMutable;<br>
+          ODRDiagNote(SecondField-><wbr>getLocation(), SecondField->getSourceRange(),<br>
+                      FieldMutable)<br>
+              << SecondII << SecondMutable;<br>
+          Diagnosed = true;<br>
+          break;<br>
+        }<br>
+        break;<br>
+      }<br>
+      case Other:<br>
+      case EndOfClass:<br>
+      case PublicSpecifer:<br>
+      case PrivateSpecifer:<br>
+      case ProtectedSpecifer:<br>
+        llvm_unreachable("Invalid diff type");<br>
+      }<br>
+<br>
+      if (Diagnosed == true)<br>
+        continue;<br>
+<br>
+      // Unable to find difference in Decl's, print simple different<br>
+      // definitions diagnostic.<br>
+      Diag(Merge.first->getLocation(<wbr>),<br>
+           diag::err_module_odr_<wbr>violation_different_<wbr>definitions)<br>
+          << Merge.first << FirstModule.empty() << FirstModule;<br>
+      Diag(RD->getLocation(),<br>
+           diag::note_module_odr_<wbr>violation_different_<wbr>definitions)<br>
+          << SecondModule;<br>
+      Diagnosed = true;<br>
     }<br>
<br>
     if (!Diagnosed) {<br>
<br>
Modified: cfe/trunk/lib/Serialization/<wbr>ASTReaderDecl.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=293585&r1=293584&r2=293585&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/lib/<wbr>Serialization/ASTReaderDecl.<wbr>cpp?rev=293585&r1=293584&r2=<wbr>293585&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/lib/Serialization/<wbr>ASTReaderDecl.cpp (original)<br>
+++ cfe/trunk/lib/Serialization/<wbr>ASTReaderDecl.cpp Mon Jan 30 19:44:15 2017<br>
@@ -1521,6 +1521,7 @@ void ASTDeclReader::<wbr>ReadCXXDefinitionDat<br>
   Data.<wbr>ImplicitCopyAssignmentHasConst<wbr>Param = Record.readInt();<br>
   Data.<wbr>HasDeclaredCopyConstructorWith<wbr>ConstParam = Record.readInt();<br>
   Data.<wbr>HasDeclaredCopyAssignmentWithC<wbr>onstParam = Record.readInt();<br>
+  Data.ODRHash = Record.readInt();<br>
<br>
   Data.NumBases = Record.readInt();<br>
   if (Data.NumBases)<br>
@@ -1651,6 +1652,7 @@ void ASTDeclReader::<wbr>MergeDefinitionData(<br>
   OR_FIELD(<wbr>HasDeclaredCopyConstructorWith<wbr>ConstParam)<br>
   OR_FIELD(<wbr>HasDeclaredCopyAssignmentWithC<wbr>onstParam)<br>
   MATCH_FIELD(IsLambda)<br>
+  MATCH_FIELD(ODRHash)<br>
 #undef OR_FIELD<br>
 #undef MATCH_FIELD<br>
<br>
<br>
Modified: cfe/trunk/lib/Serialization/<wbr>ASTWriter.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriter.cpp?rev=293585&r1=293584&r2=293585&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/lib/<wbr>Serialization/ASTWriter.cpp?<wbr>rev=293585&r1=293584&r2=<wbr>293585&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/lib/Serialization/<wbr>ASTWriter.cpp (original)<br>
+++ cfe/trunk/lib/Serialization/<wbr>ASTWriter.cpp Mon Jan 30 19:44:15 2017<br>
@@ -5699,6 +5699,7 @@ void ASTRecordWriter::<wbr>AddCXXDefinitionDa<br>
   Record->push_back(Data.<wbr>ImplicitCopyAssignmentHasConst<wbr>Param);<br>
   Record->push_back(Data.<wbr>HasDeclaredCopyConstructorWith<wbr>ConstParam);<br>
   Record->push_back(Data.<wbr>HasDeclaredCopyAssignmentWithC<wbr>onstParam);<br>
+  Record->push_back(Data.<wbr>ODRHash);<br>
   // IsLambda bit is already saved.<br>
<br>
   Record->push_back(Data.<wbr>NumBases);<br>
<br>
Modified: cfe/trunk/test/Modules/merge-<wbr>using-decls.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/merge-using-decls.cpp?rev=293585&r1=293584&r2=293585&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/test/<wbr>Modules/merge-using-decls.cpp?<wbr>rev=293585&r1=293584&r2=<wbr>293585&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/test/Modules/merge-<wbr>using-decls.cpp (original)<br>
+++ cfe/trunk/test/Modules/merge-<wbr>using-decls.cpp Mon Jan 30 19:44:15 2017<br>
@@ -37,6 +37,10 @@ template int UseAll<Y>();<br>
 // Here, we're instantiating the definition from 'A' and merging the definition<br>
 // from 'B' into it.<br>
<br>
+// expected-error@b.h:* {{'D::type' from module 'B' is not present in definition of 'D<T>' in module 'A'}}<br>
+// expected-error@b.h:* {{'D::value' from module 'B' is not present in definition of 'D<T>' in module 'A'}}<br>
+<br>
+<br>
 // expected-error@b.h:* {{'E::value' from module 'B' is not present in definition of 'E<T>' in module 'A'}}<br>
 // expected-error@b.h:* {{'E::v' from module 'B' is not present in definition of 'E<T>' in module 'A'}}<br>
<br>
<br>
Added: cfe/trunk/test/Modules/odr_<wbr>hash.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/odr_hash.cpp?rev=293585&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/test/<wbr>Modules/odr_hash.cpp?rev=<wbr>293585&view=auto</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/test/Modules/odr_<wbr>hash.cpp (added)<br>
+++ cfe/trunk/test/Modules/odr_<wbr>hash.cpp Mon Jan 30 19:44:15 2017<br>
@@ -0,0 +1,1077 @@<br>
+// Clear and create directories<br>
+// RUN: rm -rf %t<br>
+// RUN: mkdir %t<br>
+// RUN: mkdir %t/cache<br>
+// RUN: mkdir %t/Inputs<br>
+<br>
+// Build first header file<br>
+// RUN: echo "#define FIRST" >> %t/Inputs/first.h<br>
+// RUN: cat %s               >> %t/Inputs/first.h<br>
+<br>
+// Build second header file<br>
+// RUN: echo "#define SECOND" >> %t/Inputs/second.h<br>
+// RUN: cat %s                >> %t/Inputs/second.h<br>
+<br>
+// Build module map file<br>
+// RUN: echo "module first {"           >> %t/Inputs/module.map<br>
+// RUN: echo "    header \"first.h\""   >> %t/Inputs/module.map<br>
+// RUN: echo "}"                        >> %t/Inputs/module.map<br>
+// RUN: echo "module second {"          >> %t/Inputs/module.map<br>
+// RUN: echo "    header \"second.h\""  >> %t/Inputs/module.map<br>
+// RUN: echo "}"                        >> %t/Inputs/module.map<br>
+<br>
+// Run test<br>
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache -x c++ -I%t/Inputs -verify %s -std=c++11<br>
+<br>
+#if !defined(FIRST) && !defined(SECOND)<br>
+#include "first.h"<br>
+#include "second.h"<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S1 {<br>
+  public:<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S1 {<br>
+  private:<br>
+};<br>
+#else<br>
+S1 s1;<br>
+// expected-error@first.h:* {{'S1' has different definitions in different modules; first difference is definition in module 'first' found public access specifier}}<br>
+// expected-note@second.h:* {{but in 'second' found private access specifier}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S2Friend2 {};<br>
+struct S2 {<br>
+  friend S2Friend2;<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S2Friend1 {};<br>
+struct S2 {<br>
+  friend S2Friend1;<br>
+};<br>
+#else<br>
+S2 s2;<br>
+// expected-error@first.h:* {{'S2' has different definitions in different modules; first difference is definition in module 'first' found friend 'S2Friend2'}}<br>
+// expected-note@second.h:* {{but in 'second' found other friend 'S2Friend1'}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+template<class><br>
+struct S3Template {};<br>
+struct S3 {<br>
+  friend S3Template<int>;<br>
+};<br>
+#elif defined(SECOND)<br>
+template<class><br>
+struct S3Template {};<br>
+struct S3 {<br>
+  friend S3Template<double>;<br>
+};<br>
+#else<br>
+S3 s3;<br>
+// expected-error@first.h:* {{'S3' has different definitions in different modules; first difference is definition in module 'first' found friend 'S3Template<int>'}}<br>
+// expected-note@second.h:* {{but in 'second' found other friend 'S3Template<double>'}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S4 {<br>
+  static_assert(1 == 1, "First");<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S4 {<br>
+  static_assert(1 == 1, "Second");<br>
+};<br>
+#else<br>
+S4 s4;<br>
+// expected-error@first.h:* {{'S4' has different definitions in different modules; first difference is definition in module 'first' found static assert with message}}<br>
+// expected-note@second.h:* {{but in 'second' found static assert with different message}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S5 {<br>
+  static_assert(1 == 1, "Message");<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S5 {<br>
+  static_assert(2 == 2, "Message");<br>
+};<br>
+#else<br>
+S5 s5;<br>
+// expected-error@first.h:* {{'S5' has different definitions in different modules; first difference is definition in module 'first' found static assert with condition}}<br>
+// expected-note@second.h:* {{but in 'second' found static assert with different condition}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S6 {<br>
+  int First();<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S6 {<br>
+  int Second();<br>
+};<br>
+#else<br>
+S6 s6;<br>
+// expected-error@second.h:* {{'S6::Second' from module 'second' is not present in definition of 'S6' in module 'first'}}<br>
+// expected-note@first.h:* {{definition has no member 'Second'}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S7 {<br>
+  double foo();<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S7 {<br>
+  int foo();<br>
+};<br>
+#else<br>
+S7 s7;<br>
+// expected-error@second.h:* {{'S7::foo' from module 'second' is not present in definition of 'S7' in module 'first'}}<br>
+// expected-note@first.h:* {{declaration of 'foo' does not match}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S8 {<br>
+  void foo();<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S8 {<br>
+  void foo() {}<br>
+};<br>
+#else<br>
+S8 s8;<br>
+// expected-error@first.h:* {{'S8' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S9 {<br>
+  void foo() { int y = 5; }<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S9 {<br>
+  void foo() { int x = 5; }<br>
+};<br>
+#else<br>
+S9 s9;<br>
+// expected-error@first.h:* {{'S9' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S10 {<br>
+  struct {<br>
+    int x;<br>
+  } a;<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S10 {<br>
+  struct {<br>
+    int x;<br>
+    int y;<br>
+  } a;<br>
+};<br>
+#else<br>
+S10 s10;<br>
+// 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-<wbr>9]*}})' in module 'first'}}<br>
+// expected-note@first.h:* {{definition has no member 'y'}}<br>
+<br>
+// expected-error@first.h:* {{'S10' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S11 {<br>
+  void foo() { int y = sizeof(int); }<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S11 {<br>
+  void foo() { int y = sizeof(double); }<br>
+};<br>
+#else<br>
+S11 s11;<br>
+// expected-error@first.h:* {{'S11' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S12 {<br>
+  int x = sizeof(x);<br>
+  int y = sizeof(x);<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S12 {<br>
+  int x = sizeof(x);<br>
+  int y = sizeof(y);<br>
+};<br>
+#else<br>
+S12 s12;<br>
+// expected-error@first.h:* {{'S12' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S13 {<br>
+  template <typename A> void foo();<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S13 {<br>
+  template <typename B> void foo();<br>
+};<br>
+#else<br>
+S13 s13;<br>
+// expected-error@first.h:* {{'S13' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S14 {<br>
+  template <typename A, typename B> void foo();<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S14 {<br>
+  template <typename A> void foo();<br>
+};<br>
+#else<br>
+S14 s14;<br>
+// expected-error@second.h:* {{'S14::foo' from module 'second' is not present in definition of 'S14' in module 'first'}}<br>
+// expected-note@first.h:* {{declaration of 'foo' does not match}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+template <typename T><br>
+struct S15 : T {<br>
+  void foo() {<br>
+    int x = __builtin_offsetof(T, first);<br>
+  }<br>
+};<br>
+#elif defined(SECOND)<br>
+template <typename T><br>
+struct S15 : T {<br>
+  void foo() {<br>
+    int x = __builtin_offsetof(T, second);<br>
+  }<br>
+};<br>
+#else<br>
+template <typename T><br>
+void Test15() {<br>
+  S15<T> s15;<br>
+// expected-error@first.h:* {{'S15' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S16 {<br>
+  template <template<int = 0> class Y><br>
+  void foo() {<br>
+    Y<> y;<br>
+  }<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S16 {<br>
+  template <template<int = 1> class Y><br>
+  void foo() {<br>
+    Y<> y;<br>
+  }<br>
+};<br>
+#else<br>
+void TestS16() {<br>
+  S16 s16;<br>
+}<br>
+// expected-error@first.h:* {{'S16' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S17 {<br>
+  template <template <typename> class T><br>
+  static int foo(int a = 1);<br>
+  template <template <typename> class T, template <typename> class U><br>
+  using Q_type = T<int>;<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S17 {<br>
+  template <template <typename> class T><br>
+  static int foo(int a = 1);<br>
+  template <template <typename> class T, template <typename> class U><br>
+  using Q_type = U<int>;<br>
+};<br>
+#else<br>
+S17 s17;<br>
+// expected-error@second.h:* {{'S17::Q_type' from module 'second' is not present in definition of 'S17' in module 'first'}}<br>
+// expected-note@first.h:* {{declaration of 'Q_type' does not match}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S18 {<br>
+  enum E { X1 };<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S18 {<br>
+  enum X { X1 };<br>
+};<br>
+#else<br>
+S18 s18;<br>
+// expected-error@second.h:* {{'S18::X' from module 'second' is not present in definition of 'S18' in module 'first'}}<br>
+// expected-note@first.h:* {{definition has no member 'X'}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S19 {<br>
+  enum E { X1 };<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S19 {<br>
+  enum E { X1, X2 };<br>
+};<br>
+#else<br>
+S19 s19;<br>
+// expected-error@first.h:* {{'S19' has different definitions in different modules; first difference is definition in module 'first' found enum 'E' has 1 element}}<br>
+// expected-note@second.h:* {{but in 'second' found enum 'E' has 2 elements}}<br>
+// expected-error@second.h:* {{'S19::E::X2' from module 'second' is not present in definition of 'S19::E' in module 'first'}}<br>
+// expected-note@first.h:* {{definition has no member 'X2'}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S20 {<br>
+  enum E { X1 = 1 };<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S20 {<br>
+  enum E { X1 = 5};<br>
+};<br>
+#else<br>
+S20 s20;<br>
+// 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}}<br>
+// expected-note@second.h:* {{but in 'second' found element 'X1' in enum 'E' with different initializer}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S21 {<br>
+  void foo() {<br>
+    label:<br>
+    ;<br>
+  }<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S21 {<br>
+  void foo() {<br>
+    ;<br>
+  }<br>
+};<br>
+#else<br>
+S21 s21;<br>
+// expected-error@first.h:* {{'S21' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S22 {<br>
+  void foo() {<br>
+    label_first:<br>
+    ;<br>
+  }<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S22 {<br>
+  void foo() {<br>
+    label_second:<br>
+    ;<br>
+  }<br>
+};<br>
+#else<br>
+S22 s22;<br>
+// expected-error@first.h:* {{'S22' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S23 {<br>
+  typedef int a;<br>
+  typedef char b;<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S23 {<br>
+  typedef char a;<br>
+  typedef int b;<br>
+};<br>
+#else<br>
+S23 s23;<br>
+// expected-error@second.h:* {{'S23::a' from module 'second' is not present in definition of 'S23' in module 'first'}}<br>
+// expected-note@first.h:* {{declaration of 'a' does not match}}<br>
+// expected-error@second.h:* {{'S23::b' from module 'second' is not present in definition of 'S23' in module 'first'}}<br>
+// expected-note@first.h:* {{declaration of 'b' does not match}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S24 {<br>
+  inline int foo();<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S24 {<br>
+  int foo();<br>
+};<br>
+#else<br>
+S24 s24;<br>
+// expected-error@first.h:* {{'S24' has different definitions in different modules; first difference is definition in module 'first' found method 'foo' is inline}}<br>
+// expected-note@second.h:* {{but in 'second' found method 'foo' is not inline}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S25 {<br>
+  int x;<br>
+  S25() : x(5) {}<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S25 {<br>
+  int x;<br>
+  S25() {}<br>
+};<br>
+#else<br>
+S25 s25;<br>
+// expected-error@first.h:* {{'S25' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S26 {<br>
+  int x;<br>
+  S26() : x(5) {}<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S26 {<br>
+  int x;<br>
+  S26() : x(2) {}<br>
+};<br>
+#else<br>
+S26 s26;<br>
+// expected-error@first.h:* {{'S26' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S27 {<br>
+  explicit S27(int) {}<br>
+  S27() {}<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S27 {<br>
+  S27(int) {}<br>
+  S27() {}<br>
+};<br>
+#else<br>
+S27 s27;<br>
+// expected-error@first.h:* {{'S27' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST) || defined(SECOND)<br>
+struct Base1 {<br>
+  Base1();<br>
+  Base1(int);<br>
+  Base1(double);<br>
+};<br>
+<br>
+struct Base2 {<br>
+  Base2();<br>
+  Base2(int);<br>
+  Base2(double);<br>
+};<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S28 : public Base1 {};<br>
+#elif defined(SECOND)<br>
+struct S28 : public Base2 {};<br>
+#else<br>
+S28 s28;<br>
+// expected-error@first.h:* {{'S28' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S29 : virtual Base1 {};<br>
+#elif defined(SECOND)<br>
+struct S29 : virtual Base2 {};<br>
+#else<br>
+S29 s29;<br>
+// expected-error@first.h:* {{'S29' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S30 : public Base1 {<br>
+  S30() : Base1(1) {}<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S30 : public Base1 {<br>
+  S30() : Base1(1.0) {}<br>
+};<br>
+#else<br>
+S30 s30;<br>
+// expected-error@first.h:* {{'S30' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S31 : virtual Base1 {<br>
+  S31() : Base1(1) {}<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S31 : virtual Base1 {<br>
+  S31() : Base1(1.0) {}<br>
+};<br>
+#else<br>
+S31 s31;<br>
+// expected-error@first.h:* {{'S31' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S32 : public Base1, Base2 {<br>
+  S32() : Base1(1), Base2(1.0) {}<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S32 : public Base2, Base1 {<br>
+  S32() : Base2(1), Base1(1.0) {}<br>
+};<br>
+#else<br>
+S32 s32;<br>
+// expected-error@first.h:* {{'S32' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S33 {<br>
+  S33() : S33(5) {}<br>
+  S33(int) {int a;}<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S33 {<br>
+  S33() : S33(5) {}<br>
+  S33(int) {}<br>
+};<br>
+#else<br>
+S33 s33;<br>
+// expected-error@first.h:* {{'S33' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S34 {<br>
+  operator bool();<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S34 {<br>
+  operator int();<br>
+};<br>
+#else<br>
+S34 s34;<br>
+// expected-error@second.h:* {{'S34::operator int' from module 'second' is not present in definition of 'S34' in module 'first'}}<br>
+// expected-note@first.h:* {{definition has no member 'operator int'}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S35 {<br>
+  explicit operator bool();<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S35 {<br>
+  operator bool();<br>
+};<br>
+#else<br>
+S35 s35;<br>
+// expected-error@first.h:* {{'S35' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S36 {<br>
+  int x : 3;<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S36 {<br>
+  int x : 4;<br>
+};<br>
+#else<br>
+S36 s36;<br>
+// expected-error@first.h:* {{'S36' has different definitions in different modules; first difference is definition in module 'first' found bitfield 'x'}}<br>
+// expected-note@second.h:* {{but in 'second' found bitfield 'x'}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S37 {<br>
+  mutable int x;<br>
+  int y;<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S37 {<br>
+  int x;<br>
+  mutable int y;<br>
+};<br>
+#else<br>
+S37 s37;<br>
+// expected-error@first.h:* {{'S37' has different definitions in different modules; first difference is definition in module 'first' found mutable 'x'}}<br>
+// expected-note@second.h:* {{but in 'second' found non-mutable 'x'}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+template <class X><br>
+struct S38 { };<br>
+#elif defined(SECOND)<br>
+template <class Y><br>
+struct S38 { };<br>
+#else<br>
+S38<int> s38;<br>
+// expected-error@first.h:* {{'S38' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+template <class X = int><br>
+struct S39 { X x; };<br>
+#elif defined(SECOND)<br>
+template <class X = double><br>
+struct S39 { X x; };<br>
+#else<br>
+S39<> s39;<br>
+// expected-error@first.h:* {{'S39' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+template <int X = 5><br>
+struct S40 { int x = X; };<br>
+#elif defined(SECOND)<br>
+template <int X = 7><br>
+struct S40 { int x = X; };<br>
+#else<br>
+S40<> s40;<br>
+// expected-error@first.h:* {{'S40' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+template <int> class T41a{};<br>
+template <template<int> class T = T41a><br>
+struct S41 {};<br>
+#elif defined(SECOND)<br>
+template <int> class T41b{};<br>
+template <template<int> class T = T41b><br>
+struct S41 {};<br>
+#else<br>
+using ::S41;<br>
+// expected-error@first.h:* {{'S41' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S42 {<br>
+  void foo() const {}<br>
+  void bar() {}<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S42 {<br>
+  void foo() {}<br>
+  void bar() const {}<br>
+};<br>
+#else<br>
+S42 s42;<br>
+// expected-error@second.h:* {{'S42::bar' from module 'second' is not present in definition of 'S42' in module 'first'}}<br>
+// expected-note@first.h:* {{declaration of 'bar' does not match}}<br>
+// expected-error@second.h:* {{'S42::foo' from module 'second' is not present in definition of 'S42' in module 'first'}}<br>
+// expected-note@first.h:* {{declaration of 'foo' does not match}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S43 {<br>
+  static constexpr int x = 1;<br>
+  int y = 1;<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S43 {<br>
+  int x = 1;<br>
+  static constexpr int y = 1;<br>
+};<br>
+#else<br>
+S43 s43;<br>
+// expected-error@second.h:* {{'S43::x' from module 'second' is not present in definition of 'S43' in module 'first'}}<br>
+// expected-note@first.h:* {{declaration of 'x' does not match}}<br>
+// expected-error@second.h:* {{'S43::y' from module 'second' is not present in definition of 'S43' in module 'first'}}<br>
+// expected-note@first.h:* {{declaration of 'y' does not match}}<br>
+//#endif<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+void f44();<br>
+struct S44 {<br>
+  friend void f44();<br>
+};<br>
+#elif defined(SECOND)<br>
+void g44();<br>
+struct S44 {<br>
+  friend void g44();<br>
+};<br>
+#else<br>
+S44 s44;<br>
+// expected-error@first.h:* {{'S44' has different definitions in different modules; first difference is definition in module 'first' found friend 'f44'}}<br>
+// expected-note@second.h:* {{but in 'second' found other friend 'g44'}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S45 { int n : 1; };<br>
+#elif defined(SECOND)<br>
+struct S45 { int n = 1; };<br>
+#else<br>
+S45 s45;<br>
+// expected-error@first.h:* {{'S45' has different definitions in different modules; first difference is definition in module 'first' found bitfield 'n'}}<br>
+// expected-note@second.h:* {{but in 'second' found field 'n'}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S46 {<br>
+  int operator+(int) { return 0; }<br>
+};<br>
+#elif defined(SECOND)<br>
+struct S46 {<br>
+  int operator-(int) { return 0; }<br>
+};<br>
+#else<br>
+S46 s46;<br>
+// expected-error@second.h:* {{'S46::operator-' from module 'second' is not present in definition of 'S46' in module 'first'}}<br>
+// expected-note@first.h:* {{definition has no member 'operator-'}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+template <typename T><br>
+struct S47 {<br>
+  int foo(int);<br>
+  float foo(float);<br>
+  int bar(int);<br>
+  float bar(float);<br>
+  int x = foo(T());<br>
+};<br>
+#elif defined(SECOND)<br>
+template <typename T><br>
+struct S47 {<br>
+  int foo(int);<br>
+  float foo(float);<br>
+  int bar(int);<br>
+  float bar(float);<br>
+  int x = bar(T());<br>
+};<br>
+#else<br>
+template <typename T><br>
+using S48 = S47<T>;<br>
+// expected-error@first.h:* {{'S47' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+template <typename T><br>
+struct S49 {<br>
+  int operator+(int);<br>
+  float operator+(float);<br>
+  int operator-(int);<br>
+  float operator-(float);<br>
+  int x = S49() + T();<br>
+};<br>
+#elif defined(SECOND)<br>
+template <typename T><br>
+struct S49 {<br>
+  int operator+(int);<br>
+  float operator+(float);<br>
+  int operator-(int);<br>
+  float operator-(float);<br>
+  int x = S49() - T();<br>
+};<br>
+#else<br>
+template <typename T><br>
+using S50 = S49<T>;<br>
+// expected-error@first.h:* {{'S49' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+namespace A51 {<br>
+  void foo();<br>
+}<br>
+struct S51 {<br>
+  S51() {<br>
+    A51::foo();<br>
+  }<br>
+};<br>
+#elif defined(SECOND)<br>
+namespace B51 {<br>
+  void foo();<br>
+}<br>
+struct S51 {<br>
+  S51() {<br>
+    B51::foo();<br>
+  }<br>
+};<br>
+#else<br>
+S51 s51;<br>
+// expected-error@first.h:* {{'S51' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+namespace N52 {<br>
+  void foo();<br>
+}<br>
+struct S52 {<br>
+  S52() {<br>
+    N52::foo();<br>
+  }<br>
+};<br>
+#elif defined(SECOND)<br>
+namespace N52 {<br>
+  void foo();<br>
+}<br>
+struct S52 {<br>
+  S52() {<br>
+    ::N52::foo();<br>
+  }<br>
+};<br>
+#else<br>
+S52 s52;<br>
+// expected-error@first.h:* {{'S52' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+namespace N53 {<br>
+  struct foo {<br>
+    static int bar();<br>
+  };<br>
+  using A = foo;<br>
+}<br>
+struct S53 {<br>
+  S53() {<br>
+    N53::A::bar();<br>
+  }<br>
+};<br>
+#elif defined(SECOND)<br>
+namespace N53 {<br>
+  struct foo {<br>
+    static int bar();<br>
+  };<br>
+  using B = foo;<br>
+}<br>
+struct S53 {<br>
+  S53() {<br>
+    N53::B::bar();<br>
+  }<br>
+};<br>
+#else<br>
+S53 s53;<br>
+// expected-error@first.h:* {{'S53' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+namespace N54 {<br>
+namespace A {<br>
+void foo();<br>
+}<br>
+namespace AA = A;<br>
+}<br>
+<br>
+struct S54 {<br>
+  S54() {<br>
+    N54::AA::foo();<br>
+  }<br>
+};<br>
+#elif defined(SECOND)<br>
+namespace N54 {<br>
+namespace B {<br>
+void foo();<br>
+}<br>
+namespace BB = B;<br>
+}<br>
+<br>
+struct S54 {<br>
+  S54() {<br>
+    N54::BB::foo();<br>
+  }<br>
+};<br>
+#else<br>
+S54 s54;<br>
+// expected-error@first.h:* {{'S54' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+namespace N55 {<br>
+namespace A {<br>
+void foo();<br>
+}<br>
+namespace X = A;<br>
+}<br>
+<br>
+struct S55 {<br>
+  S55() {<br>
+    N55::X::foo();<br>
+  }<br>
+};<br>
+#elif defined(SECOND)<br>
+namespace N55 {<br>
+namespace B {<br>
+void foo();<br>
+}<br>
+namespace X = B;<br>
+}<br>
+<br>
+struct S55 {<br>
+  S55() {<br>
+    N55::X::foo();<br>
+  }<br>
+};<br>
+#else<br>
+S55 s55;<br>
+// expected-error@first.h:* {{'S55' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+template<int> struct foo56{};<br>
+template <template<int> class T><br>
+struct S56 {};<br>
+struct S57 {<br>
+  S56<foo56> a;<br>
+};<br>
+#elif defined(SECOND)<br>
+template<int> struct bar56{};<br>
+template <template<int> class T><br>
+struct S56 {};<br>
+struct S57 {<br>
+  S56<bar56> a;<br>
+};<br>
+#else<br>
+S57 s57;<br>
+// expected-error@second.h:* {{'S57::a' from module 'second' is not present in definition of 'S57' in module 'first'}}<br>
+// expected-note@first.h:* {{declaration of 'a' does not match}}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+template<int> struct foo58{};<br>
+template <template<int> class T><br>
+struct S58 {};<br>
+struct S59 {<br>
+  S58<foo58> a;<br>
+};<br>
+#elif defined(SECOND)<br>
+template<int> struct foo58{};<br>
+template <template<int> class T><br>
+struct S58 {};<br>
+struct S59 {<br>
+  S58<::foo58> a;<br>
+};<br>
+#else<br>
+S59 s59;<br>
+// expected-error@first.h:* {{'S59' has different definitions in different modules; definition in module 'first' is here}}<br>
+// expected-note@second.h:* {{definition in module 'second' is here}}<br>
+#endif<br>
+<br>
+<br>
+// Don't warn on these cases<br>
+#if defined(FIRST)<br>
+void f01(int = 0);<br>
+struct S01 { friend void f01(int); };<br>
+#elif defined(SECOND)<br>
+void f01(int);<br>
+struct S01 { friend void f01(int); };<br>
+#else<br>
+S01 s01;<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+template <template <int> class T> class Wrapper {};<br>
+<br>
+template <int N> class SelfReference {<br>
+  SelfReference(Wrapper<::<wbr>SelfReference> &R) {}<br>
+};<br>
+<br>
+struct Xx {<br>
+  struct Yy {<br>
+  };<br>
+};<br>
+<br>
+Xx::Xx::Xx::Yy yy;<br>
+<br>
+namespace NNS {<br>
+template <typename> struct Foo;<br>
+template <template <class> class T = NNS::Foo><br>
+struct NestedNamespaceSpecifier {};<br>
+}<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+struct S02 { };<br>
+void S02Construct() {<br>
+  S02 foo;<br>
+  S02 bar = foo;<br>
+  S02 baz(bar);<br>
+}<br>
+#elif defined(SECOND)<br>
+struct S02 { };<br>
+#else<br>
+S02 s02;<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+template <class><br>
+struct S03 {};<br>
+#elif defined(SECOND)<br>
+template <class><br>
+struct S03 {};<br>
+#else<br>
+S03<int> s03;<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+template <class T><br>
+struct S04 {<br>
+  T t;<br>
+};<br>
+#elif defined(SECOND)<br>
+template <class T><br>
+struct S04 {<br>
+  T t;<br>
+};<br>
+#else<br>
+S03<int> s04;<br>
+#endif<br>
+<br>
+#if defined(FIRST)<br>
+template <class T><br>
+class Wrapper05;<br>
+template <class T><br>
+struct S05 {<br>
+  Wrapper05<T> t;<br>
+};<br>
+#elif defined(SECOND)<br>
+template <class T><br>
+class Wrapper05;<br>
+template <class T><br>
+struct S05 {<br>
+  Wrapper05<T> t;<br>
+};<br>
+#else<br>
+template <class T><br>
+class Wrapper05{};<br>
+S05<int> s05;<br>
+#endif<br>
+<br>
+// Keep macros contained to one file.<br>
+#ifdef FIRST<br>
+#undef FIRST<br>
+#endif<br>
+#ifdef SECOND<br>
+#undef SECOND<br>
+#endif<br>
<br>
<br>
______________________________<wbr>_________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org">cfe-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/<wbr>mailman/listinfo/cfe-commits</a><br>
</blockquote></div><br></div>