r295284 - Add better ODR checking for modules.

Richard Trieu via cfe-commits cfe-commits at lists.llvm.org
Wed Feb 15 20:53:41 PST 2017


Author: rtrieu
Date: Wed Feb 15 22:53:40 2017
New Revision: 295284

URL: http://llvm.org/viewvc/llvm-project?rev=295284&view=rev
Log:
Add better ODR checking for modules.

Recommit r293585 that was reverted in r293611 with new fixes.  The previous
issue was determined to be an overly aggressive AST visitor from forward
declared objects.  The visitor will now only deeply visit certain Decl's and
only do a shallow information extraction from all other Decl's.

When objects are imported for modules, there is a chance that a name collision
will cause an ODR violation.  Previously, only a small number of such
violations were detected.  This patch provides a stronger check based on
AST nodes.

The information needed to uniquely identify an object is taken from the AST and
put into a one-dimensional byte stream.  This stream is then hashed to give
a value to represent the object, which is stored with the other object data
in the module.

When modules are loaded, and Decl's are merged, the hash values of the two
Decl's are compared.  Only Decl's with matched hash values will be merged.
Mismatch hashes will generate a module error, and if possible, point to the
first difference between the two objects.

The transform from AST to byte stream is a modified depth first algorithm.
Due to references between some AST nodes, a pure depth first algorithm could
generate loops.  For Stmt nodes, a straight depth first processing occurs.
For Type and Decl nodes, they are replaced with an index number and only on
first visit will these nodes be processed.  As an optimization, boolean
values are saved and stored together in reverse order at the end of the
byte stream to lower the ammount of data that needs to be hashed.

Compile time impact was measured at 1.5-2.0% during module building, and
negligible during builds without module building.

Differential Revision: https://reviews.llvm.org/D21675

Added:
    cfe/trunk/include/clang/AST/ODRHash.h
    cfe/trunk/lib/AST/ODRHash.cpp
    cfe/trunk/test/Modules/odr_hash.cpp
Modified:
    cfe/trunk/include/clang/AST/DeclCXX.h
    cfe/trunk/include/clang/AST/Stmt.h
    cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
    cfe/trunk/lib/AST/CMakeLists.txt
    cfe/trunk/lib/AST/DeclCXX.cpp
    cfe/trunk/lib/AST/StmtProfile.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Serialization/ASTReader.cpp
    cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
    cfe/trunk/lib/Serialization/ASTWriter.cpp
    cfe/trunk/test/Modules/merge-using-decls.cpp

Modified: cfe/trunk/include/clang/AST/DeclCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=295284&r1=295283&r2=295284&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclCXX.h (original)
+++ cfe/trunk/include/clang/AST/DeclCXX.h Wed Feb 15 22:53:40 2017
@@ -458,6 +458,9 @@ class CXXRecordDecl : public RecordDecl
     /// \brief Whether we are currently parsing base specifiers.
     unsigned IsParsingBaseSpecifiers : 1;
 
+    /// \brief A hash of parts of the class to help in ODR checking.
+    unsigned ODRHash;
+
     /// \brief The number of base class specifiers in Bases.
     unsigned NumBases;
 
@@ -703,6 +706,9 @@ public:
     return data().IsParsingBaseSpecifiers;
   }
 
+  void computeODRHash();
+  unsigned getODRHash() const { return data().ODRHash; }
+
   /// \brief Sets the base classes of this struct or class.
   void setBases(CXXBaseSpecifier const * const *Bases, unsigned NumBases);
 

Added: cfe/trunk/include/clang/AST/ODRHash.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ODRHash.h?rev=295284&view=auto
==============================================================================
--- cfe/trunk/include/clang/AST/ODRHash.h (added)
+++ cfe/trunk/include/clang/AST/ODRHash.h Wed Feb 15 22:53:40 2017
@@ -0,0 +1,82 @@
+//===-- ODRHash.h - Hashing to diagnose ODR failures ------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the declaration of the ODRHash class, which calculates
+/// a hash based on AST nodes, which is stable across different runs.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/DeclarationName.h"
+#include "clang/AST/Type.h"
+#include "clang/AST/TemplateBase.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace clang {
+
+class Decl;
+class IdentifierInfo;
+class NestedNameSpecifer;
+class Stmt;
+class TemplateParameterList;
+
+// ODRHash is used to calculate a hash based on AST node contents that
+// does not rely on pointer addresses.  This allows the hash to not vary
+// between runs and is usable to detect ODR problems in modules.  To use,
+// construct an ODRHash object, then call Add* methods over the nodes that
+// need to be hashed.  Then call CalculateHash to get the hash value.
+// Typically, only one Add* call is needed.  clear can be called to reuse the
+// object.
+class ODRHash {
+  // Use DenseMaps to convert between Decl and Type pointers and an index value.
+  llvm::DenseMap<const Decl*, unsigned> DeclMap;
+  llvm::DenseMap<const Type*, unsigned> TypeMap;
+
+  // Save space by processing bools at the end.
+  llvm::SmallVector<bool, 128> Bools;
+
+  llvm::FoldingSetNodeID ID;
+
+public:
+  ODRHash() {}
+
+  // Use this for ODR checking classes between modules.  This method compares
+  // more information than the AddDecl class.
+  void AddCXXRecordDecl(const CXXRecordDecl *Record);
+
+  // Process SubDecls of the main Decl.  This method calls the DeclVisitor
+  // while AddDecl does not.
+  void AddSubDecl(const Decl *D);
+
+  // Reset the object for reuse.
+  void clear();
+
+  // Add booleans to ID and uses it to calculate the hash.
+  unsigned CalculateHash();
+
+  // Add AST nodes that need to be processed.
+  void AddDecl(const Decl *D);
+  void AddType(const Type *T);
+  void AddQualType(QualType T);
+  void AddStmt(const Stmt *S);
+  void AddIdentifierInfo(const IdentifierInfo *II);
+  void AddNestedNameSpecifier(const NestedNameSpecifier *NNS);
+  void AddTemplateName(TemplateName Name);
+  void AddDeclarationName(DeclarationName Name);
+  void AddTemplateArgument(TemplateArgument TA);
+  void AddTemplateParameterList(const TemplateParameterList *TPL);
+
+  // Save booleans until the end to lower the size of data to process.
+  void AddBoolean(bool value);
+};
+
+}  // end namespace clang

Modified: cfe/trunk/include/clang/AST/Stmt.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Stmt.h?rev=295284&r1=295283&r2=295284&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Stmt.h (original)
+++ cfe/trunk/include/clang/AST/Stmt.h Wed Feb 15 22:53:40 2017
@@ -39,6 +39,7 @@ namespace clang {
   class Expr;
   class IdentifierInfo;
   class LabelDecl;
+  class ODRHash;
   class ParmVarDecl;
   class PrinterHelper;
   struct PrintingPolicy;
@@ -436,6 +437,8 @@ public:
   /// written in the source.
   void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
                bool Canonical) const;
+
+  void ProcessODRHash(llvm::FoldingSetNodeID &ID, ODRHash& Hash) const;
 };
 
 /// DeclStmt - Adaptor class for mixing declarations with statements and

Modified: cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td?rev=295284&r1=295283&r2=295284&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td Wed Feb 15 22:53:40 2017
@@ -117,6 +117,70 @@ def note_module_odr_violation_different_
 def err_module_odr_violation_different_instantiations : Error<
   "instantiation of %q0 is different in different modules">;
 
+def err_module_odr_violation_mismatch_decl : Error<
+  "%q0 has different definitions in different modules; first difference is "
+  "%select{definition in module '%2'|defined here}1 found "
+  "%select{end of class|public access specifier|private access specifier|"
+  "protected access specifier|friend declaration|enum|"
+  "static assert|typedef|type alias|method|constructor|destructor|"
+  "conversion operator|field|other}3">;
+def note_module_odr_violation_mismatch_decl : Note<"but in '%0' found "
+  "%select{end of class|public access specifier|private access specifier|"
+  "protected access specifier|friend declaration|enum|"
+  "static assert|typedef|type alias|method|constructor|destructor|"
+  "conversion operator|field|other}1">;
+
+def err_module_odr_violation_mismatch_decl_diff : Error<
+  "%q0 has different definitions in different modules; first difference is "
+  "%select{definition in module '%2'|defined here}1 found "
+  "%select{friend %4|enum %4|element %4 in enum %5|"
+  "element %4 in enum %5 with initializer|"
+  "element %4 in enum %5 with no initializer|"
+  "element %4 in enum %5 with initializer|"
+  "enum %4 has %5 element%s5|"
+  "static assert with condition|"
+  "static assert with message|"
+  "static assert with %select{|no}4 message|"
+  "%select{typedef|type alias}4 name %5|"
+  "method named %4|"
+  "method %4 is %select{non-|}5static|"
+  "method %4 is %select{not |}5inline|"
+  "method %4 is %select{not |}5const|"
+  "method %4 has %5 parameter%s5|"
+  "method %4 has %ordinal5 parameter %select{named %7|with no name}6|"
+  "method %4 has %ordinal5 parameter with type %6|"
+  "method %4 has %ordinal5 parameter with default argument|"
+  "method %4 has %ordinal5 parameter with %select{no |}6 default argument|"
+  "method %4 has %select{|no }5body|"
+  "method %4 has different body|"
+  "field %4|"
+  "%select{field|bitfield}5 %4|"
+  "%select{non-mutable|mutable}5 %4}3">;
+def note_module_odr_violation_mismatch_decl_diff : Note<"but in '%0' found "
+  "%select{other friend %2|other enum %2|different element %2 in enum %3|"
+  "element %2 in enum %3 with initializer|"
+  "element %2 in enum %3 with no initializer|"
+  "element %2 in enum %3 with different initializer|"
+  "enum %2 has %3 element%s3|"
+  "static assert with different condition|"
+  "static assert with different message|"
+  "static assert with %select{|no}2 message|"
+  "different %select{typedef|type alias}2 name %3|"
+  "method named %2|"
+  "method %2 is %select{non-|}3static|"
+  "method %2 is %select{not |}3inline|"
+  "method %2 is %select{not |}3const|"
+  "method %2 has %3 parameter%s3|"
+  "method %2 has %ordinal3 parameter %select{named %5|with no name}4|"
+  "method %2 has %ordinal3 parameter with type %4|"
+  "method %2 has %ordinal3 parameter with different default argument|"
+  "method %2 has %ordinal3 parameter with %select{no |}4default argument|"
+  "method %2 has %select{|no }3body|"
+  "method %2 has different body|"
+  "field %2|"
+  "%select{field|bitfield}3 %2|"
+  "%select{non-mutable|mutable}3 %2}1">;
+
 def warn_module_uses_date_time : Warning<
   "%select{precompiled header|module}0 uses __DATE__ or __TIME__">,
   InGroup<DiagGroup<"pch-date-time">>;

Modified: cfe/trunk/lib/AST/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/CMakeLists.txt?rev=295284&r1=295283&r2=295284&view=diff
==============================================================================
--- cfe/trunk/lib/AST/CMakeLists.txt (original)
+++ cfe/trunk/lib/AST/CMakeLists.txt Wed Feb 15 22:53:40 2017
@@ -40,6 +40,7 @@ add_clang_library(clangAST
   MicrosoftMangle.cpp
   NestedNameSpecifier.cpp
   NSAPI.cpp
+  ODRHash.cpp
   OpenMPClause.cpp
   ParentMap.cpp
   RawCommentList.cpp

Modified: cfe/trunk/lib/AST/DeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclCXX.cpp?rev=295284&r1=295283&r2=295284&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclCXX.cpp (original)
+++ cfe/trunk/lib/AST/DeclCXX.cpp Wed Feb 15 22:53:40 2017
@@ -18,6 +18,7 @@
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/ODRHash.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/Basic/IdentifierTable.h"
 #include "llvm/ADT/STLExtras.h"
@@ -71,8 +72,8 @@ CXXRecordDecl::DefinitionData::Definitio
       ImplicitCopyAssignmentHasConstParam(true),
       HasDeclaredCopyConstructorWithConstParam(false),
       HasDeclaredCopyAssignmentWithConstParam(false), IsLambda(false),
-      IsParsingBaseSpecifiers(false), NumBases(0), NumVBases(0), Bases(),
-      VBases(), Definition(D), FirstFriend() {}
+      IsParsingBaseSpecifiers(false), ODRHash(0), NumBases(0), NumVBases(0),
+      Bases(), VBases(), Definition(D), FirstFriend() {}
 
 CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const {
   return Bases.get(Definition->getASTContext().getExternalSource());
@@ -371,6 +372,16 @@ CXXRecordDecl::setBases(CXXBaseSpecifier
   data().IsParsingBaseSpecifiers = false;
 }
 
+void CXXRecordDecl::computeODRHash() {
+  if (!DefinitionData)
+    return;
+
+  ODRHash Hash;
+  Hash.AddCXXRecordDecl(this);
+
+  DefinitionData->ODRHash = Hash.CalculateHash();
+}
+
 void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) {
   // C++11 [class.copy]p11:
   //   A defaulted copy/move constructor for a class X is defined as

Added: cfe/trunk/lib/AST/ODRHash.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ODRHash.cpp?rev=295284&view=auto
==============================================================================
--- cfe/trunk/lib/AST/ODRHash.cpp (added)
+++ cfe/trunk/lib/AST/ODRHash.cpp Wed Feb 15 22:53:40 2017
@@ -0,0 +1,940 @@
+//===-- ODRHash.cpp - Hashing to diagnose ODR failures ----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements the ODRHash class, which calculates a hash based
+/// on AST nodes, which is stable across different runs.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ODRHash.h"
+
+#include "clang/AST/DeclVisitor.h"
+#include "clang/AST/NestedNameSpecifier.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/TypeVisitor.h"
+
+using namespace clang;
+
+
+// Hashing for Stmt is with Stmt::Profile, since they derive from the same base
+// class.
+void ODRHash::AddStmt(const Stmt *S) {
+  assert(S && "Expecting non-null pointer.");
+  S->ProcessODRHash(ID, *this);
+}
+
+void ODRHash::AddIdentifierInfo(const IdentifierInfo *II) {
+  assert(II && "Expecting non-null pointer.");
+  ID.AddString(II->getName());
+}
+
+void ODRHash::AddNestedNameSpecifier(const NestedNameSpecifier *NNS) {
+  assert(NNS && "Expecting non-null pointer.");
+  const auto *Prefix = NNS->getPrefix();
+  AddBoolean(Prefix);
+  if (Prefix)
+    AddNestedNameSpecifier(Prefix);
+
+  auto Kind = NNS->getKind();
+  ID.AddInteger(Kind);
+  switch (Kind) {
+  case NestedNameSpecifier::Identifier:
+    AddIdentifierInfo(NNS->getAsIdentifier());
+    break;
+  case NestedNameSpecifier::Namespace:
+    AddDecl(NNS->getAsNamespace());
+    break;
+  case NestedNameSpecifier::NamespaceAlias:
+    AddDecl(NNS->getAsNamespaceAlias());
+    break;
+  case NestedNameSpecifier::TypeSpec:
+  case NestedNameSpecifier::TypeSpecWithTemplate:
+    AddType(NNS->getAsType());
+    break;
+  case NestedNameSpecifier::Global:
+  case NestedNameSpecifier::Super:
+    break;
+  }
+}
+
+void ODRHash::AddTemplateName(TemplateName Name) {
+  const auto Kind = Name.getKind();
+  ID.AddInteger(Kind);
+  AddBoolean(Name.isDependent());
+  AddBoolean(Name.isInstantiationDependent());
+  switch (Kind) {
+  case TemplateName::Template:
+    AddDecl(Name.getAsTemplateDecl());
+    break;
+  case TemplateName::OverloadedTemplate: {
+    const auto *Storage = Name.getAsOverloadedTemplate();
+    ID.AddInteger(Storage->size());
+    for (const auto *ND : *Storage) {
+      AddDecl(ND);
+    }
+    break;
+  }
+  case TemplateName::QualifiedTemplate: {
+    const auto *QTN = Name.getAsQualifiedTemplateName();
+    AddNestedNameSpecifier(QTN->getQualifier());
+    AddBoolean(QTN->hasTemplateKeyword());
+    AddDecl(QTN->getDecl());
+    break;
+  }
+  case TemplateName::DependentTemplate: {
+    const auto *DTN = Name.getAsDependentTemplateName();
+    AddBoolean(DTN->isIdentifier());
+    if (DTN->isIdentifier()) {
+      AddIdentifierInfo(DTN->getIdentifier());
+    } else {
+      ID.AddInteger(DTN->getOperator());
+    }
+    break;
+  }
+  case TemplateName::SubstTemplateTemplateParm: {
+    const auto *Storage = Name.getAsSubstTemplateTemplateParm();
+    AddDecl(Storage->getParameter());
+    AddTemplateName(Storage->getReplacement());
+    break;
+  }
+  case TemplateName::SubstTemplateTemplateParmPack: {
+    const auto *Storage = Name.getAsSubstTemplateTemplateParmPack();
+    AddDecl(Storage->getParameterPack());
+    AddTemplateArgument(Storage->getArgumentPack());
+    break;
+  }
+  }
+}
+
+void ODRHash::AddDeclarationName(DeclarationName Name) {
+  AddBoolean(Name.isEmpty());
+  if (Name.isEmpty())
+    return;
+
+  auto Kind = Name.getNameKind();
+  ID.AddInteger(Kind);
+  switch (Kind) {
+  case DeclarationName::Identifier:
+    AddIdentifierInfo(Name.getAsIdentifierInfo());
+    break;
+  case DeclarationName::ObjCZeroArgSelector:
+  case DeclarationName::ObjCOneArgSelector:
+  case DeclarationName::ObjCMultiArgSelector: {
+    Selector S = Name.getObjCSelector();
+    AddBoolean(S.isNull());
+    AddBoolean(S.isKeywordSelector());
+    AddBoolean(S.isUnarySelector());
+    unsigned NumArgs = S.getNumArgs();
+    for (unsigned i = 0; i < NumArgs; ++i) {
+      AddIdentifierInfo(S.getIdentifierInfoForSlot(i));
+    }
+    break;
+  }
+  case DeclarationName::CXXConstructorName:
+  case DeclarationName::CXXDestructorName:
+    AddQualType(Name.getCXXNameType());
+    break;
+  case DeclarationName::CXXOperatorName:
+    ID.AddInteger(Name.getCXXOverloadedOperator());
+    break;
+  case DeclarationName::CXXLiteralOperatorName:
+    AddIdentifierInfo(Name.getCXXLiteralIdentifier());
+    break;
+  case DeclarationName::CXXConversionFunctionName:
+    AddQualType(Name.getCXXNameType());
+    break;
+  case DeclarationName::CXXUsingDirective:
+    break;
+  case DeclarationName::CXXDeductionGuideName: {
+    auto *Template = Name.getCXXDeductionGuideTemplate();
+    AddBoolean(Template);
+    if (Template) {
+      AddDecl(Template);
+    }
+  }
+  }
+}
+
+void ODRHash::AddTemplateArgument(TemplateArgument TA) {
+  const auto Kind = TA.getKind();
+  ID.AddInteger(Kind);
+  switch (Kind) {
+  case TemplateArgument::Null:
+    llvm_unreachable("Require valid TemplateArgument");
+  case TemplateArgument::Type:
+    AddQualType(TA.getAsType());
+    break;
+  case TemplateArgument::Declaration:
+    AddDecl(TA.getAsDecl());
+    break;
+  case TemplateArgument::NullPtr:
+    AddQualType(TA.getNullPtrType());
+    break;
+  case TemplateArgument::Integral:
+    TA.getAsIntegral().Profile(ID);
+    AddQualType(TA.getIntegralType());
+    break;
+  case TemplateArgument::Template:
+  case TemplateArgument::TemplateExpansion:
+    AddTemplateName(TA.getAsTemplateOrTemplatePattern());
+    break;
+  case TemplateArgument::Expression:
+    AddStmt(TA.getAsExpr());
+    break;
+  case TemplateArgument::Pack:
+    ID.AddInteger(TA.pack_size());
+    for (auto SubTA : TA.pack_elements())
+      AddTemplateArgument(SubTA);
+    break;
+  }
+}
+
+void ODRHash::AddTemplateParameterList(const TemplateParameterList *TPL) {
+  assert(TPL && "Expecting non-null pointer.");
+  ID.AddInteger(TPL->size());
+  for (auto *ND : TPL->asArray()) {
+    AddSubDecl(ND);
+  }
+}
+
+void ODRHash::clear() {
+  DeclMap.clear();
+  TypeMap.clear();
+  Bools.clear();
+  ID.clear();
+}
+
+unsigned ODRHash::CalculateHash() {
+  // Append the bools to the end of the data segment backwards.  This allows
+  // for the bools data to be compressed 32 times smaller compared to using
+  // ID.AddBoolean
+  const unsigned unsigned_bits = sizeof(unsigned) * CHAR_BIT;
+  const unsigned size = Bools.size();
+  const unsigned remainder = size % unsigned_bits;
+  const unsigned loops = size / unsigned_bits;
+  auto I = Bools.rbegin();
+  unsigned value = 0;
+  for (unsigned i = 0; i < remainder; ++i) {
+    value <<= 1;
+    value |= *I;
+    ++I;
+  }
+  ID.AddInteger(value);
+
+  for (unsigned i = 0; i < loops; ++i) {
+    value = 0;
+    for (unsigned j = 0; j < unsigned_bits; ++j) {
+      value <<= 1;
+      value |= *I;
+      ++I;
+    }
+    ID.AddInteger(value);
+  }
+
+  assert(I == Bools.rend());
+  Bools.clear();
+  return ID.ComputeHash();
+}
+
+// Process a Decl pointer.  Add* methods call back into ODRHash while Visit*
+// methods process the relevant parts of the Decl.
+class ODRDeclVisitor : public ConstDeclVisitor<ODRDeclVisitor> {
+  typedef ConstDeclVisitor<ODRDeclVisitor> Inherited;
+  llvm::FoldingSetNodeID &ID;
+  ODRHash &Hash;
+
+public:
+  ODRDeclVisitor(llvm::FoldingSetNodeID &ID, ODRHash &Hash)
+      : ID(ID), Hash(Hash) {}
+
+  void AddDecl(const Decl *D) {
+    Hash.AddBoolean(D);
+    if (D) {
+      Hash.AddDecl(D);
+    }
+  }
+
+  void AddStmt(const Stmt *S) {
+    Hash.AddBoolean(S);
+    if (S) {
+      Hash.AddStmt(S);
+    }
+  }
+
+  void AddQualType(QualType T) {
+    Hash.AddQualType(T);
+  }
+
+  void AddIdentifierInfo(const IdentifierInfo *II) {
+    Hash.AddBoolean(II);
+    if (II) {
+      Hash.AddIdentifierInfo(II);
+    }
+  }
+
+  void AddTemplateParameterList(TemplateParameterList *TPL) {
+    Hash.AddBoolean(TPL);
+    if (TPL) {
+      Hash.AddTemplateParameterList(TPL);
+    }
+  }
+
+  void AddTemplateArgument(TemplateArgument TA) {
+    Hash.AddTemplateArgument(TA);
+  }
+
+  void Visit(const Decl *D) {
+    if (!D)
+      return;
+    if (D->isImplicit())
+      return;
+    if (D->isInvalidDecl())
+      return;
+    ID.AddInteger(D->getKind());
+    Hash.AddBoolean(D->hasAttrs());
+
+    Inherited::Visit(D);
+  }
+
+  void VisitDecl(const Decl *D) {
+    Inherited::VisitDecl(D);
+  }
+
+  void VisitLabelDecl(const LabelDecl *D) {
+    Inherited::VisitLabelDecl(D);
+  }
+
+  void VisitEnumDecl(const EnumDecl *D) {
+    const bool isFixed = D->isFixed();
+    Hash.AddBoolean(isFixed);
+    if (isFixed)
+      AddQualType(D->getIntegerType());
+    Hash.AddBoolean(D->isScoped());
+    Hash.AddBoolean(D->isScopedUsingClassTag());
+
+    // TODO: Enums should have their own ODR hash.
+    for (auto *SubDecl : D->decls()) {
+      Hash.AddSubDecl(SubDecl);
+    }
+
+    Inherited::VisitEnumDecl(D);
+  }
+
+  void VisitEnumConstantDecl(const EnumConstantDecl *D) {
+    auto *E = D->getInitExpr();
+    AddStmt(E);
+
+    Inherited::VisitEnumConstantDecl(D);
+  }
+
+  void VisitNamedDecl(const NamedDecl *D) {
+    AddIdentifierInfo(D->getIdentifier());
+    Inherited::VisitNamedDecl(D);
+  }
+
+  void VisitValueDecl(const ValueDecl *D) {
+    AddQualType(D->getType());
+    Inherited::VisitValueDecl(D);
+  }
+
+  void VisitParmVarDecl(const ParmVarDecl *D) {
+    AddStmt(D->getDefaultArg());
+    Inherited::VisitParmVarDecl(D);
+  }
+
+  void VisitAccessSpecDecl(const AccessSpecDecl *D) {
+    ID.AddInteger(D->getAccess());
+    Inherited::VisitAccessSpecDecl(D);
+  }
+
+  void VisitFriendDecl(const FriendDecl *D) {
+    TypeSourceInfo *TSI = D->getFriendType();
+    Hash.AddBoolean(TSI);
+    if (TSI)
+      AddQualType(TSI->getType());
+    else
+      AddDecl(D->getFriendDecl());
+
+    unsigned NumLists = D->getFriendTypeNumTemplateParameterLists();
+    ID.AddInteger(NumLists);
+    for (unsigned i = 0; i < NumLists; ++i)
+      AddTemplateParameterList(D->getFriendTypeTemplateParameterList(i));
+    Inherited::VisitFriendDecl(D);
+  }
+
+  void VisitStaticAssertDecl(const StaticAssertDecl *D) {
+    AddStmt(D->getAssertExpr());
+    AddStmt(D->getMessage());
+
+    Inherited::VisitStaticAssertDecl(D);
+  }
+
+  void VisitTypedefNameDecl(const TypedefNameDecl *D) {
+    AddQualType(D->getUnderlyingType());
+
+    Inherited::VisitTypedefNameDecl(D);
+  }
+
+  void VisitFunctionDecl(const FunctionDecl *D) {
+    // TODO: Functions should have their own ODR hashes.
+    AddStmt(D->getBody());
+
+    ID.AddInteger(D->getStorageClass());
+    Hash.AddBoolean(D->isInlineSpecified());
+    Hash.AddBoolean(D->isVirtualAsWritten());
+    Hash.AddBoolean(D->isPure());
+    Hash.AddBoolean(D->isDeletedAsWritten());
+    ID.AddInteger(D->getOverloadedOperator());
+    Inherited::VisitFunctionDecl(D);
+  }
+
+  void VisitCXXMethodDecl(const CXXMethodDecl *D) {
+    Hash.AddBoolean(D->isStatic());
+    Hash.AddBoolean(D->isInstance());
+    Hash.AddBoolean(D->isConst());
+    Hash.AddBoolean(D->isVolatile());
+    Inherited::VisitCXXMethodDecl(D);
+  }
+
+  void VisitCXXConstructorDecl(const CXXConstructorDecl *D) {
+    Hash.AddBoolean(D->isExplicitSpecified());
+    unsigned NumCtorInits = 0;
+    llvm::SmallVector<CXXCtorInitializer *, 4> Initializers;
+    ID.AddInteger(D->getNumCtorInitializers());
+    for (auto Initializer : D->inits()) {
+      if (Initializer->isWritten()) {
+        ++NumCtorInits;
+        Initializers.push_back(Initializer);
+      }
+    }
+    for (auto Initializer : Initializers) {
+      AddStmt(Initializer->getInit());
+    }
+
+    Inherited::VisitCXXConstructorDecl(D);
+  }
+
+  void VisitCXXConversionDecl(const CXXConversionDecl *D) {
+    AddQualType(D->getConversionType());
+    Hash.AddBoolean(D->isExplicitSpecified());
+    Inherited::VisitCXXConversionDecl(D);
+  }
+
+  void VisitFieldDecl(const FieldDecl *D) {
+    Hash.AddBoolean(D->isMutable());
+
+    const bool isBitField = D->isBitField();
+    Hash.AddBoolean(isBitField);
+    if (isBitField)
+      AddStmt(D->getBitWidth());
+
+    AddStmt(D->getInClassInitializer());
+
+    Inherited::VisitFieldDecl(D);
+  }
+
+  void VisitTemplateDecl(const TemplateDecl *D) {
+    AddDecl(D->getTemplatedDecl());
+
+    auto *Parameters = D->getTemplateParameters();
+    ID.AddInteger(Parameters->size());
+    for (auto *ND : *Parameters)
+      AddDecl(ND);
+
+    Inherited::VisitTemplateDecl(D);
+  }
+
+  void VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) {
+    Inherited::VisitFunctionTemplateDecl(D);
+  }
+
+  void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) {
+    const bool hasDefaultArgument = D->hasDefaultArgument();
+    Hash.AddBoolean(hasDefaultArgument);
+    if (hasDefaultArgument)
+      AddTemplateArgument(D->getDefaultArgument());
+
+    Inherited::VisitTemplateTypeParmDecl(D);
+  }
+
+  void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D) {
+    AddStmt(D->hasDefaultArgument() ? D->getDefaultArgument() : nullptr);
+
+    Inherited::VisitNonTypeTemplateParmDecl(D);
+  }
+
+  void VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl *D) {
+    const bool hasDefaultArgument = D->hasDefaultArgument();
+    Hash.AddBoolean(hasDefaultArgument);
+    if (hasDefaultArgument)
+      AddTemplateArgument(D->getDefaultArgument().getArgument());
+
+    Inherited::VisitTemplateTemplateParmDecl(D);
+  }
+
+  void VisitCXXRecordDecl(const CXXRecordDecl *D) {
+    const bool hasDefinition = D->hasDefinition();
+    Hash.AddBoolean(hasDefinition);
+    if (hasDefinition) {
+      ID.AddInteger(D->getODRHash());
+    }
+    Inherited::VisitCXXRecordDecl(D);
+  };
+};
+
+
+void ODRHash::AddSubDecl(const Decl *D) {
+  assert(D && "Expecting non-null pointer.");
+  AddDecl(D);
+  ODRDeclVisitor(ID, *this).Visit(D);
+}
+
+void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) {
+  assert(Record && Record->hasDefinition() &&
+         "Expected non-null record to be a definition.");
+  AddDecl(Record);
+
+  // Filter out sub-Decls which will not be processed in order to get an
+  // accurate count of Decl's.
+  llvm::SmallVector<const Decl *, 16> Decls;
+  for (const Decl *SubDecl : Record->decls()) {
+    // Ignore implicit Decl's.
+    if (SubDecl->isImplicit()) {
+      continue;
+    }
+    // Ignore Decl's that are not in the context of the CXXRecordDecl.
+    if (SubDecl->getDeclContext() != Record) {
+      continue;
+    }
+    Decls.push_back(SubDecl);
+  }
+
+  ID.AddInteger(Decls.size());
+  for (auto SubDecl : Decls) {
+    AddSubDecl(SubDecl);
+  }
+
+  ID.AddInteger(Record->getNumBases());
+  for (auto base : Record->bases()) {
+    AddBoolean(base.isVirtual());
+    AddQualType(base.getType());
+  }
+
+  const ClassTemplateDecl *TD = Record->getDescribedClassTemplate();
+  AddBoolean(TD);
+  if (TD) {
+    AddTemplateParameterList(TD->getTemplateParameters());
+  }
+}
+
+void ODRHash::AddDecl(const Decl *D) {
+  assert(D && "Expecting non-null pointer.");
+  auto Result = DeclMap.insert(std::make_pair(D, DeclMap.size()));
+  ID.AddInteger(Result.first->second);
+  // On first encounter of a Decl pointer, process it.  Every time afterwards,
+  // only the index value is needed.
+  if (!Result.second) {
+    return;
+  }
+
+  // Unlike the DeclVisitor, this adds a limited amount of information to
+  // identify the Decl.
+  ID.AddInteger(D->getKind());
+
+  // Unlike other places where AddBoolean is used with possibly null pointers,
+  // the nullness of the following pointers is already encoded with the
+  // DeclKind value, so there is no ambiguity on what information is added.
+  if (const auto *ND = dyn_cast<NamedDecl>(D)) {
+    AddDeclarationName(ND->getDeclName());
+  }
+
+  if (const auto *Typedef = dyn_cast<TypedefNameDecl>(D)) {
+    AddQualType(Typedef->getUnderlyingType());
+  }
+
+  if (const auto *Alias = dyn_cast<NamespaceAliasDecl>(D)) {
+    AddDecl(Alias->getNamespace());
+  }
+
+  if (const auto *Template = dyn_cast<TemplateDecl>(D)) {
+    AddTemplateParameterList(Template->getTemplateParameters());
+  }
+}
+
+// Process a Type pointer.  Add* methods call back into ODRHash while Visit*
+// methods process the relevant parts of the Type.
+class ODRTypeVisitor : public TypeVisitor<ODRTypeVisitor> {
+  typedef TypeVisitor<ODRTypeVisitor> Inherited;
+  llvm::FoldingSetNodeID &ID;
+  ODRHash &Hash;
+
+public:
+  ODRTypeVisitor(llvm::FoldingSetNodeID &ID, ODRHash &Hash)
+      : ID(ID), Hash(Hash) {}
+
+  void AddType(const Type *T) {
+    Hash.AddType(T);
+  }
+
+  void AddQualType(QualType T) {
+    Hash.AddQualType(T);
+  }
+
+  void AddDecl(Decl *D) {
+    Hash.AddBoolean(D);
+    if (D) {
+      Hash.AddDecl(D);
+    }
+  }
+
+  void AddTemplateArgument(TemplateArgument TA) {
+    Hash.AddTemplateArgument(TA);
+  }
+
+  void AddStmt(Stmt *S) {
+    Hash.AddBoolean(S);
+    if (S) {
+      Hash.AddStmt(S);
+    }
+  }
+
+  void AddNestedNameSpecifier(NestedNameSpecifier *NNS) {
+    Hash.AddBoolean(NNS);
+    if (NNS) {
+      Hash.AddNestedNameSpecifier(NNS);
+    }
+  }
+  void AddIdentiferInfo(const IdentifierInfo *II) {
+    Hash.AddBoolean(II);
+    if (II) {
+      Hash.AddIdentifierInfo(II);
+    }
+  }
+
+  void AddTemplateName(TemplateName TN) {
+    Hash.AddTemplateName(TN);
+  }
+
+  void VisitQualifiers(Qualifiers Quals) {
+    ID.AddInteger(Quals.getAsOpaqueValue());
+  }
+
+  void Visit(const Type *T) {
+    ID.AddInteger(T->getTypeClass());
+    Inherited::Visit(T);
+  }
+
+  void VisitType(const Type *T) {}
+
+  void VisitAdjustedType(const AdjustedType *T) {
+    AddQualType(T->getOriginalType());
+    AddQualType(T->getAdjustedType());
+    VisitType(T);
+  }
+
+  void VisitDecayedType(const DecayedType *T) {
+    AddQualType(T->getDecayedType());
+    AddQualType(T->getPointeeType());
+    VisitAdjustedType(T);
+  }
+
+  void VisitArrayType(const ArrayType *T) {
+    AddQualType(T->getElementType());
+    ID.AddInteger(T->getSizeModifier());
+    VisitQualifiers(T->getIndexTypeQualifiers());
+    VisitType(T);
+  }
+  void VisitConstantArrayType(const ConstantArrayType *T) {
+    T->getSize().Profile(ID);
+    VisitArrayType(T);
+  }
+
+  void VisitDependentSizedArrayType(const DependentSizedArrayType *T) {
+    AddStmt(T->getSizeExpr());
+    VisitArrayType(T);
+  }
+
+  void VisitIncompleteArrayType(const IncompleteArrayType *T) {
+    VisitArrayType(T);
+  }
+
+  void VisitVariableArrayType(const VariableArrayType *T) {
+    AddStmt(T->getSizeExpr());
+    VisitArrayType(T);
+  }
+
+  void VisitAtomicType(const AtomicType *T) {
+    AddQualType(T->getValueType());
+    VisitType(T);
+  }
+
+  void VisitAttributedType(const AttributedType *T) {
+    ID.AddInteger(T->getAttrKind());
+    AddQualType(T->getModifiedType());
+    AddQualType(T->getEquivalentType());
+    VisitType(T);
+  }
+
+  void VisitBlockPointerType(const BlockPointerType *T) {
+    AddQualType(T->getPointeeType());
+    VisitType(T);
+  }
+
+  void VisitBuiltinType(const BuiltinType *T) {
+    ID.AddInteger(T->getKind());
+    VisitType(T);
+  }
+
+  void VisitComplexType(const ComplexType *T) {
+    AddQualType(T->getElementType());
+    VisitType(T);
+  }
+
+  void VisitDecltypeType(const DecltypeType *T) {
+    AddQualType(T->getUnderlyingType());
+    AddStmt(T->getUnderlyingExpr());
+    VisitType(T);
+  }
+
+  void VisitDependentSizedExtVectorType(const DependentSizedExtVectorType *T) {
+    AddQualType(T->getElementType());
+    AddStmt(T->getSizeExpr());
+    VisitType(T);
+  }
+
+  void VisitFunctionType(const FunctionType *T) {
+    AddQualType(T->getReturnType());
+    T->getExtInfo().Profile(ID);
+    Hash.AddBoolean(T->isConst());
+    Hash.AddBoolean(T->isVolatile());
+    Hash.AddBoolean(T->isRestrict());
+    VisitType(T);
+  }
+
+  void VisitFunctionNoProtoType(const FunctionNoProtoType *T) {
+    VisitFunctionType(T);
+  }
+
+  void VisitFunctionProtoType(const FunctionProtoType *T) {
+    ID.AddInteger(T->getNumParams());
+    for (auto ParamType : T->getParamTypes())
+      AddQualType(ParamType);
+
+    const auto &epi = T->getExtProtoInfo();
+    ID.AddInteger(epi.Variadic);
+    ID.AddInteger(epi.TypeQuals);
+    ID.AddInteger(epi.RefQualifier);
+    ID.AddInteger(epi.ExceptionSpec.Type);
+
+    if (epi.ExceptionSpec.Type == EST_Dynamic) {
+      for (QualType Ex : epi.ExceptionSpec.Exceptions)
+        AddQualType(Ex);
+    } else if (epi.ExceptionSpec.Type == EST_ComputedNoexcept &&
+               epi.ExceptionSpec.NoexceptExpr) {
+      AddStmt(epi.ExceptionSpec.NoexceptExpr);
+    } else if (epi.ExceptionSpec.Type == EST_Uninstantiated ||
+               epi.ExceptionSpec.Type == EST_Unevaluated) {
+      AddDecl(epi.ExceptionSpec.SourceDecl->getCanonicalDecl());
+    }
+    if (epi.ExtParameterInfos) {
+      for (unsigned i = 0; i != T->getNumParams(); ++i)
+        ID.AddInteger(epi.ExtParameterInfos[i].getOpaqueValue());
+    }
+    epi.ExtInfo.Profile(ID);
+    Hash.AddBoolean(epi.HasTrailingReturn);
+
+    VisitFunctionType(T);
+  }
+
+  void VisitInjectedClassNameType(const InjectedClassNameType *T) {
+    AddDecl(T->getDecl());
+    VisitType(T);
+  }
+
+  void VisitMemberPointerType(const MemberPointerType *T) {
+    AddQualType(T->getPointeeType());
+    AddType(T->getClass());
+    VisitType(T);
+  }
+
+  void VisitObjCObjectPointerType(const ObjCObjectPointerType *T) {
+    AddQualType(T->getPointeeType());
+    VisitType(T);
+  }
+
+  void VisitObjCObjectType(const ObjCObjectType *T) {
+    QualType Base = T->getBaseType();
+    Hash.AddBoolean(Base.getTypePtr() != T);
+    if (Base.getTypePtr() != T)
+      AddQualType(Base);
+    auto TypeArgs = T->getTypeArgsAsWritten();
+    ID.AddInteger(TypeArgs.size());
+    for (auto TypeArg : TypeArgs)
+      AddQualType(TypeArg);
+    ID.AddInteger(T->getNumProtocols());
+    for (auto proto : T->quals())
+      AddDecl(proto);
+    ID.AddInteger(T->isKindOfTypeAsWritten());
+    VisitType(T);
+  }
+
+  void VisitObjCInterfaceType(const ObjCInterfaceType *T) {
+    VisitObjCObjectType(T);
+  }
+
+  void VisitObjCObjectTypeImpl(const ObjCObjectTypeImpl *T) {
+    VisitObjCObjectType(T);
+  }
+
+  void VisitPackExpansionType(const PackExpansionType *T) {
+    AddQualType(T->getPattern());
+    auto NumExpansions = T->getNumExpansions();
+    Hash.AddBoolean(NumExpansions.hasValue());
+    if (NumExpansions)
+      ID.AddInteger(*NumExpansions);
+    VisitType(T);
+  };
+
+  void VisitPointerType(const PointerType *T) {
+    AddQualType(T->getPointeeType());
+    VisitType(T);
+  }
+
+  void VisitReferenceType(const ReferenceType *T) {
+    AddQualType(T->getPointeeTypeAsWritten());
+    VisitType(T);
+  }
+
+  void VisitLValueReferenceType(const LValueReferenceType *T) {
+    VisitReferenceType(T);
+  }
+
+  void VisitRValueReferenceType(const RValueReferenceType *T) {
+    VisitReferenceType(T);
+  }
+
+  void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *T) {
+    AddQualType(T->getReplacementType());
+    AddType(T->getReplacedParameter());
+    VisitType(T);
+  }
+
+  void
+  VisitSubstTemplateTypeParmPackType(const SubstTemplateTypeParmPackType *T) {
+    AddType(T->getReplacedParameter());
+    AddTemplateArgument(T->getArgumentPack());
+    VisitType(T);
+  }
+
+  void VisitTagType(const TagType *T) {
+    AddDecl(T->getDecl());
+    Hash.AddBoolean(T->isBeingDefined());
+    VisitType(T);
+  }
+
+  void VisitEnumType(const EnumType *T) {
+    AddDecl(T->getDecl());
+    VisitTagType(T);
+  }
+
+  void VisitRecordType(const RecordType *T) {
+    AddDecl(T->getDecl());
+    VisitTagType(T);
+  }
+
+  void VisitTemplateSpecializationType(const TemplateSpecializationType *T) {
+    AddTemplateName(T->getTemplateName());
+    ID.AddInteger(T->getNumArgs());
+    for (auto I = T->begin(), E = T->end(); I != E; ++I)
+      AddTemplateArgument(*I);
+    VisitType(T);
+  }
+
+  void VisitTemplateTypeParmType(const TemplateTypeParmType *T) {
+    ID.AddInteger(T->getDepth());
+    ID.AddInteger(T->getIndex());
+    Hash.AddBoolean(T->isParameterPack());
+    AddDecl(T->getDecl());
+    VisitType(T);
+  }
+
+  void VisitTypedefType(const TypedefType *T) {
+    AddDecl(T->getDecl());
+    VisitType(T);
+  }
+
+  void VisitTypeOfExprType(const TypeOfExprType *T) {
+    AddStmt(T->getUnderlyingExpr());
+    VisitType(T);
+  }
+
+  void VisitDependentTypeOfExprType(const DependentTypeOfExprType *T) {
+    VisitTypeOfExprType(T);
+  }
+
+  void VisitTypeWithKeyword(const TypeWithKeyword *T) { VisitType(T); }
+
+  void VisitElaboratedType(const ElaboratedType *T) {
+    ID.AddInteger(T->getKeyword());
+    AddNestedNameSpecifier(T->getQualifier());
+    AddQualType(T->getNamedType());
+    VisitTypeWithKeyword(T);
+  }
+
+  void VisitUnaryTransformType(const UnaryTransformType *T) {
+    AddQualType(T->getBaseType());
+    ID.AddInteger(T->getUTTKind());
+    VisitType(T);
+  }
+
+  void VisitDependentUnaryTransformType(const DependentUnaryTransformType *T) {
+    VisitUnaryTransformType(T);
+  }
+
+  void VisitUnresolvedUsingType(const UnresolvedUsingType *T) {
+    AddDecl(T->getDecl());
+    VisitType(T);
+  }
+
+  void VisitVectorType(const VectorType *T) {
+    AddQualType(T->getElementType());
+    ID.AddInteger(T->getNumElements());
+    ID.AddInteger(T->getVectorKind());
+    VisitType(T);
+  }
+
+  void VisitExtVectorType(const ExtVectorType *T) { VisitVectorType(T); }
+};
+
+void ODRHash::AddType(const Type *T) {
+  assert(T && "Expecting non-null pointer.");
+  auto Result = TypeMap.insert(std::make_pair(T, TypeMap.size()));
+  ID.AddInteger(Result.first->second);
+  // On first encounter of a Type pointer, process it.  Every time afterwards,
+  // only the index value is needed.
+  if (!Result.second) {
+    return;
+  }
+
+  ODRTypeVisitor(ID, *this).Visit(T);
+}
+
+void ODRHash::AddQualType(QualType T) {
+  AddBoolean(T.isNull());
+  if (T.isNull())
+    return;
+  SplitQualType split = T.split();
+  ID.AddInteger(split.Quals.getAsOpaqueValue());
+  AddType(split.Ty);
+}
+
+void ODRHash::AddBoolean(bool Value) {
+  Bools.push_back(Value);
+}

Modified: cfe/trunk/lib/AST/StmtProfile.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/StmtProfile.cpp?rev=295284&r1=295283&r2=295284&view=diff
==============================================================================
--- cfe/trunk/lib/AST/StmtProfile.cpp (original)
+++ cfe/trunk/lib/AST/StmtProfile.cpp Wed Feb 15 22:53:40 2017
@@ -19,20 +19,22 @@
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/ExprObjC.h"
 #include "clang/AST/ExprOpenMP.h"
+#include "clang/AST/ODRHash.h"
 #include "clang/AST/StmtVisitor.h"
 #include "llvm/ADT/FoldingSet.h"
 using namespace clang;
 
 namespace {
   class StmtProfiler : public ConstStmtVisitor<StmtProfiler> {
+  protected:
     llvm::FoldingSetNodeID &ID;
-    const ASTContext &Context;
     bool Canonical;
 
   public:
-    StmtProfiler(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
-                 bool Canonical)
-      : ID(ID), Context(Context), Canonical(Canonical) { }
+    StmtProfiler(llvm::FoldingSetNodeID &ID, bool Canonical)
+        : ID(ID), Canonical(Canonical) {}
+
+    virtual ~StmtProfiler() {}
 
     void VisitStmt(const Stmt *S);
 
@@ -41,22 +43,25 @@ namespace {
 
     /// \brief Visit a declaration that is referenced within an expression
     /// or statement.
-    void VisitDecl(const Decl *D);
+    virtual void VisitDecl(const Decl *D) = 0;
 
     /// \brief Visit a type that is referenced within an expression or
     /// statement.
-    void VisitType(QualType T);
+    virtual void VisitType(QualType T) = 0;
 
     /// \brief Visit a name that occurs within an expression or statement.
-    void VisitName(DeclarationName Name);
+    virtual void VisitName(DeclarationName Name) = 0;
+
+    /// \brief Visit identifiers that are not in Decl's or Type's.
+    virtual void VisitIdentifierInfo(IdentifierInfo *II) = 0;
 
     /// \brief Visit a nested-name-specifier that occurs within an expression
     /// or statement.
-    void VisitNestedNameSpecifier(NestedNameSpecifier *NNS);
+    virtual void VisitNestedNameSpecifier(NestedNameSpecifier *NNS) = 0;
 
     /// \brief Visit a template name that occurs within an expression or
     /// statement.
-    void VisitTemplateName(TemplateName Name);
+    virtual void VisitTemplateName(TemplateName Name) = 0;
 
     /// \brief Visit template arguments that occur within an expression or
     /// statement.
@@ -66,6 +71,127 @@ namespace {
     /// \brief Visit a single template argument.
     void VisitTemplateArgument(const TemplateArgument &Arg);
   };
+
+  class StmtProfilerWithPointers : public StmtProfiler {
+    const ASTContext &Context;
+
+  public:
+    StmtProfilerWithPointers(llvm::FoldingSetNodeID &ID,
+                             const ASTContext &Context, bool Canonical)
+        : StmtProfiler(ID, Canonical), Context(Context) {}
+  private:
+    void VisitDecl(const Decl *D) override {
+      ID.AddInteger(D ? D->getKind() : 0);
+
+      if (Canonical && D) {
+        if (const NonTypeTemplateParmDecl *NTTP =
+                dyn_cast<NonTypeTemplateParmDecl>(D)) {
+          ID.AddInteger(NTTP->getDepth());
+          ID.AddInteger(NTTP->getIndex());
+          ID.AddBoolean(NTTP->isParameterPack());
+          VisitType(NTTP->getType());
+          return;
+        }
+
+        if (const ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D)) {
+          // The Itanium C++ ABI uses the type, scope depth, and scope
+          // index of a parameter when mangling expressions that involve
+          // function parameters, so we will use the parameter's type for
+          // establishing function parameter identity. That way, our
+          // definition of "equivalent" (per C++ [temp.over.link]) is at
+          // least as strong as the definition of "equivalent" used for
+          // name mangling.
+          VisitType(Parm->getType());
+          ID.AddInteger(Parm->getFunctionScopeDepth());
+          ID.AddInteger(Parm->getFunctionScopeIndex());
+          return;
+        }
+
+        if (const TemplateTypeParmDecl *TTP =
+                dyn_cast<TemplateTypeParmDecl>(D)) {
+          ID.AddInteger(TTP->getDepth());
+          ID.AddInteger(TTP->getIndex());
+          ID.AddBoolean(TTP->isParameterPack());
+          return;
+        }
+
+        if (const TemplateTemplateParmDecl *TTP =
+                dyn_cast<TemplateTemplateParmDecl>(D)) {
+          ID.AddInteger(TTP->getDepth());
+          ID.AddInteger(TTP->getIndex());
+          ID.AddBoolean(TTP->isParameterPack());
+          return;
+        }
+      }
+
+      ID.AddPointer(D ? D->getCanonicalDecl() : nullptr);
+    }
+
+    void VisitType(QualType T) override {
+      if (Canonical)
+        T = Context.getCanonicalType(T);
+
+      ID.AddPointer(T.getAsOpaquePtr());
+    }
+
+    void VisitName(DeclarationName Name) override {
+      ID.AddPointer(Name.getAsOpaquePtr());
+    }
+
+    void VisitIdentifierInfo(IdentifierInfo *II) override {
+      ID.AddPointer(II);
+    }
+
+    void VisitNestedNameSpecifier(NestedNameSpecifier *NNS) override {
+      if (Canonical)
+        NNS = Context.getCanonicalNestedNameSpecifier(NNS);
+      ID.AddPointer(NNS);
+    }
+
+    void VisitTemplateName(TemplateName Name) override {
+      if (Canonical)
+        Name = Context.getCanonicalTemplateName(Name);
+
+      Name.Profile(ID);
+    }
+  };
+
+  class StmtProfilerWithoutPointers : public StmtProfiler {
+    ODRHash &Hash;
+  public:
+    StmtProfilerWithoutPointers(llvm::FoldingSetNodeID &ID, ODRHash &Hash)
+        : StmtProfiler(ID, false), Hash(Hash) {}
+
+  private:
+    void VisitType(QualType T) override {
+      Hash.AddQualType(T);
+    }
+
+    void VisitName(DeclarationName Name) override {
+      Hash.AddDeclarationName(Name);
+    }
+    void VisitIdentifierInfo(IdentifierInfo *II) override {
+      ID.AddBoolean(II);
+      if (II) {
+        Hash.AddIdentifierInfo(II);
+      }
+    }
+    void VisitDecl(const Decl *D) override {
+      ID.AddBoolean(D);
+      if (D) {
+        Hash.AddDecl(D);
+      }
+    }
+    void VisitTemplateName(TemplateName Name) override {
+      Hash.AddTemplateName(Name);
+    }
+    void VisitNestedNameSpecifier(NestedNameSpecifier *NNS) override {
+      ID.AddBoolean(NNS);
+      if (NNS) {
+        Hash.AddNestedNameSpecifier(NNS);
+      }
+    }
+  };
 }
 
 void StmtProfiler::VisitStmt(const Stmt *S) {
@@ -853,7 +979,7 @@ void StmtProfiler::VisitOffsetOfExpr(con
       break;
 
     case OffsetOfNode::Identifier:
-      ID.AddPointer(ON.getFieldName());
+      VisitIdentifierInfo(ON.getFieldName());
       break;
 
     case OffsetOfNode::Base:
@@ -861,7 +987,7 @@ void StmtProfiler::VisitOffsetOfExpr(con
       break;
     }
   }
-  
+
   VisitExpr(S);
 }
 
@@ -1451,7 +1577,7 @@ StmtProfiler::VisitCXXPseudoDestructorEx
   if (S->getDestroyedTypeInfo())
     VisitType(S->getDestroyedType());
   else
-    ID.AddPointer(S->getDestroyedTypeIdentifier());
+    VisitIdentifierInfo(S->getDestroyedTypeIdentifier());
 }
 
 void StmtProfiler::VisitOverloadExpr(const OverloadExpr *S) {
@@ -1701,77 +1827,6 @@ void StmtProfiler::VisitObjCAvailability
   VisitExpr(S);
 }
 
-void StmtProfiler::VisitDecl(const Decl *D) {
-  ID.AddInteger(D? D->getKind() : 0);
-
-  if (Canonical && D) {
-    if (const NonTypeTemplateParmDecl *NTTP =
-          dyn_cast<NonTypeTemplateParmDecl>(D)) {
-      ID.AddInteger(NTTP->getDepth());
-      ID.AddInteger(NTTP->getIndex());
-      ID.AddBoolean(NTTP->isParameterPack());
-      VisitType(NTTP->getType());
-      return;
-    }
-
-    if (const ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D)) {
-      // The Itanium C++ ABI uses the type, scope depth, and scope
-      // index of a parameter when mangling expressions that involve
-      // function parameters, so we will use the parameter's type for
-      // establishing function parameter identity. That way, our
-      // definition of "equivalent" (per C++ [temp.over.link]) is at
-      // least as strong as the definition of "equivalent" used for
-      // name mangling.
-      VisitType(Parm->getType());
-      ID.AddInteger(Parm->getFunctionScopeDepth());
-      ID.AddInteger(Parm->getFunctionScopeIndex());
-      return;
-    }
-
-    if (const TemplateTypeParmDecl *TTP =
-          dyn_cast<TemplateTypeParmDecl>(D)) {
-      ID.AddInteger(TTP->getDepth());
-      ID.AddInteger(TTP->getIndex());
-      ID.AddBoolean(TTP->isParameterPack());
-      return;
-    }
-
-    if (const TemplateTemplateParmDecl *TTP =
-          dyn_cast<TemplateTemplateParmDecl>(D)) {
-      ID.AddInteger(TTP->getDepth());
-      ID.AddInteger(TTP->getIndex());
-      ID.AddBoolean(TTP->isParameterPack());
-      return;
-    }
-  }
-
-  ID.AddPointer(D? D->getCanonicalDecl() : nullptr);
-}
-
-void StmtProfiler::VisitType(QualType T) {
-  if (Canonical)
-    T = Context.getCanonicalType(T);
-
-  ID.AddPointer(T.getAsOpaquePtr());
-}
-
-void StmtProfiler::VisitName(DeclarationName Name) {
-  ID.AddPointer(Name.getAsOpaquePtr());
-}
-
-void StmtProfiler::VisitNestedNameSpecifier(NestedNameSpecifier *NNS) {
-  if (Canonical)
-    NNS = Context.getCanonicalNestedNameSpecifier(NNS);
-  ID.AddPointer(NNS);
-}
-
-void StmtProfiler::VisitTemplateName(TemplateName Name) {
-  if (Canonical)
-    Name = Context.getCanonicalTemplateName(Name);
-
-  Name.Profile(ID);
-}
-
 void StmtProfiler::VisitTemplateArguments(const TemplateArgumentLoc *Args,
                                           unsigned NumArgs) {
   ID.AddInteger(NumArgs);
@@ -1821,6 +1876,12 @@ void StmtProfiler::VisitTemplateArgument
 
 void Stmt::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
                    bool Canonical) const {
-  StmtProfiler Profiler(ID, Context, Canonical);
+  StmtProfilerWithPointers Profiler(ID, Context, Canonical);
+  Profiler.Visit(this);
+}
+
+void Stmt::ProcessODRHash(llvm::FoldingSetNodeID &ID,
+                          class ODRHash &Hash) const {
+  StmtProfilerWithoutPointers Profiler(ID, Hash);
   Profiler.Visit(this);
 }

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=295284&r1=295283&r2=295284&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Wed Feb 15 22:53:40 2017
@@ -13775,8 +13775,11 @@ void Sema::ActOnTagFinishDefinition(Scop
       RD->completeDefinition();
   }
 
-  if (isa<CXXRecordDecl>(Tag))
+  if (auto *RD = dyn_cast<CXXRecordDecl>(Tag)) {
     FieldCollector->FinishClass();
+    if (Context.getLangOpts().Modules)
+      RD->computeODRHash();
+  }
 
   // Exit this scope of this tag's definition.
   PopDeclContext();

Modified: cfe/trunk/lib/Serialization/ASTReader.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReader.cpp?rev=295284&r1=295283&r2=295284&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReader.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReader.cpp Wed Feb 15 22:53:40 2017
@@ -26,6 +26,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/NestedNameSpecifier.h"
+#include "clang/AST/ODRHash.h"
 #include "clang/AST/RawCommentList.h"
 #include "clang/AST/Type.h"
 #include "clang/AST/TypeLocVisitor.h"
@@ -8895,21 +8896,638 @@ void ASTReader::diagnoseOdrViolations()
     for (auto *RD : Merge.second) {
       // Multiple different declarations got merged together; tell the user
       // where they came from.
-      if (Merge.first != RD) {
-        // FIXME: Walk the definition, figure out what's different,
-        // and diagnose that.
-        if (!Diagnosed) {
-          std::string Module = getOwningModuleNameForDiagnostic(Merge.first);
-          Diag(Merge.first->getLocation(),
-               diag::err_module_odr_violation_different_definitions)
-            << Merge.first << Module.empty() << Module;
-          Diagnosed = true;
+      if (Merge.first == RD)
+        continue;
+
+      llvm::SmallVector<std::pair<Decl *, unsigned>, 4> FirstHashes;
+      llvm::SmallVector<std::pair<Decl *, unsigned>, 4> SecondHashes;
+      ODRHash Hash;
+      for (auto D : Merge.first->decls()) {
+        if (D->isImplicit())
+          continue;
+        Hash.clear();
+        Hash.AddSubDecl(D);
+        FirstHashes.emplace_back(D, Hash.CalculateHash());
+      }
+      for (auto D : RD->decls()) {
+        if (D->isImplicit())
+          continue;
+        Hash.clear();
+        Hash.AddSubDecl(D);
+        SecondHashes.emplace_back(D, Hash.CalculateHash());
+      }
+
+      // Used with err_module_odr_violation_mismatch_decl and
+      // note_module_odr_violation_mismatch_decl
+      enum {
+        EndOfClass,
+        PublicSpecifer,
+        PrivateSpecifer,
+        ProtectedSpecifer,
+        Friend,
+        Enum,
+        StaticAssert,
+        Typedef,
+        TypeAlias,
+        CXXMethod,
+        CXXConstructor,
+        CXXDestructor,
+        CXXConversion,
+        Field,
+        Other
+      } FirstDiffType = Other,
+        SecondDiffType = Other;
+
+      auto DifferenceSelector = [](Decl *D) {
+        assert(D && "valid Decl required");
+        switch (D->getKind()) {
+        default:
+          return Other;
+        case Decl::AccessSpec:
+          switch (D->getAccess()) {
+          case AS_public:
+            return PublicSpecifer;
+          case AS_private:
+            return PrivateSpecifer;
+          case AS_protected:
+            return ProtectedSpecifer;
+          case AS_none:
+            llvm_unreachable("Invalid access specifier");
+          }
+        case Decl::Friend:
+          return Friend;
+        case Decl::Enum:
+          return Enum;
+        case Decl::StaticAssert:
+          return StaticAssert;
+        case Decl::Typedef:
+          return Typedef;
+        case Decl::TypeAlias:
+          return TypeAlias;
+        case Decl::CXXMethod:
+          return CXXMethod;
+        case Decl::CXXConstructor:
+          return CXXConstructor;
+        case Decl::CXXDestructor:
+          return CXXDestructor;
+        case Decl::CXXConversion:
+          return CXXConversion;
+        case Decl::Field:
+          return Field;
         }
+      };
+      Decl *FirstDecl = nullptr;
+      Decl *SecondDecl = nullptr;
+      auto FirstIt = FirstHashes.begin();
+      auto SecondIt = SecondHashes.begin();
+
+      // If there is a diagnoseable difference, FirstDiffType and
+      // SecondDiffType will not be Other and FirstDecl and SecondDecl will be
+      // filled in if not EndOfClass.
+      while (FirstIt != FirstHashes.end() || SecondIt != SecondHashes.end()) {
+        if (FirstIt->second == SecondIt->second) {
+          ++FirstIt;
+          ++SecondIt;
+          continue;
+        }
+
+        FirstDecl = FirstIt == FirstHashes.end() ? nullptr : FirstIt->first;
+        SecondDecl = SecondIt == SecondHashes.end() ? nullptr : SecondIt->first;
+
+        FirstDiffType = FirstDecl ? DifferenceSelector(FirstDecl) : EndOfClass;
+        SecondDiffType =
+            SecondDecl ? DifferenceSelector(SecondDecl) : EndOfClass;
+
+        break;
+      }
+
+      if (FirstDiffType == Other || SecondDiffType == Other) {
+        // Reaching this point means an unexpected Decl was encountered
+        // or no difference was detected.  This causes a generic error
+        // message to be emitted.
+        std::string Module = getOwningModuleNameForDiagnostic(Merge.first);
+        Diag(Merge.first->getLocation(),
+             diag::err_module_odr_violation_different_definitions)
+            << Merge.first << Module.empty() << Module;
+
+
 
         Diag(RD->getLocation(),
              diag::note_module_odr_violation_different_definitions)
-          << getOwningModuleNameForDiagnostic(RD);
+            << getOwningModuleNameForDiagnostic(RD);
+        Diagnosed = true;
+        break;
+      }
+
+      std::string FirstModule = getOwningModuleNameForDiagnostic(Merge.first);
+      std::string SecondModule = getOwningModuleNameForDiagnostic(RD);
+
+      if (FirstDiffType != SecondDiffType) {
+        SourceLocation FirstLoc;
+        SourceRange FirstRange;
+        if (FirstDiffType == EndOfClass) {
+          FirstLoc = Merge.first->getBraceRange().getEnd();
+        } else {
+          FirstLoc = FirstIt->first->getLocation();
+          FirstRange = FirstIt->first->getSourceRange();
+        }
+        Diag(FirstLoc, diag::err_module_odr_violation_mismatch_decl)
+            << Merge.first << FirstModule.empty() << FirstModule << FirstRange
+            << FirstDiffType;
+
+        SourceLocation SecondLoc;
+        SourceRange SecondRange;
+        if (SecondDiffType == EndOfClass) {
+          SecondLoc = RD->getBraceRange().getEnd();
+        } else {
+          SecondLoc = SecondDecl->getLocation();
+          SecondRange = SecondDecl->getSourceRange();
+        }
+        Diag(SecondLoc, diag::note_module_odr_violation_mismatch_decl)
+            << SecondModule << SecondRange << SecondDiffType;
+        Diagnosed = true;
+        break;
+      }
+
+      // Used with err_module_odr_violation_mismatch_decl_diff and
+      // note_module_odr_violation_mismatch_decl_diff
+      enum ODRDeclDifference{
+        FriendName,
+        EnumName,
+        EnumConstantName,
+        EnumConstantInit,
+        EnumConstantNoInit,
+        EnumConstantDiffInit,
+        EnumNumberOfConstants,
+        StaticAssertCondition,
+        StaticAssertMessage,
+        StaticAssertOnlyMessage,
+        TypedefName,
+        MethodName,
+        MethodStatic,
+        MethodInline,
+        MethodConst,
+        MethodNumParams,
+        MethodParamName,
+        MethodParamType,
+        MethodDefaultArg,
+        MethodOnlyDefaultArg,
+        MethodOnlyBody,
+        MethodBody,
+        FieldName,
+        FieldSingleBitField,
+        FieldMutable,
+      };
+
+      // These lambdas have the common portions of the ODR diagnostics.  This
+      // has the same return as Diag(), so addition parameters can be passed
+      // in with operator<<
+      auto ODRDiagError = [&Merge, &FirstModule, this](
+          SourceLocation Loc, SourceRange Range, ODRDeclDifference DiffType) {
+        return Diag(Loc, diag::err_module_odr_violation_mismatch_decl_diff)
+               << Merge.first << FirstModule.empty() << FirstModule << Range
+               << DiffType;
+      };
+      auto ODRDiagNote = [&SecondModule, this](
+          SourceLocation Loc, SourceRange Range, ODRDeclDifference DiffType) {
+        return Diag(Loc, diag::note_module_odr_violation_mismatch_decl_diff)
+               << SecondModule << Range << DiffType;
+      };
+
+      auto ComputeODRHash = [&Hash](const Stmt* S) {
+        assert(S);
+        Hash.clear();
+        Hash.AddStmt(S);
+        return Hash.CalculateHash();
+      };
+
+      // At this point, both decls are of the same type.  Dive down deeper into
+      // the Decl to determine where the first difference is located.
+      switch (FirstDiffType) {
+      case Friend: {
+        FriendDecl *FirstFriend = cast<FriendDecl>(FirstDecl);
+        FriendDecl *SecondFriend = cast<FriendDecl>(SecondDecl);
+        {
+          auto D = ODRDiagError(FirstFriend->getFriendLoc(),
+                                FirstFriend->getSourceRange(), FriendName);
+          if (TypeSourceInfo *FirstTSI = FirstFriend->getFriendType())
+            D << FirstTSI->getType();
+          else
+            D << FirstFriend->getFriendDecl();
+        }
+        {
+          auto D = ODRDiagNote(SecondFriend->getFriendLoc(),
+                               SecondFriend->getSourceRange(), FriendName);
+          if (TypeSourceInfo *SecondTSI = SecondFriend->getFriendType())
+            D << SecondTSI->getType();
+          else
+            D << SecondFriend->getFriendDecl();
+        }
+        Diagnosed = true;
+        break;
+      }
+      case Enum: {
+        EnumDecl *FirstEnum = cast<EnumDecl>(FirstDecl);
+        EnumDecl *SecondEnum = cast<EnumDecl>(SecondDecl);
+        if (FirstEnum->getName() != SecondEnum->getName()) {
+          ODRDiagError(FirstEnum->getLocStart(), FirstEnum->getSourceRange(),
+                       EnumName)
+              << FirstEnum;
+          ODRDiagNote(SecondEnum->getLocStart(), SecondEnum->getSourceRange(),
+                      EnumName)
+              << SecondEnum;
+          Diagnosed = true;
+          break;
+        }
+
+        // Don't use EnumDecl::enumerator_{begin,end}.  Decl merging can
+        // cause the iterators from them to be the same for both Decl's.
+        EnumDecl::enumerator_iterator FirstEnumIt(FirstEnum->decls_begin());
+        EnumDecl::enumerator_iterator FirstEnumEnd(FirstEnum->decls_end());
+        EnumDecl::enumerator_iterator SecondEnumIt(SecondEnum->decls_begin());
+        EnumDecl::enumerator_iterator SecondEnumEnd(SecondEnum->decls_end());
+        int NumElements = 0;
+        for (; FirstEnumIt != FirstEnumEnd && SecondEnumIt != SecondEnumEnd;
+             ++FirstEnumIt, ++SecondEnumIt, ++NumElements) {
+          if (FirstEnumIt->getName() != SecondEnumIt->getName()) {
+            ODRDiagError(FirstEnumIt->getLocStart(),
+                         FirstEnumIt->getSourceRange(), EnumConstantName)
+                << *FirstEnumIt << FirstEnum;
+            ODRDiagNote(SecondEnumIt->getLocStart(),
+                        SecondEnumIt->getSourceRange(), EnumConstantName)
+                << *SecondEnumIt << SecondEnum;
+            Diagnosed = true;
+            break;
+          }
+          Expr *FirstInit = FirstEnumIt->getInitExpr();
+          Expr *SecondInit = SecondEnumIt->getInitExpr();
+
+          if (FirstInit && !SecondInit) {
+            ODRDiagError(FirstEnumIt->getLocStart(),
+                         FirstEnumIt->getSourceRange(), EnumConstantInit)
+                << *FirstEnumIt << FirstEnum;
+
+            ODRDiagNote(SecondEnumIt->getLocStart(),
+                        SecondEnumIt->getSourceRange(), EnumConstantNoInit)
+                << *SecondEnumIt << SecondEnum;
+            Diagnosed = true;
+            break;
+          }
+
+          if (!FirstInit && SecondInit) {
+            ODRDiagError(FirstEnumIt->getLocStart(),
+                         FirstEnumIt->getSourceRange(), EnumConstantNoInit)
+                << *FirstEnumIt << FirstEnum;
+            ODRDiagNote(SecondEnumIt->getLocStart(),
+                        SecondEnumIt->getSourceRange(), EnumConstantInit)
+                << *SecondEnumIt << SecondEnum;
+            Diagnosed = true;
+            break;
+          }
+
+          if (FirstInit == SecondInit)
+            continue;
+
+          unsigned FirstODRHash = ComputeODRHash(FirstInit);
+          unsigned SecondODRHash = ComputeODRHash(SecondInit);
+
+          if (FirstODRHash != SecondODRHash) {
+            ODRDiagError(FirstEnumIt->getLocStart(),
+                         FirstEnumIt->getSourceRange(), EnumConstantDiffInit)
+                << *FirstEnumIt << FirstEnum;
+            ODRDiagNote(SecondEnumIt->getLocStart(),
+                        SecondEnumIt->getSourceRange(), EnumConstantDiffInit)
+                << *SecondEnumIt << SecondEnum;
+            Diagnosed = true;
+            break;
+          }
+        }
+
+        if (FirstEnumIt == FirstEnumEnd && SecondEnumIt != SecondEnumEnd) {
+          unsigned FirstEnumSize = NumElements;
+          unsigned SecondEnumSize = NumElements;
+          for (; SecondEnumIt != SecondEnumEnd; ++SecondEnumIt)
+            ++SecondEnumSize;
+          ODRDiagError(FirstEnum->getLocStart(), FirstEnum->getSourceRange(),
+                       EnumNumberOfConstants)
+              << FirstEnum << FirstEnumSize;
+          ODRDiagNote(SecondEnum->getLocStart(), SecondEnum->getSourceRange(),
+                      EnumNumberOfConstants)
+              << SecondEnum << SecondEnumSize;
+          Diagnosed = true;
+          break;
+        }
+
+        if (FirstEnumIt != FirstEnumEnd && SecondEnumIt == SecondEnumEnd) {
+          unsigned FirstEnumSize = NumElements;
+          unsigned SecondEnumSize = NumElements;
+          for (; FirstEnumIt != FirstEnumEnd; ++FirstEnumIt)
+            ++FirstEnumSize;
+          ODRDiagError(FirstEnum->getLocStart(), FirstEnum->getSourceRange(),
+                       EnumNumberOfConstants)
+              << FirstEnum << FirstEnumSize;
+          ODRDiagNote(SecondEnum->getLocStart(), SecondEnum->getSourceRange(),
+                      EnumNumberOfConstants)
+              << SecondEnum << SecondEnumSize;
+          Diagnosed = true;
+          break;
+        }
+
+        break;
+      }
+      case StaticAssert: {
+        StaticAssertDecl *FirstSA = cast<StaticAssertDecl>(FirstDecl);
+        StaticAssertDecl *SecondSA = cast<StaticAssertDecl>(SecondDecl);
+
+        Expr *FirstExpr = FirstSA->getAssertExpr();
+        Expr *SecondExpr = SecondSA->getAssertExpr();
+        unsigned FirstODRHash = ComputeODRHash(FirstExpr);
+        unsigned SecondODRHash = ComputeODRHash(SecondExpr);
+        if (FirstODRHash != SecondODRHash) {
+          ODRDiagError(FirstExpr->getLocStart(), FirstExpr->getSourceRange(),
+                       StaticAssertCondition);
+          ODRDiagNote(SecondExpr->getLocStart(),
+                      SecondExpr->getSourceRange(), StaticAssertCondition);
+          Diagnosed = true;
+          break;
+        }
+
+        StringLiteral *FirstStr = FirstSA->getMessage();
+        StringLiteral *SecondStr = SecondSA->getMessage();
+        if ((FirstStr && !SecondStr) || (!FirstStr && SecondStr)) {
+          SourceLocation FirstLoc, SecondLoc;
+          SourceRange FirstRange, SecondRange;
+          if (FirstStr) {
+            FirstLoc = FirstStr->getLocStart();
+            FirstRange = FirstStr->getSourceRange();
+          } else {
+            FirstLoc = FirstSA->getLocStart();
+            FirstRange = FirstSA->getSourceRange();
+          }
+          if (SecondStr) {
+            SecondLoc = SecondStr->getLocStart();
+            SecondRange = SecondStr->getSourceRange();
+          } else {
+            SecondLoc = SecondSA->getLocStart();
+            SecondRange = SecondSA->getSourceRange();
+          }
+          ODRDiagError(FirstLoc, FirstRange, StaticAssertOnlyMessage)
+              << (FirstStr == nullptr);
+          ODRDiagNote(SecondLoc, SecondRange, StaticAssertOnlyMessage)
+              << (SecondStr == nullptr);
+          Diagnosed = true;
+          break;
+        }
+
+        if (FirstStr && SecondStr &&
+            FirstStr->getString() != SecondStr->getString()) {
+          ODRDiagError(FirstStr->getLocStart(), FirstStr->getSourceRange(),
+                       StaticAssertMessage);
+          ODRDiagNote(SecondStr->getLocStart(), SecondStr->getSourceRange(),
+                      StaticAssertMessage);
+          Diagnosed = true;
+          break;
+        }
+        break;
+      }
+      case Typedef:
+      case TypeAlias: {
+        TypedefNameDecl *FirstTD = cast<TypedefNameDecl>(FirstDecl);
+        TypedefNameDecl *SecondTD = cast<TypedefNameDecl>(SecondDecl);
+        IdentifierInfo *FirstII = FirstTD->getIdentifier();
+        IdentifierInfo *SecondII = SecondTD->getIdentifier();
+        if (FirstII && SecondII && FirstII->getName() != SecondII->getName()) {
+          ODRDiagError(FirstTD->getLocation(), FirstTD->getSourceRange(),
+                       TypedefName)
+              << (FirstDiffType == TypeAlias) << FirstII;
+          ODRDiagNote(SecondTD->getLocation(), SecondTD->getSourceRange(),
+                      TypedefName)
+              << (FirstDiffType == TypeAlias) << SecondII;
+          Diagnosed = true;
+          break;
+        }
+        break;
+      }
+      case CXXMethod:
+      case CXXConstructor:
+      case CXXConversion:
+      case CXXDestructor: {
+        // TODO: Merge with existing method diff logic.
+        CXXMethodDecl *FirstMD = cast<CXXMethodDecl>(FirstDecl);
+        CXXMethodDecl *SecondMD = cast<CXXMethodDecl>(SecondDecl);
+        IdentifierInfo *FirstII = FirstMD->getIdentifier();
+        IdentifierInfo *SecondII = SecondMD->getIdentifier();
+        if (FirstII && SecondII && FirstII->getName() != SecondII->getName()) {
+          ODRDiagError(FirstMD->getLocation(), FirstMD->getSourceRange(),
+                       MethodName)
+              << FirstII;
+          ODRDiagNote(SecondMD->getLocation(), SecondMD->getSourceRange(),
+                      MethodName)
+              << SecondII;
+          Diagnosed = true;
+          break;
+        }
+
+        bool FirstStatic = FirstMD->getStorageClass() == SC_Static;
+        bool SecondStatic = SecondMD->getStorageClass() == SC_Static;
+        if (FirstStatic != SecondStatic) {
+          ODRDiagError(FirstMD->getLocation(), FirstMD->getSourceRange(),
+                       MethodStatic)
+              << FirstMD << FirstStatic;
+          ODRDiagNote(SecondMD->getLocation(), SecondMD->getSourceRange(),
+                      MethodStatic)
+              << SecondMD << SecondStatic;
+          Diagnosed = true;
+          break;
+        }
+
+        bool FirstInline = FirstMD->isInlineSpecified();
+        bool SecondInline = SecondMD->isInlineSpecified();
+        if (FirstInline != SecondInline) {
+          ODRDiagError(FirstMD->getLocation(), FirstMD->getSourceRange(),
+                       MethodInline)
+              << FirstMD << FirstInline;
+          ODRDiagNote(SecondMD->getLocation(), SecondMD->getSourceRange(),
+                      MethodInline)
+              << SecondMD << SecondInline;
+          Diagnosed = true;
+          break;
+        }
+
+        bool FirstConst = FirstMD->isConst();
+        bool SecondConst = SecondMD->isConst();
+        if (FirstConst != SecondConst) {
+          ODRDiagError(FirstMD->getLocation(), FirstMD->getSourceRange(),
+                       MethodConst)
+              << FirstMD << FirstInline;
+          ODRDiagNote(SecondMD->getLocation(), SecondMD->getSourceRange(),
+                      MethodConst)
+              << SecondMD << SecondInline;
+          Diagnosed = true;
+          break;
+        }
+
+        if (FirstMD->getNumParams() != SecondMD->getNumParams()) {
+          ODRDiagError(FirstMD->getLocation(), FirstMD->getSourceRange(),
+                       MethodNumParams)
+              << SecondMD << FirstMD->getNumParams();
+          ODRDiagNote(SecondMD->getLocation(), SecondMD->getSourceRange(),
+                      MethodNumParams)
+              << SecondMD << SecondMD->getNumParams();
+          Diagnosed = true;
+          break;
+        }
+
+        for (unsigned i = 0, e = FirstMD->getNumParams(); i < e; ++i) {
+          ParmVarDecl *FirstParam = FirstMD->getParamDecl(i);
+          ParmVarDecl *SecondParam = SecondMD->getParamDecl(i);
+          IdentifierInfo *FirstII = FirstParam->getIdentifier();
+          IdentifierInfo *SecondII = SecondParam->getIdentifier();
+          if ((!FirstII && SecondII) || (FirstII && !SecondII) ||
+              (FirstII && SecondII &&
+               FirstII->getName() != SecondII->getName())) {
+            ODRDiagError(FirstParam->getLocation(),
+                         FirstParam->getSourceRange(), MethodParamName)
+                << SecondMD << i + 1 << (FirstII == nullptr) << FirstII;
+            ODRDiagNote(SecondParam->getLocation(),
+                        SecondParam->getSourceRange(), MethodParamName)
+                << SecondMD << i + 1 << (SecondII == nullptr) << SecondII;
+            Diagnosed = true;
+            break;
+          }
+
+          if (FirstParam->getType() != SecondParam->getType()) {
+            ODRDiagError(FirstParam->getLocation(),
+                         FirstParam->getSourceRange(), MethodParamType)
+                << SecondMD << i + 1 << FirstParam->getType();
+            ODRDiagNote(SecondParam->getLocation(),
+                        SecondParam->getSourceRange(), MethodParamType)
+                << SecondMD << i + 1 << SecondParam->getType();
+            Diagnosed = true;
+            break;
+          }
+
+          Expr *FirstDefaultArg = FirstParam->getDefaultArg();
+          Expr *SecondDefaultArg = SecondParam->getDefaultArg();
+          if ((!FirstDefaultArg && SecondDefaultArg) ||
+              (FirstDefaultArg && !SecondDefaultArg)) {
+            ODRDiagError(FirstParam->getLocation(),
+                         FirstParam->getSourceRange(), MethodOnlyDefaultArg)
+                << SecondMD << i + 1 << (FirstDefaultArg != nullptr);
+            ODRDiagNote(SecondParam->getLocation(),
+                        SecondParam->getSourceRange(), MethodOnlyDefaultArg)
+                << SecondMD << i + 1 << (SecondDefaultArg != nullptr);
+            Diagnosed = true;
+            break;
+          }
+
+          if (FirstDefaultArg && SecondDefaultArg) {
+            unsigned FirstODRHash = ComputeODRHash(FirstDefaultArg);
+            unsigned SecondODRHash = ComputeODRHash(SecondDefaultArg);
+            if (FirstODRHash != SecondODRHash) {
+              ODRDiagError(FirstParam->getLocation(),
+                           FirstParam->getSourceRange(), MethodDefaultArg)
+                  << SecondMD << i + 1;
+              ODRDiagNote(SecondParam->getLocation(),
+                          SecondParam->getSourceRange(), MethodDefaultArg)
+                  << SecondMD << i + 1;
+              Diagnosed = true;
+              break;
+            }
+          }
+        }
+
+        // TODO: Figure out how to diagnose different function bodies.
+        // Deserialization does not import the second function body.
+
+        break;
       }
+      case Field: {
+        // TODO: Merge with exising field diff logic.
+        FieldDecl *FirstField = cast<FieldDecl>(FirstDecl);
+        FieldDecl *SecondField = cast<FieldDecl>(SecondDecl);
+        IdentifierInfo *FirstII = FirstField->getIdentifier();
+        IdentifierInfo *SecondII = SecondField->getIdentifier();
+        if (FirstII->getName() != SecondII->getName()) {
+          ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(),
+                       FieldName)
+              << FirstII;
+          ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(),
+                      FieldName)
+              << SecondII;
+          Diagnosed = true;
+          break;
+        }
+
+        // This case is handled elsewhere.
+        if (FirstField->getType() != SecondField->getType()) {
+          break;
+        }
+
+        bool FirstBitField = FirstField->isBitField();
+        bool SecondBitField = SecondField->isBitField();
+        if (FirstBitField != SecondBitField) {
+          ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(),
+                       FieldSingleBitField)
+              << FirstII << FirstBitField;
+          ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(),
+                      FieldSingleBitField)
+              << SecondII << SecondBitField;
+          Diagnosed = true;
+          break;
+        }
+
+        if (FirstBitField && SecondBitField) {
+          Expr* FirstWidth = FirstField->getBitWidth();
+          Expr *SecondWidth = SecondField->getBitWidth();
+          unsigned FirstODRHash = ComputeODRHash(FirstWidth);
+          unsigned SecondODRHash = ComputeODRHash(SecondWidth);
+          if (FirstODRHash != SecondODRHash) {
+            ODRDiagError(FirstField->getLocation(),
+                         FirstField->getSourceRange(), FieldSingleBitField)
+                << FirstII << FirstBitField;
+            ODRDiagNote(SecondField->getLocation(),
+                        SecondField->getSourceRange(), FieldSingleBitField)
+                << SecondII << SecondBitField;
+            Diagnosed = true;
+            break;
+          }
+        }
+
+        bool FirstMutable = FirstField->isMutable();
+        bool SecondMutable = SecondField->isMutable();
+        if (FirstMutable != SecondMutable) {
+          ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(),
+                       FieldMutable)
+              << FirstII << FirstMutable;
+          ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(),
+                      FieldMutable)
+              << SecondII << SecondMutable;
+          Diagnosed = true;
+          break;
+        }
+        break;
+      }
+      case Other:
+      case EndOfClass:
+      case PublicSpecifer:
+      case PrivateSpecifer:
+      case ProtectedSpecifer:
+        llvm_unreachable("Invalid diff type");
+      }
+
+      if (Diagnosed == true)
+        continue;
+
+      // Unable to find difference in Decl's, print simple different
+      // definitions diagnostic.
+      Diag(Merge.first->getLocation(),
+           diag::err_module_odr_violation_different_definitions)
+          << Merge.first << FirstModule.empty() << FirstModule;
+      Diag(RD->getLocation(),
+           diag::note_module_odr_violation_different_definitions)
+          << SecondModule;
+      Diagnosed = true;
     }
 
     if (!Diagnosed) {

Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=295284&r1=295283&r2=295284&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Wed Feb 15 22:53:40 2017
@@ -1527,6 +1527,7 @@ void ASTDeclReader::ReadCXXDefinitionDat
   Data.ImplicitCopyAssignmentHasConstParam = Record.readInt();
   Data.HasDeclaredCopyConstructorWithConstParam = Record.readInt();
   Data.HasDeclaredCopyAssignmentWithConstParam = Record.readInt();
+  Data.ODRHash = Record.readInt();
 
   Data.NumBases = Record.readInt();
   if (Data.NumBases)
@@ -1657,6 +1658,7 @@ void ASTDeclReader::MergeDefinitionData(
   OR_FIELD(HasDeclaredCopyConstructorWithConstParam)
   OR_FIELD(HasDeclaredCopyAssignmentWithConstParam)
   MATCH_FIELD(IsLambda)
+  MATCH_FIELD(ODRHash)
 #undef OR_FIELD
 #undef MATCH_FIELD
 

Modified: cfe/trunk/lib/Serialization/ASTWriter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriter.cpp?rev=295284&r1=295283&r2=295284&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTWriter.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTWriter.cpp Wed Feb 15 22:53:40 2017
@@ -5707,6 +5707,7 @@ void ASTRecordWriter::AddCXXDefinitionDa
   Record->push_back(Data.ImplicitCopyAssignmentHasConstParam);
   Record->push_back(Data.HasDeclaredCopyConstructorWithConstParam);
   Record->push_back(Data.HasDeclaredCopyAssignmentWithConstParam);
+  Record->push_back(Data.ODRHash);
   // IsLambda bit is already saved.
 
   Record->push_back(Data.NumBases);

Modified: cfe/trunk/test/Modules/merge-using-decls.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/merge-using-decls.cpp?rev=295284&r1=295283&r2=295284&view=diff
==============================================================================
--- cfe/trunk/test/Modules/merge-using-decls.cpp (original)
+++ cfe/trunk/test/Modules/merge-using-decls.cpp Wed Feb 15 22:53:40 2017
@@ -37,6 +37,10 @@ template int UseAll<Y>();
 // Here, we're instantiating the definition from 'A' and merging the definition
 // from 'B' into it.
 
+// expected-error at b.h:* {{'D::type' from module 'B' is not present in definition of 'D<T>' in module 'A'}}
+// expected-error at b.h:* {{'D::value' from module 'B' is not present in definition of 'D<T>' in module 'A'}}
+
+
 // expected-error at b.h:* {{'E::value' from module 'B' is not present in definition of 'E<T>' in module 'A'}}
 // expected-error at b.h:* {{'E::v' from module 'B' is not present in definition of 'E<T>' in module 'A'}}
 

Added: cfe/trunk/test/Modules/odr_hash.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/odr_hash.cpp?rev=295284&view=auto
==============================================================================
--- cfe/trunk/test/Modules/odr_hash.cpp (added)
+++ cfe/trunk/test/Modules/odr_hash.cpp Wed Feb 15 22:53:40 2017
@@ -0,0 +1,1156 @@
+// Clear and create directories
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: mkdir %t/cache
+// RUN: mkdir %t/Inputs
+
+// Build first header file
+// RUN: echo "#define FIRST" >> %t/Inputs/first.h
+// RUN: cat %s               >> %t/Inputs/first.h
+
+// Build second header file
+// RUN: echo "#define SECOND" >> %t/Inputs/second.h
+// RUN: cat %s                >> %t/Inputs/second.h
+
+// Build module map file
+// RUN: echo "module first {"           >> %t/Inputs/module.map
+// RUN: echo "    header \"first.h\""   >> %t/Inputs/module.map
+// RUN: echo "}"                        >> %t/Inputs/module.map
+// RUN: echo "module second {"          >> %t/Inputs/module.map
+// RUN: echo "    header \"second.h\""  >> %t/Inputs/module.map
+// RUN: echo "}"                        >> %t/Inputs/module.map
+
+// Run test
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache -x c++ -I%t/Inputs -verify %s -std=c++11
+
+#if !defined(FIRST) && !defined(SECOND)
+#include "first.h"
+#include "second.h"
+#endif
+
+#if defined(FIRST)
+struct S1 {
+  public:
+};
+#elif defined(SECOND)
+struct S1 {
+  private:
+};
+#else
+S1 s1;
+// expected-error at first.h:* {{'S1' has different definitions in different modules; first difference is definition in module 'first' found public access specifier}}
+// expected-note at second.h:* {{but in 'second' found private access specifier}}
+#endif
+
+#if defined(FIRST)
+struct S2Friend2 {};
+struct S2 {
+  friend S2Friend2;
+};
+#elif defined(SECOND)
+struct S2Friend1 {};
+struct S2 {
+  friend S2Friend1;
+};
+#else
+S2 s2;
+// expected-error at first.h:* {{'S2' has different definitions in different modules; first difference is definition in module 'first' found friend 'S2Friend2'}}
+// expected-note at second.h:* {{but in 'second' found other friend 'S2Friend1'}}
+#endif
+
+#if defined(FIRST)
+template<class>
+struct S3Template {};
+struct S3 {
+  friend S3Template<int>;
+};
+#elif defined(SECOND)
+template<class>
+struct S3Template {};
+struct S3 {
+  friend S3Template<double>;
+};
+#else
+S3 s3;
+// expected-error at first.h:* {{'S3' has different definitions in different modules; first difference is definition in module 'first' found friend 'S3Template<int>'}}
+// expected-note at second.h:* {{but in 'second' found other friend 'S3Template<double>'}}
+#endif
+
+#if defined(FIRST)
+struct S4 {
+  static_assert(1 == 1, "First");
+};
+#elif defined(SECOND)
+struct S4 {
+  static_assert(1 == 1, "Second");
+};
+#else
+S4 s4;
+// expected-error at first.h:* {{'S4' has different definitions in different modules; first difference is definition in module 'first' found static assert with message}}
+// expected-note at second.h:* {{but in 'second' found static assert with different message}}
+#endif
+
+#if defined(FIRST)
+struct S5 {
+  static_assert(1 == 1, "Message");
+};
+#elif defined(SECOND)
+struct S5 {
+  static_assert(2 == 2, "Message");
+};
+#else
+S5 s5;
+// expected-error at first.h:* {{'S5' has different definitions in different modules; first difference is definition in module 'first' found static assert with condition}}
+// expected-note at second.h:* {{but in 'second' found static assert with different condition}}
+#endif
+
+#if defined(FIRST)
+struct S6 {
+  int First();
+};
+#elif defined(SECOND)
+struct S6 {
+  int Second();
+};
+#else
+S6 s6;
+// expected-error at second.h:* {{'S6::Second' from module 'second' is not present in definition of 'S6' in module 'first'}}
+// expected-note at first.h:* {{definition has no member 'Second'}}
+#endif
+
+#if defined(FIRST)
+struct S7 {
+  double foo();
+};
+#elif defined(SECOND)
+struct S7 {
+  int foo();
+};
+#else
+S7 s7;
+// expected-error at second.h:* {{'S7::foo' from module 'second' is not present in definition of 'S7' in module 'first'}}
+// expected-note at first.h:* {{declaration of 'foo' does not match}}
+#endif
+
+#if defined(FIRST)
+struct S8 {
+  void foo();
+};
+#elif defined(SECOND)
+struct S8 {
+  void foo() {}
+};
+#else
+S8 s8;
+// expected-error at first.h:* {{'S8' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S9 {
+  void foo() { int y = 5; }
+};
+#elif defined(SECOND)
+struct S9 {
+  void foo() { int x = 5; }
+};
+#else
+S9 s9;
+// expected-error at first.h:* {{'S9' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S10 {
+  struct {
+    int x;
+  } a;
+};
+#elif defined(SECOND)
+struct S10 {
+  struct {
+    int x;
+    int y;
+  } a;
+};
+#else
+S10 s10;
+// expected-error-re at second.h:* {{'S10::(anonymous struct)::y' from module 'second' is not present in definition of 'S10::(anonymous struct at {{.*}}first.h:{{[0-9]*}}:{{[0-9]*}})' in module 'first'}}
+// expected-note at first.h:* {{definition has no member 'y'}}
+
+// expected-error at first.h:* {{'S10' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S11 {
+  void foo() { int y = sizeof(int); }
+};
+#elif defined(SECOND)
+struct S11 {
+  void foo() { int y = sizeof(double); }
+};
+#else
+S11 s11;
+// expected-error at first.h:* {{'S11' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S12 {
+  int x = sizeof(x);
+  int y = sizeof(x);
+};
+#elif defined(SECOND)
+struct S12 {
+  int x = sizeof(x);
+  int y = sizeof(y);
+};
+#else
+S12 s12;
+// expected-error at first.h:* {{'S12' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S13 {
+  template <typename A> void foo();
+};
+#elif defined(SECOND)
+struct S13 {
+  template <typename B> void foo();
+};
+#else
+S13 s13;
+// expected-error at first.h:* {{'S13' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S14 {
+  template <typename A, typename B> void foo();
+};
+#elif defined(SECOND)
+struct S14 {
+  template <typename A> void foo();
+};
+#else
+S14 s14;
+// expected-error at second.h:* {{'S14::foo' from module 'second' is not present in definition of 'S14' in module 'first'}}
+// expected-note at first.h:* {{declaration of 'foo' does not match}}
+#endif
+
+#if defined(FIRST)
+template <typename T>
+struct S15 : T {
+  void foo() {
+    int x = __builtin_offsetof(T, first);
+  }
+};
+#elif defined(SECOND)
+template <typename T>
+struct S15 : T {
+  void foo() {
+    int x = __builtin_offsetof(T, second);
+  }
+};
+#else
+template <typename T>
+void Test15() {
+  S15<T> s15;
+// expected-error at first.h:* {{'S15' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+}
+#endif
+
+#if defined(FIRST)
+struct S16 {
+  template <template<int = 0> class Y>
+  void foo() {
+    Y<> y;
+  }
+};
+#elif defined(SECOND)
+struct S16 {
+  template <template<int = 1> class Y>
+  void foo() {
+    Y<> y;
+  }
+};
+#else
+void TestS16() {
+  S16 s16;
+}
+// expected-error at first.h:* {{'S16' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S17 {
+  template <template <typename> class T>
+  static int foo(int a = 1);
+  template <template <typename> class T, template <typename> class U>
+  using Q_type = T<int>;
+};
+#elif defined(SECOND)
+struct S17 {
+  template <template <typename> class T>
+  static int foo(int a = 1);
+  template <template <typename> class T, template <typename> class U>
+  using Q_type = U<int>;
+};
+#else
+S17 s17;
+// expected-error at second.h:* {{'S17::Q_type' from module 'second' is not present in definition of 'S17' in module 'first'}}
+// expected-note at first.h:* {{declaration of 'Q_type' does not match}}
+#endif
+
+#if defined(FIRST)
+struct S18 {
+  enum E { X1 };
+};
+#elif defined(SECOND)
+struct S18 {
+  enum X { X1 };
+};
+#else
+S18 s18;
+// expected-error at second.h:* {{'S18::X' from module 'second' is not present in definition of 'S18' in module 'first'}}
+// expected-note at first.h:* {{definition has no member 'X'}}
+#endif
+
+#if defined(FIRST)
+struct S19 {
+  enum E { X1 };
+};
+#elif defined(SECOND)
+struct S19 {
+  enum E { X1, X2 };
+};
+#else
+S19 s19;
+// expected-error at first.h:* {{'S19' has different definitions in different modules; first difference is definition in module 'first' found enum 'E' has 1 element}}
+// expected-note at second.h:* {{but in 'second' found enum 'E' has 2 elements}}
+// expected-error at second.h:* {{'S19::E::X2' from module 'second' is not present in definition of 'S19::E' in module 'first'}}
+// expected-note at first.h:* {{definition has no member 'X2'}}
+#endif
+
+#if defined(FIRST)
+struct S20 {
+  enum E { X1 = 1 };
+};
+#elif defined(SECOND)
+struct S20 {
+  enum E { X1 = 5};
+};
+#else
+S20 s20;
+// expected-error at first.h:* {{'S20' has different definitions in different modules; first difference is definition in module 'first' found element 'X1' in enum 'E' with initializer}}
+// expected-note at second.h:* {{but in 'second' found element 'X1' in enum 'E' with different initializer}}
+#endif
+
+#if defined(FIRST)
+struct S21 {
+  void foo() {
+    label:
+    ;
+  }
+};
+#elif defined(SECOND)
+struct S21 {
+  void foo() {
+    ;
+  }
+};
+#else
+S21 s21;
+// expected-error at first.h:* {{'S21' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S22 {
+  void foo() {
+    label_first:
+    ;
+  }
+};
+#elif defined(SECOND)
+struct S22 {
+  void foo() {
+    label_second:
+    ;
+  }
+};
+#else
+S22 s22;
+// expected-error at first.h:* {{'S22' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S23 {
+  typedef int a;
+  typedef char b;
+};
+#elif defined(SECOND)
+struct S23 {
+  typedef char a;
+  typedef int b;
+};
+#else
+S23 s23;
+// expected-error at second.h:* {{'S23::a' from module 'second' is not present in definition of 'S23' in module 'first'}}
+// expected-note at first.h:* {{declaration of 'a' does not match}}
+// expected-error at second.h:* {{'S23::b' from module 'second' is not present in definition of 'S23' in module 'first'}}
+// expected-note at first.h:* {{declaration of 'b' does not match}}
+#endif
+
+#if defined(FIRST)
+struct S24 {
+  inline int foo();
+};
+#elif defined(SECOND)
+struct S24 {
+  int foo();
+};
+#else
+S24 s24;
+// expected-error at first.h:* {{'S24' has different definitions in different modules; first difference is definition in module 'first' found method 'foo' is inline}}
+// expected-note at second.h:* {{but in 'second' found method 'foo' is not inline}}
+#endif
+
+#if defined(FIRST)
+struct S25 {
+  int x;
+  S25() : x(5) {}
+};
+#elif defined(SECOND)
+struct S25 {
+  int x;
+  S25() {}
+};
+#else
+S25 s25;
+// expected-error at first.h:* {{'S25' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S26 {
+  int x;
+  S26() : x(5) {}
+};
+#elif defined(SECOND)
+struct S26 {
+  int x;
+  S26() : x(2) {}
+};
+#else
+S26 s26;
+// expected-error at first.h:* {{'S26' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S27 {
+  explicit S27(int) {}
+  S27() {}
+};
+#elif defined(SECOND)
+struct S27 {
+  S27(int) {}
+  S27() {}
+};
+#else
+S27 s27;
+// expected-error at first.h:* {{'S27' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST) || defined(SECOND)
+struct Base1 {
+  Base1();
+  Base1(int);
+  Base1(double);
+};
+
+struct Base2 {
+  Base2();
+  Base2(int);
+  Base2(double);
+};
+#endif
+
+#if defined(FIRST)
+struct S28 : public Base1 {};
+#elif defined(SECOND)
+struct S28 : public Base2 {};
+#else
+S28 s28;
+// expected-error at first.h:* {{'S28' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S29 : virtual Base1 {};
+#elif defined(SECOND)
+struct S29 : virtual Base2 {};
+#else
+S29 s29;
+// expected-error at first.h:* {{'S29' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S30 : public Base1 {
+  S30() : Base1(1) {}
+};
+#elif defined(SECOND)
+struct S30 : public Base1 {
+  S30() : Base1(1.0) {}
+};
+#else
+S30 s30;
+// expected-error at first.h:* {{'S30' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S31 : virtual Base1 {
+  S31() : Base1(1) {}
+};
+#elif defined(SECOND)
+struct S31 : virtual Base1 {
+  S31() : Base1(1.0) {}
+};
+#else
+S31 s31;
+// expected-error at first.h:* {{'S31' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S32 : public Base1, Base2 {
+  S32() : Base1(1), Base2(1.0) {}
+};
+#elif defined(SECOND)
+struct S32 : public Base2, Base1 {
+  S32() : Base2(1), Base1(1.0) {}
+};
+#else
+S32 s32;
+// expected-error at first.h:* {{'S32' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S33 {
+  S33() : S33(5) {}
+  S33(int) {int a;}
+};
+#elif defined(SECOND)
+struct S33 {
+  S33() : S33(5) {}
+  S33(int) {}
+};
+#else
+S33 s33;
+// expected-error at first.h:* {{'S33' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S34 {
+  operator bool();
+};
+#elif defined(SECOND)
+struct S34 {
+  operator int();
+};
+#else
+S34 s34;
+// expected-error at second.h:* {{'S34::operator int' from module 'second' is not present in definition of 'S34' in module 'first'}}
+// expected-note at first.h:* {{definition has no member 'operator int'}}
+#endif
+
+#if defined(FIRST)
+struct S35 {
+  explicit operator bool();
+};
+#elif defined(SECOND)
+struct S35 {
+  operator bool();
+};
+#else
+S35 s35;
+// expected-error at first.h:* {{'S35' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S36 {
+  int x : 3;
+};
+#elif defined(SECOND)
+struct S36 {
+  int x : 4;
+};
+#else
+S36 s36;
+// expected-error at first.h:* {{'S36' has different definitions in different modules; first difference is definition in module 'first' found bitfield 'x'}}
+// expected-note at second.h:* {{but in 'second' found bitfield 'x'}}
+#endif
+
+#if defined(FIRST)
+struct S37 {
+  mutable int x;
+  int y;
+};
+#elif defined(SECOND)
+struct S37 {
+  int x;
+  mutable int y;
+};
+#else
+S37 s37;
+// expected-error at first.h:* {{'S37' has different definitions in different modules; first difference is definition in module 'first' found mutable 'x'}}
+// expected-note at second.h:* {{but in 'second' found non-mutable 'x'}}
+#endif
+
+#if defined(FIRST)
+template <class X>
+struct S38 { };
+#elif defined(SECOND)
+template <class Y>
+struct S38 { };
+#else
+S38<int> s38;
+// expected-error at first.h:* {{'S38' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+template <class X = int>
+struct S39 { X x; };
+#elif defined(SECOND)
+template <class X = double>
+struct S39 { X x; };
+#else
+S39<> s39;
+// expected-error at first.h:* {{'S39' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+template <int X = 5>
+struct S40 { int x = X; };
+#elif defined(SECOND)
+template <int X = 7>
+struct S40 { int x = X; };
+#else
+S40<> s40;
+// expected-error at first.h:* {{'S40' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+
+#endif
+
+#if defined(FIRST)
+template <int> class T41a{};
+template <template<int> class T = T41a>
+struct S41 {};
+#elif defined(SECOND)
+template <int> class T41b{};
+template <template<int> class T = T41b>
+struct S41 {};
+#else
+using ::S41;
+// expected-error at first.h:* {{'S41' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S42 {
+  void foo() const {}
+  void bar() {}
+};
+#elif defined(SECOND)
+struct S42 {
+  void foo() {}
+  void bar() const {}
+};
+#else
+S42 s42;
+// expected-error at second.h:* {{'S42::bar' from module 'second' is not present in definition of 'S42' in module 'first'}}
+// expected-note at first.h:* {{declaration of 'bar' does not match}}
+// expected-error at second.h:* {{'S42::foo' from module 'second' is not present in definition of 'S42' in module 'first'}}
+// expected-note at first.h:* {{declaration of 'foo' does not match}}
+#endif
+
+#if defined(FIRST)
+struct S43 {
+  static constexpr int x = 1;
+  int y = 1;
+};
+#elif defined(SECOND)
+struct S43 {
+  int x = 1;
+  static constexpr int y = 1;
+};
+#else
+S43 s43;
+// expected-error at second.h:* {{'S43::x' from module 'second' is not present in definition of 'S43' in module 'first'}}
+// expected-note at first.h:* {{declaration of 'x' does not match}}
+// expected-error at second.h:* {{'S43::y' from module 'second' is not present in definition of 'S43' in module 'first'}}
+// expected-note at first.h:* {{declaration of 'y' does not match}}
+//#endif
+#endif
+
+#if defined(FIRST)
+void f44();
+struct S44 {
+  friend void f44();
+};
+#elif defined(SECOND)
+void g44();
+struct S44 {
+  friend void g44();
+};
+#else
+S44 s44;
+// expected-error at first.h:* {{'S44' has different definitions in different modules; first difference is definition in module 'first' found friend 'f44'}}
+// expected-note at second.h:* {{but in 'second' found other friend 'g44'}}
+#endif
+
+#if defined(FIRST)
+struct S45 { int n : 1; };
+#elif defined(SECOND)
+struct S45 { int n = 1; };
+#else
+S45 s45;
+// expected-error at first.h:* {{'S45' has different definitions in different modules; first difference is definition in module 'first' found bitfield 'n'}}
+// expected-note at second.h:* {{but in 'second' found field 'n'}}
+#endif
+
+#if defined(FIRST)
+struct S46 {
+  int operator+(int) { return 0; }
+};
+#elif defined(SECOND)
+struct S46 {
+  int operator-(int) { return 0; }
+};
+#else
+S46 s46;
+// expected-error at second.h:* {{'S46::operator-' from module 'second' is not present in definition of 'S46' in module 'first'}}
+// expected-note at first.h:* {{definition has no member 'operator-'}}
+#endif
+
+#if defined(FIRST)
+template <typename T>
+struct S47 {
+  int foo(int);
+  float foo(float);
+  int bar(int);
+  float bar(float);
+  int x = foo(T());
+};
+#elif defined(SECOND)
+template <typename T>
+struct S47 {
+  int foo(int);
+  float foo(float);
+  int bar(int);
+  float bar(float);
+  int x = bar(T());
+};
+#else
+template <typename T>
+using S48 = S47<T>;
+// expected-error at first.h:* {{'S47' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+template <typename T>
+struct S49 {
+  int operator+(int);
+  float operator+(float);
+  int operator-(int);
+  float operator-(float);
+  int x = S49() + T();
+};
+#elif defined(SECOND)
+template <typename T>
+struct S49 {
+  int operator+(int);
+  float operator+(float);
+  int operator-(int);
+  float operator-(float);
+  int x = S49() - T();
+};
+#else
+template <typename T>
+using S50 = S49<T>;
+// expected-error at first.h:* {{'S49' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+namespace A51 {
+  void foo();
+}
+struct S51 {
+  S51() {
+    A51::foo();
+  }
+};
+#elif defined(SECOND)
+namespace B51 {
+  void foo();
+}
+struct S51 {
+  S51() {
+    B51::foo();
+  }
+};
+#else
+S51 s51;
+// expected-error at first.h:* {{'S51' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+namespace N52 {
+  void foo();
+}
+struct S52 {
+  S52() {
+    N52::foo();
+  }
+};
+#elif defined(SECOND)
+namespace N52 {
+  void foo();
+}
+struct S52 {
+  S52() {
+    ::N52::foo();
+  }
+};
+#else
+S52 s52;
+// expected-error at first.h:* {{'S52' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+namespace N53 {
+  struct foo {
+    static int bar();
+  };
+  using A = foo;
+}
+struct S53 {
+  S53() {
+    N53::A::bar();
+  }
+};
+#elif defined(SECOND)
+namespace N53 {
+  struct foo {
+    static int bar();
+  };
+  using B = foo;
+}
+struct S53 {
+  S53() {
+    N53::B::bar();
+  }
+};
+#else
+S53 s53;
+// expected-error at first.h:* {{'S53' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+namespace N54 {
+namespace A {
+void foo();
+}
+namespace AA = A;
+}
+
+struct S54 {
+  S54() {
+    N54::AA::foo();
+  }
+};
+#elif defined(SECOND)
+namespace N54 {
+namespace B {
+void foo();
+}
+namespace BB = B;
+}
+
+struct S54 {
+  S54() {
+    N54::BB::foo();
+  }
+};
+#else
+S54 s54;
+// expected-error at first.h:* {{'S54' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+namespace N55 {
+namespace A {
+void foo();
+}
+namespace X = A;
+}
+
+struct S55 {
+  S55() {
+    N55::X::foo();
+  }
+};
+#elif defined(SECOND)
+namespace N55 {
+namespace B {
+void foo();
+}
+namespace X = B;
+}
+
+struct S55 {
+  S55() {
+    N55::X::foo();
+  }
+};
+#else
+S55 s55;
+// expected-error at first.h:* {{'S55' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+template<int> struct foo56{};
+template <template<int> class T>
+struct S56 {};
+struct S57 {
+  S56<foo56> a;
+};
+#elif defined(SECOND)
+template<int> struct bar56{};
+template <template<int> class T>
+struct S56 {};
+struct S57 {
+  S56<bar56> a;
+};
+#else
+S57 s57;
+// expected-error at second.h:* {{'S57::a' from module 'second' is not present in definition of 'S57' in module 'first'}}
+// expected-note at first.h:* {{declaration of 'a' does not match}}
+#endif
+
+#if defined(FIRST)
+template<int> struct foo58{};
+template <template<int> class T>
+struct S58 {};
+struct S59 {
+  S58<foo58> a;
+};
+#elif defined(SECOND)
+template<int> struct foo58{};
+template <template<int> class T>
+struct S58 {};
+struct S59 {
+  S58<::foo58> a;
+};
+#else
+S59 s59;
+// expected-error at first.h:* {{'S59' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note at second.h:* {{definition in module 'second' is here}}
+#endif
+
+
+#if defined(FIRST)
+struct S60a {};
+
+typedef S60a S61;
+
+struct S62 {
+  S61 s61;
+};
+#elif defined(SECOND)
+struct S60b {};
+
+typedef S60b S61;
+
+struct S62 {
+  S61 s61;
+};
+#else
+S62 s62;
+// expected-error at second.h:* {{'S62::s61' from module 'second' is not present in definition of 'S62' in module 'first'}}
+// expected-note at first.h:* {{declaration of 's61' does not match}}
+#endif
+
+// Don't warn on these cases
+#if defined(FIRST)
+void f01(int = 0);
+struct S01 { friend void f01(int); };
+#elif defined(SECOND)
+void f01(int);
+struct S01 { friend void f01(int); };
+#else
+S01 s01;
+#endif
+
+#if defined(FIRST)
+template <template <int> class T> class Wrapper {};
+
+template <int N> class SelfReference {
+  SelfReference(Wrapper<::SelfReference> &R) {}
+};
+
+struct Xx {
+  struct Yy {
+  };
+};
+
+Xx::Xx::Xx::Yy yy;
+
+namespace NNS {
+template <typename> struct Foo;
+template <template <class> class T = NNS::Foo>
+struct NestedNamespaceSpecifier {};
+}
+#endif
+
+#if defined(FIRST)
+struct S02 { };
+void S02Construct() {
+  S02 foo;
+  S02 bar = foo;
+  S02 baz(bar);
+}
+#elif defined(SECOND)
+struct S02 { };
+#else
+S02 s02;
+#endif
+
+#if defined(FIRST)
+template <class>
+struct S03 {};
+#elif defined(SECOND)
+template <class>
+struct S03 {};
+#else
+S03<int> s03;
+#endif
+
+#if defined(FIRST)
+template <class T>
+struct S04 {
+  T t;
+};
+#elif defined(SECOND)
+template <class T>
+struct S04 {
+  T t;
+};
+#else
+S03<int> s04;
+#endif
+
+#if defined(FIRST)
+template <class T>
+class Wrapper05;
+template <class T>
+struct S05 {
+  Wrapper05<T> t;
+};
+#elif defined(SECOND)
+template <class T>
+class Wrapper05;
+template <class T>
+struct S05 {
+  Wrapper05<T> t;
+};
+#else
+template <class T>
+class Wrapper05{};
+S05<int> s05;
+#endif
+
+#if defined(FIRST)
+enum E06 : int;
+struct S06 {
+  void get(E06) {}
+};
+#elif defined(SECOND)
+enum E06 : int { A06 };
+struct S06 {
+  void get(E06) {}
+};
+#else
+S06 s06;
+#endif
+
+#if defined(FIRST)
+struct S07a {};
+struct S07 {
+  struct S07a *ptr;
+};
+#elif defined(SECOND)
+struct S07 {
+  struct S07a *ptr;
+};
+#else
+S07 s07;
+#endif
+
+#if defined(FIRST)
+struct S08a;
+struct S08 {
+  struct S08a *ptr;
+};
+#elif defined(SECOND)
+struct S08 {
+  struct S08a *ptr;
+};
+#else
+S08 s08;
+#endif
+
+// TODO: Error on these cases.
+#if defined(FIRST)
+enum E { E1 };
+#elif defined(SECOND)
+enum E { E1, E2 };
+#else
+E e;
+#endif
+
+#if defined(FIRST)
+int foo() { return 1; }
+#elif defined(SECOND)
+int foo() { return 2; }
+#else
+int number = foo();
+#endif
+
+// Keep macros contained to one file.
+#ifdef FIRST
+#undef FIRST
+#endif
+#ifdef SECOND
+#undef SECOND
+#endif




More information about the cfe-commits mailing list