This revision was automatically updated to reflect the committed changes.
Closed by commit rL293585: Add better ODR checking for modules. (authored by 
rtrieu).

Changed prior to commit:
  https://reviews.llvm.org/D21675?vs=86142&id=86376#toc

Repository:
  rL LLVM

https://reviews.llvm.org/D21675

Files:
  cfe/trunk/include/clang/AST/DeclCXX.h
  cfe/trunk/include/clang/AST/ODRHash.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/ODRHash.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
  cfe/trunk/test/Modules/odr_hash.cpp

Index: cfe/trunk/lib/AST/DeclCXX.cpp
===================================================================
--- cfe/trunk/lib/AST/DeclCXX.cpp
+++ cfe/trunk/lib/AST/DeclCXX.cpp
@@ -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 @@
       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 @@
   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
Index: cfe/trunk/lib/AST/ODRHash.cpp
===================================================================
--- cfe/trunk/lib/AST/ODRHash.cpp
+++ cfe/trunk/lib/AST/ODRHash.cpp
@@ -0,0 +1,892 @@
+//===-- 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;
+
+// This method adds more information that AddDecl does.
+void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) {
+  assert(Record && Record->hasDefinition() &&
+         "Expected non-null record to be a definition.");
+  AddDecl(Record);
+
+  // Additional information that is not needed in AddDecl.  Do not move this
+  // to ODRTypeVisitor.
+  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());
+  }
+}
+
+// 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;
+  }
+}
+
+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()) {
+    AddDecl(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());
+  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 VisitDecl(const Decl *D) {
+    if (!D) {
+      return;
+    }
+    if (D->isImplicit()) {
+      return;
+    }
+    if (D->isInvalidDecl()) {
+      return;
+    }
+    ID.AddInteger(D->getKind());
+    Hash.AddBoolean(D->hasAttrs());
+
+    if (auto *DC = dyn_cast<DeclContext>(D)) {
+      llvm::SmallVector<const Decl *, 16> Decls;
+      for (const Decl* D : DC->decls()) {
+        if (!D->isImplicit()) {
+          Decls.push_back(D);
+        }
+      }
+      ID.AddInteger(Decls.size());
+      for (auto SubDecl : Decls) {
+        AddDecl(SubDecl);
+      }
+    }
+
+    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());
+
+    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) {
+    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 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) {
+    ODRDeclVisitor(ID, *this).Visit(D);
+  }
+}
+
+// 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 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 VisitType(const Type *T) { ID.AddInteger(T->getTypeClass()); }
+
+  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) {
+    AddQualType(T->getInjectedSpecializationType());
+    AddDecl(T->getDecl());
+    VisitType(T);
+  }
+
+  void VisitMemberPointerType(const MemberPointerType *T) {
+    AddQualType(T->getPointeeType());
+    Visit(T->getClass());
+    VisitType(T);
+  }
+
+  void VisitObjCObjectPointerType(const ObjCObjectPointerType *T) {
+    AddQualType(T->getPointeeType());
+    VisitType(T);
+  }
+
+  void VisitObjCObjectType(const ObjCObjectType *T) {
+    QualType Base = T->getBaseType();
+    const bool SameType = Base.getTypePtr() == T;
+    Hash.AddBoolean(SameType);
+    if (!SameType) {
+      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());
+    AddQualType(QualType(T->getReplacedParameter(), 0));
+    VisitType(T);
+  }
+
+  void
+  VisitSubstTemplateTypeParmPackType(const SubstTemplateTypeParmPackType *T) {
+    AddQualType(QualType(T->getReplacedParameter(), 0));
+    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) {
+    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);
+}
Index: cfe/trunk/lib/AST/StmtProfile.cpp
===================================================================
--- cfe/trunk/lib/AST/StmtProfile.cpp
+++ cfe/trunk/lib/AST/StmtProfile.cpp
@@ -19,44 +19,49 @@
 #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);
 
 #define STMT(Node, Base) void Visit##Node(const Node *S);
 #include "clang/AST/StmtNodes.inc"
 
     /// \brief Visit a declaration that is referenced within an expression
     /// or statement.
-    void VisitDecl(const Decl *D);
+    virtual void VisitDecl(const Decl *D) = 0;
 
     /// \brief Visit a type that is referenced within an expression or
     /// statement.
-    void VisitType(QualType T);
+    virtual void VisitType(QualType T) = 0;
 
     /// \brief Visit a name that occurs within an expression or statement.
-    void VisitName(DeclarationName Name);
+    virtual void VisitName(DeclarationName Name) = 0;
+
+    /// \brief Visit identifiers that are not in Decl's or Type's.
+    virtual void VisitIdentifierInfo(IdentifierInfo *II) = 0;
 
     /// \brief Visit a nested-name-specifier that occurs within an expression
     /// or statement.
-    void VisitNestedNameSpecifier(NestedNameSpecifier *NNS);
+    virtual void VisitNestedNameSpecifier(NestedNameSpecifier *NNS) = 0;
 
     /// \brief Visit a template name that occurs within an expression or
     /// statement.
-    void VisitTemplateName(TemplateName Name);
+    virtual void VisitTemplateName(TemplateName Name) = 0;
 
     /// \brief Visit template arguments that occur within an expression or
     /// statement.
@@ -66,6 +71,127 @@
     /// \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,15 +979,15 @@
       break;
 
     case OffsetOfNode::Identifier:
-      ID.AddPointer(ON.getFieldName());
+      VisitIdentifierInfo(ON.getFieldName());
       break;
 
     case OffsetOfNode::Base:
       // These nodes are implicit, and therefore don't need profiling.
       break;
     }
   }
-  
+
   VisitExpr(S);
 }
 
@@ -1451,7 +1577,7 @@
   if (S->getDestroyedTypeInfo())
     VisitType(S->getDestroyedType());
   else
-    ID.AddPointer(S->getDestroyedTypeIdentifier());
+    VisitIdentifierInfo(S->getDestroyedTypeIdentifier());
 }
 
 void StmtProfiler::VisitOverloadExpr(const OverloadExpr *S) {
@@ -1701,77 +1827,6 @@
   VisitExpr(S);
 }
 
-void StmtProfiler::VisitDecl(const Decl *D) {
-  ID.AddInteger(D? D->getKind() : 0);
-
-  if (Canonical && D) {
-    if (const NonTypeTemplateParmDecl *NTTP =
-          dyn_cast<NonTypeTemplateParmDecl>(D)) {
-      ID.AddInteger(NTTP->getDepth());
-      ID.AddInteger(NTTP->getIndex());
-      ID.AddBoolean(NTTP->isParameterPack());
-      VisitType(NTTP->getType());
-      return;
-    }
-
-    if (const ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D)) {
-      // The Itanium C++ ABI uses the type, scope depth, and scope
-      // index of a parameter when mangling expressions that involve
-      // function parameters, so we will use the parameter's type for
-      // establishing function parameter identity. That way, our
-      // definition of "equivalent" (per C++ [temp.over.link]) is at
-      // least as strong as the definition of "equivalent" used for
-      // name mangling.
-      VisitType(Parm->getType());
-      ID.AddInteger(Parm->getFunctionScopeDepth());
-      ID.AddInteger(Parm->getFunctionScopeIndex());
-      return;
-    }
-
-    if (const TemplateTypeParmDecl *TTP =
-          dyn_cast<TemplateTypeParmDecl>(D)) {
-      ID.AddInteger(TTP->getDepth());
-      ID.AddInteger(TTP->getIndex());
-      ID.AddBoolean(TTP->isParameterPack());
-      return;
-    }
-
-    if (const TemplateTemplateParmDecl *TTP =
-          dyn_cast<TemplateTemplateParmDecl>(D)) {
-      ID.AddInteger(TTP->getDepth());
-      ID.AddInteger(TTP->getIndex());
-      ID.AddBoolean(TTP->isParameterPack());
-      return;
-    }
-  }
-
-  ID.AddPointer(D? D->getCanonicalDecl() : nullptr);
-}
-
-void StmtProfiler::VisitType(QualType T) {
-  if (Canonical)
-    T = Context.getCanonicalType(T);
-
-  ID.AddPointer(T.getAsOpaquePtr());
-}
-
-void StmtProfiler::VisitName(DeclarationName Name) {
-  ID.AddPointer(Name.getAsOpaquePtr());
-}
-
-void StmtProfiler::VisitNestedNameSpecifier(NestedNameSpecifier *NNS) {
-  if (Canonical)
-    NNS = Context.getCanonicalNestedNameSpecifier(NNS);
-  ID.AddPointer(NNS);
-}
-
-void StmtProfiler::VisitTemplateName(TemplateName Name) {
-  if (Canonical)
-    Name = Context.getCanonicalTemplateName(Name);
-
-  Name.Profile(ID);
-}
-
 void StmtProfiler::VisitTemplateArguments(const TemplateArgumentLoc *Args,
                                           unsigned NumArgs) {
   ID.AddInteger(NumArgs);
@@ -1821,6 +1876,12 @@
 
 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);
 }
Index: cfe/trunk/lib/AST/CMakeLists.txt
===================================================================
--- cfe/trunk/lib/AST/CMakeLists.txt
+++ cfe/trunk/lib/AST/CMakeLists.txt
@@ -40,6 +40,7 @@
   MicrosoftMangle.cpp
   NestedNameSpecifier.cpp
   NSAPI.cpp
+  ODRHash.cpp
   OpenMPClause.cpp
   ParentMap.cpp
   RawCommentList.cpp
Index: cfe/trunk/lib/Sema/SemaDecl.cpp
===================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp
+++ cfe/trunk/lib/Sema/SemaDecl.cpp
@@ -13705,8 +13705,11 @@
       RD->completeDefinition();
   }
 
-  if (isa<CXXRecordDecl>(Tag))
+  if (auto *RD = dyn_cast<CXXRecordDecl>(Tag)) {
     FieldCollector->FinishClass();
+    if (Context.getLangOpts().Modules)
+      RD->computeODRHash();
+  }
 
   // Exit this scope of this tag's definition.
   PopDeclContext();
Index: cfe/trunk/lib/Serialization/ASTWriter.cpp
===================================================================
--- cfe/trunk/lib/Serialization/ASTWriter.cpp
+++ cfe/trunk/lib/Serialization/ASTWriter.cpp
@@ -5699,6 +5699,7 @@
   Record->push_back(Data.ImplicitCopyAssignmentHasConstParam);
   Record->push_back(Data.HasDeclaredCopyConstructorWithConstParam);
   Record->push_back(Data.HasDeclaredCopyAssignmentWithConstParam);
+  Record->push_back(Data.ODRHash);
   // IsLambda bit is already saved.
 
   Record->push_back(Data.NumBases);
Index: cfe/trunk/lib/Serialization/ASTReader.cpp
===================================================================
--- cfe/trunk/lib/Serialization/ASTReader.cpp
+++ cfe/trunk/lib/Serialization/ASTReader.cpp
@@ -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"
@@ -8884,21 +8885,638 @@
     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.AddDecl(D);
+        FirstHashes.emplace_back(D, Hash.CalculateHash());
+      }
+      for (auto D : RD->decls()) {
+        if (D->isImplicit())
+          continue;
+        Hash.clear();
+        Hash.AddDecl(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) {
Index: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
===================================================================
--- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
+++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
@@ -1521,6 +1521,7 @@
   Data.ImplicitCopyAssignmentHasConstParam = Record.readInt();
   Data.HasDeclaredCopyConstructorWithConstParam = Record.readInt();
   Data.HasDeclaredCopyAssignmentWithConstParam = Record.readInt();
+  Data.ODRHash = Record.readInt();
 
   Data.NumBases = Record.readInt();
   if (Data.NumBases)
@@ -1651,6 +1652,7 @@
   OR_FIELD(HasDeclaredCopyConstructorWithConstParam)
   OR_FIELD(HasDeclaredCopyAssignmentWithConstParam)
   MATCH_FIELD(IsLambda)
+  MATCH_FIELD(ODRHash)
 #undef OR_FIELD
 #undef MATCH_FIELD
 
Index: cfe/trunk/include/clang/AST/DeclCXX.h
===================================================================
--- cfe/trunk/include/clang/AST/DeclCXX.h
+++ cfe/trunk/include/clang/AST/DeclCXX.h
@@ -458,6 +458,9 @@
     /// \brief Whether we are currently parsing base specifiers.
     unsigned IsParsingBaseSpecifiers : 1;
 
+    /// \brief A hash of parts of the class to help in ODR checking.
+    unsigned ODRHash;
+
     /// \brief The number of base class specifiers in Bases.
     unsigned NumBases;
 
@@ -703,6 +706,9 @@
     return data().IsParsingBaseSpecifiers;
   }
 
+  void computeODRHash();
+  unsigned getODRHash() const { return data().ODRHash; }
+
   /// \brief Sets the base classes of this struct or class.
   void setBases(CXXBaseSpecifier const * const *Bases, unsigned NumBases);
 
Index: cfe/trunk/include/clang/AST/ODRHash.h
===================================================================
--- cfe/trunk/include/clang/AST/ODRHash.h
+++ cfe/trunk/include/clang/AST/ODRHash.h
@@ -0,0 +1,80 @@
+//===-- 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);
+
+  // Add AST nodes that need to be processes.  Some nodes are processed
+  // immediately while others are queued and processed later.
+  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);
+
+  // Reset the object for reuse.
+  void clear();
+
+  // Processes any unvisited nodes in Pointers and computes the final hash
+  // value.
+  unsigned CalculateHash();
+
+  // Save booleans until the end to lower the size of data to process.
+  void AddBoolean(bool value);
+};
+
+}  // end namespace clang
Index: cfe/trunk/include/clang/AST/Stmt.h
===================================================================
--- cfe/trunk/include/clang/AST/Stmt.h
+++ cfe/trunk/include/clang/AST/Stmt.h
@@ -39,6 +39,7 @@
   class Expr;
   class IdentifierInfo;
   class LabelDecl;
+  class ODRHash;
   class ParmVarDecl;
   class PrinterHelper;
   struct PrintingPolicy;
@@ -436,6 +437,8 @@
   /// 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
Index: cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
===================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
+++ cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
@@ -117,6 +117,70 @@
 def err_module_odr_violation_different_instantiations : Error<
   "instantiation of %q0 is different in different modules">;
 
+def err_module_odr_violation_mismatch_decl : Error<
+  "%q0 has different definitions in different modules; first difference is "
+  "%select{definition in module '%2'|defined here}1 found "
+  "%select{end of class|public access specifier|private access specifier|"
+  "protected access specifier|friend declaration|enum|"
+  "static assert|typedef|type alias|method|constructor|destructor|"
+  "conversion operator|field|other}3">;
+def note_module_odr_violation_mismatch_decl : Note<"but in '%0' found "
+  "%select{end of class|public access specifier|private access specifier|"
+  "protected access specifier|friend declaration|enum|"
+  "static assert|typedef|type alias|method|constructor|destructor|"
+  "conversion operator|field|other}1">;
+
+def err_module_odr_violation_mismatch_decl_diff : Error<
+  "%q0 has different definitions in different modules; first difference is "
+  "%select{definition in module '%2'|defined here}1 found "
+  "%select{friend %4|enum %4|element %4 in enum %5|"
+  "element %4 in enum %5 with initializer|"
+  "element %4 in enum %5 with no initializer|"
+  "element %4 in enum %5 with initializer|"
+  "enum %4 has %5 element%s5|"
+  "static assert with condition|"
+  "static assert with message|"
+  "static assert with %select{|no}4 message|"
+  "%select{typedef|type alias}4 name %5|"
+  "method named %4|"
+  "method %4 is %select{non-|}5static|"
+  "method %4 is %select{not |}5inline|"
+  "method %4 is %select{not |}5const|"
+  "method %4 has %5 parameter%s5|"
+  "method %4 has %ordinal5 parameter %select{named %7|with no name}6|"
+  "method %4 has %ordinal5 parameter with type %6|"
+  "method %4 has %ordinal5 parameter with default argument|"
+  "method %4 has %ordinal5 parameter with %select{no |}6 default argument|"
+  "method %4 has %select{|no }5body|"
+  "method %4 has different body|"
+  "field %4|"
+  "%select{field|bitfield}5 %4|"
+  "%select{non-mutable|mutable}5 %4}3">;
+def note_module_odr_violation_mismatch_decl_diff : Note<"but in '%0' found "
+  "%select{other friend %2|other enum %2|different element %2 in enum %3|"
+  "element %2 in enum %3 with initializer|"
+  "element %2 in enum %3 with no initializer|"
+  "element %2 in enum %3 with different initializer|"
+  "enum %2 has %3 element%s3|"
+  "static assert with different condition|"
+  "static assert with different message|"
+  "static assert with %select{|no}2 message|"
+  "different %select{typedef|type alias}2 name %3|"
+  "method named %2|"
+  "method %2 is %select{non-|}3static|"
+  "method %2 is %select{not |}3inline|"
+  "method %2 is %select{not |}3const|"
+  "method %2 has %3 parameter%s3|"
+  "method %2 has %ordinal3 parameter %select{named %5|with no name}4|"
+  "method %2 has %ordinal3 parameter with type %4|"
+  "method %2 has %ordinal3 parameter with different default argument|"
+  "method %2 has %ordinal3 parameter with %select{no |}4default argument|"
+  "method %2 has %select{|no }3body|"
+  "method %2 has different body|"
+  "field %2|"
+  "%select{field|bitfield}3 %2|"
+  "%select{non-mutable|mutable}3 %2}1">;
+
 def warn_module_uses_date_time : Warning<
   "%select{precompiled header|module}0 uses __DATE__ or __TIME__">,
   InGroup<DiagGroup<"pch-date-time">>;
Index: cfe/trunk/test/Modules/odr_hash.cpp
===================================================================
--- cfe/trunk/test/Modules/odr_hash.cpp
+++ cfe/trunk/test/Modules/odr_hash.cpp
@@ -0,0 +1,1077 @@
+// 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@first.h:* {{'S1' has different definitions in different modules; first difference is definition in module 'first' found public access specifier}}
+// expected-note@second.h:* {{but in 'second' found private access specifier}}
+#endif
+
+#if defined(FIRST)
+struct S2Friend2 {};
+struct S2 {
+  friend S2Friend2;
+};
+#elif defined(SECOND)
+struct S2Friend1 {};
+struct S2 {
+  friend S2Friend1;
+};
+#else
+S2 s2;
+// expected-error@first.h:* {{'S2' has different definitions in different modules; first difference is definition in module 'first' found friend 'S2Friend2'}}
+// expected-note@second.h:* {{but in 'second' found other friend 'S2Friend1'}}
+#endif
+
+#if defined(FIRST)
+template<class>
+struct S3Template {};
+struct S3 {
+  friend S3Template<int>;
+};
+#elif defined(SECOND)
+template<class>
+struct S3Template {};
+struct S3 {
+  friend S3Template<double>;
+};
+#else
+S3 s3;
+// expected-error@first.h:* {{'S3' has different definitions in different modules; first difference is definition in module 'first' found friend 'S3Template<int>'}}
+// expected-note@second.h:* {{but in 'second' found other friend 'S3Template<double>'}}
+#endif
+
+#if defined(FIRST)
+struct S4 {
+  static_assert(1 == 1, "First");
+};
+#elif defined(SECOND)
+struct S4 {
+  static_assert(1 == 1, "Second");
+};
+#else
+S4 s4;
+// expected-error@first.h:* {{'S4' has different definitions in different modules; first difference is definition in module 'first' found static assert with message}}
+// expected-note@second.h:* {{but in 'second' found static assert with different message}}
+#endif
+
+#if defined(FIRST)
+struct S5 {
+  static_assert(1 == 1, "Message");
+};
+#elif defined(SECOND)
+struct S5 {
+  static_assert(2 == 2, "Message");
+};
+#else
+S5 s5;
+// expected-error@first.h:* {{'S5' has different definitions in different modules; first difference is definition in module 'first' found static assert with condition}}
+// expected-note@second.h:* {{but in 'second' found static assert with different condition}}
+#endif
+
+#if defined(FIRST)
+struct S6 {
+  int First();
+};
+#elif defined(SECOND)
+struct S6 {
+  int Second();
+};
+#else
+S6 s6;
+// expected-error@second.h:* {{'S6::Second' from module 'second' is not present in definition of 'S6' in module 'first'}}
+// expected-note@first.h:* {{definition has no member 'Second'}}
+#endif
+
+#if defined(FIRST)
+struct S7 {
+  double foo();
+};
+#elif defined(SECOND)
+struct S7 {
+  int foo();
+};
+#else
+S7 s7;
+// expected-error@second.h:* {{'S7::foo' from module 'second' is not present in definition of 'S7' in module 'first'}}
+// expected-note@first.h:* {{declaration of 'foo' does not match}}
+#endif
+
+#if defined(FIRST)
+struct S8 {
+  void foo();
+};
+#elif defined(SECOND)
+struct S8 {
+  void foo() {}
+};
+#else
+S8 s8;
+// expected-error@first.h:* {{'S8' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S9 {
+  void foo() { int y = 5; }
+};
+#elif defined(SECOND)
+struct S9 {
+  void foo() { int x = 5; }
+};
+#else
+S9 s9;
+// expected-error@first.h:* {{'S9' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S10 {
+  struct {
+    int x;
+  } a;
+};
+#elif defined(SECOND)
+struct S10 {
+  struct {
+    int x;
+    int y;
+  } a;
+};
+#else
+S10 s10;
+// expected-error-re@second.h:* {{'S10::(anonymous struct)::y' from module 'second' is not present in definition of 'S10::(anonymous struct at {{.*}}first.h:{{[0-9]*}}:{{[0-9]*}})' in module 'first'}}
+// expected-note@first.h:* {{definition has no member 'y'}}
+
+// expected-error@first.h:* {{'S10' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S11 {
+  void foo() { int y = sizeof(int); }
+};
+#elif defined(SECOND)
+struct S11 {
+  void foo() { int y = sizeof(double); }
+};
+#else
+S11 s11;
+// expected-error@first.h:* {{'S11' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S12 {
+  int x = sizeof(x);
+  int y = sizeof(x);
+};
+#elif defined(SECOND)
+struct S12 {
+  int x = sizeof(x);
+  int y = sizeof(y);
+};
+#else
+S12 s12;
+// expected-error@first.h:* {{'S12' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S13 {
+  template <typename A> void foo();
+};
+#elif defined(SECOND)
+struct S13 {
+  template <typename B> void foo();
+};
+#else
+S13 s13;
+// expected-error@first.h:* {{'S13' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S14 {
+  template <typename A, typename B> void foo();
+};
+#elif defined(SECOND)
+struct S14 {
+  template <typename A> void foo();
+};
+#else
+S14 s14;
+// expected-error@second.h:* {{'S14::foo' from module 'second' is not present in definition of 'S14' in module 'first'}}
+// expected-note@first.h:* {{declaration of 'foo' does not match}}
+#endif
+
+#if defined(FIRST)
+template <typename T>
+struct S15 : T {
+  void foo() {
+    int x = __builtin_offsetof(T, first);
+  }
+};
+#elif defined(SECOND)
+template <typename T>
+struct S15 : T {
+  void foo() {
+    int x = __builtin_offsetof(T, second);
+  }
+};
+#else
+template <typename T>
+void Test15() {
+  S15<T> s15;
+// expected-error@first.h:* {{'S15' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+}
+#endif
+
+#if defined(FIRST)
+struct S16 {
+  template <template<int = 0> class Y>
+  void foo() {
+    Y<> y;
+  }
+};
+#elif defined(SECOND)
+struct S16 {
+  template <template<int = 1> class Y>
+  void foo() {
+    Y<> y;
+  }
+};
+#else
+void TestS16() {
+  S16 s16;
+}
+// expected-error@first.h:* {{'S16' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S17 {
+  template <template <typename> class T>
+  static int foo(int a = 1);
+  template <template <typename> class T, template <typename> class U>
+  using Q_type = T<int>;
+};
+#elif defined(SECOND)
+struct S17 {
+  template <template <typename> class T>
+  static int foo(int a = 1);
+  template <template <typename> class T, template <typename> class U>
+  using Q_type = U<int>;
+};
+#else
+S17 s17;
+// expected-error@second.h:* {{'S17::Q_type' from module 'second' is not present in definition of 'S17' in module 'first'}}
+// expected-note@first.h:* {{declaration of 'Q_type' does not match}}
+#endif
+
+#if defined(FIRST)
+struct S18 {
+  enum E { X1 };
+};
+#elif defined(SECOND)
+struct S18 {
+  enum X { X1 };
+};
+#else
+S18 s18;
+// expected-error@second.h:* {{'S18::X' from module 'second' is not present in definition of 'S18' in module 'first'}}
+// expected-note@first.h:* {{definition has no member 'X'}}
+#endif
+
+#if defined(FIRST)
+struct S19 {
+  enum E { X1 };
+};
+#elif defined(SECOND)
+struct S19 {
+  enum E { X1, X2 };
+};
+#else
+S19 s19;
+// expected-error@first.h:* {{'S19' has different definitions in different modules; first difference is definition in module 'first' found enum 'E' has 1 element}}
+// expected-note@second.h:* {{but in 'second' found enum 'E' has 2 elements}}
+// expected-error@second.h:* {{'S19::E::X2' from module 'second' is not present in definition of 'S19::E' in module 'first'}}
+// expected-note@first.h:* {{definition has no member 'X2'}}
+#endif
+
+#if defined(FIRST)
+struct S20 {
+  enum E { X1 = 1 };
+};
+#elif defined(SECOND)
+struct S20 {
+  enum E { X1 = 5};
+};
+#else
+S20 s20;
+// expected-error@first.h:* {{'S20' has different definitions in different modules; first difference is definition in module 'first' found element 'X1' in enum 'E' with initializer}}
+// expected-note@second.h:* {{but in 'second' found element 'X1' in enum 'E' with different initializer}}
+#endif
+
+#if defined(FIRST)
+struct S21 {
+  void foo() {
+    label:
+    ;
+  }
+};
+#elif defined(SECOND)
+struct S21 {
+  void foo() {
+    ;
+  }
+};
+#else
+S21 s21;
+// expected-error@first.h:* {{'S21' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S22 {
+  void foo() {
+    label_first:
+    ;
+  }
+};
+#elif defined(SECOND)
+struct S22 {
+  void foo() {
+    label_second:
+    ;
+  }
+};
+#else
+S22 s22;
+// expected-error@first.h:* {{'S22' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S23 {
+  typedef int a;
+  typedef char b;
+};
+#elif defined(SECOND)
+struct S23 {
+  typedef char a;
+  typedef int b;
+};
+#else
+S23 s23;
+// expected-error@second.h:* {{'S23::a' from module 'second' is not present in definition of 'S23' in module 'first'}}
+// expected-note@first.h:* {{declaration of 'a' does not match}}
+// expected-error@second.h:* {{'S23::b' from module 'second' is not present in definition of 'S23' in module 'first'}}
+// expected-note@first.h:* {{declaration of 'b' does not match}}
+#endif
+
+#if defined(FIRST)
+struct S24 {
+  inline int foo();
+};
+#elif defined(SECOND)
+struct S24 {
+  int foo();
+};
+#else
+S24 s24;
+// expected-error@first.h:* {{'S24' has different definitions in different modules; first difference is definition in module 'first' found method 'foo' is inline}}
+// expected-note@second.h:* {{but in 'second' found method 'foo' is not inline}}
+#endif
+
+#if defined(FIRST)
+struct S25 {
+  int x;
+  S25() : x(5) {}
+};
+#elif defined(SECOND)
+struct S25 {
+  int x;
+  S25() {}
+};
+#else
+S25 s25;
+// expected-error@first.h:* {{'S25' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S26 {
+  int x;
+  S26() : x(5) {}
+};
+#elif defined(SECOND)
+struct S26 {
+  int x;
+  S26() : x(2) {}
+};
+#else
+S26 s26;
+// expected-error@first.h:* {{'S26' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S27 {
+  explicit S27(int) {}
+  S27() {}
+};
+#elif defined(SECOND)
+struct S27 {
+  S27(int) {}
+  S27() {}
+};
+#else
+S27 s27;
+// expected-error@first.h:* {{'S27' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST) || defined(SECOND)
+struct Base1 {
+  Base1();
+  Base1(int);
+  Base1(double);
+};
+
+struct Base2 {
+  Base2();
+  Base2(int);
+  Base2(double);
+};
+#endif
+
+#if defined(FIRST)
+struct S28 : public Base1 {};
+#elif defined(SECOND)
+struct S28 : public Base2 {};
+#else
+S28 s28;
+// expected-error@first.h:* {{'S28' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S29 : virtual Base1 {};
+#elif defined(SECOND)
+struct S29 : virtual Base2 {};
+#else
+S29 s29;
+// expected-error@first.h:* {{'S29' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S30 : public Base1 {
+  S30() : Base1(1) {}
+};
+#elif defined(SECOND)
+struct S30 : public Base1 {
+  S30() : Base1(1.0) {}
+};
+#else
+S30 s30;
+// expected-error@first.h:* {{'S30' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S31 : virtual Base1 {
+  S31() : Base1(1) {}
+};
+#elif defined(SECOND)
+struct S31 : virtual Base1 {
+  S31() : Base1(1.0) {}
+};
+#else
+S31 s31;
+// expected-error@first.h:* {{'S31' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S32 : public Base1, Base2 {
+  S32() : Base1(1), Base2(1.0) {}
+};
+#elif defined(SECOND)
+struct S32 : public Base2, Base1 {
+  S32() : Base2(1), Base1(1.0) {}
+};
+#else
+S32 s32;
+// expected-error@first.h:* {{'S32' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S33 {
+  S33() : S33(5) {}
+  S33(int) {int a;}
+};
+#elif defined(SECOND)
+struct S33 {
+  S33() : S33(5) {}
+  S33(int) {}
+};
+#else
+S33 s33;
+// expected-error@first.h:* {{'S33' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S34 {
+  operator bool();
+};
+#elif defined(SECOND)
+struct S34 {
+  operator int();
+};
+#else
+S34 s34;
+// expected-error@second.h:* {{'S34::operator int' from module 'second' is not present in definition of 'S34' in module 'first'}}
+// expected-note@first.h:* {{definition has no member 'operator int'}}
+#endif
+
+#if defined(FIRST)
+struct S35 {
+  explicit operator bool();
+};
+#elif defined(SECOND)
+struct S35 {
+  operator bool();
+};
+#else
+S35 s35;
+// expected-error@first.h:* {{'S35' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S36 {
+  int x : 3;
+};
+#elif defined(SECOND)
+struct S36 {
+  int x : 4;
+};
+#else
+S36 s36;
+// expected-error@first.h:* {{'S36' has different definitions in different modules; first difference is definition in module 'first' found bitfield 'x'}}
+// expected-note@second.h:* {{but in 'second' found bitfield 'x'}}
+#endif
+
+#if defined(FIRST)
+struct S37 {
+  mutable int x;
+  int y;
+};
+#elif defined(SECOND)
+struct S37 {
+  int x;
+  mutable int y;
+};
+#else
+S37 s37;
+// expected-error@first.h:* {{'S37' has different definitions in different modules; first difference is definition in module 'first' found mutable 'x'}}
+// expected-note@second.h:* {{but in 'second' found non-mutable 'x'}}
+#endif
+
+#if defined(FIRST)
+template <class X>
+struct S38 { };
+#elif defined(SECOND)
+template <class Y>
+struct S38 { };
+#else
+S38<int> s38;
+// expected-error@first.h:* {{'S38' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+template <class X = int>
+struct S39 { X x; };
+#elif defined(SECOND)
+template <class X = double>
+struct S39 { X x; };
+#else
+S39<> s39;
+// expected-error@first.h:* {{'S39' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+template <int X = 5>
+struct S40 { int x = X; };
+#elif defined(SECOND)
+template <int X = 7>
+struct S40 { int x = X; };
+#else
+S40<> s40;
+// expected-error@first.h:* {{'S40' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+
+#endif
+
+#if defined(FIRST)
+template <int> class T41a{};
+template <template<int> class T = T41a>
+struct S41 {};
+#elif defined(SECOND)
+template <int> class T41b{};
+template <template<int> class T = T41b>
+struct S41 {};
+#else
+using ::S41;
+// expected-error@first.h:* {{'S41' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+struct S42 {
+  void foo() const {}
+  void bar() {}
+};
+#elif defined(SECOND)
+struct S42 {
+  void foo() {}
+  void bar() const {}
+};
+#else
+S42 s42;
+// expected-error@second.h:* {{'S42::bar' from module 'second' is not present in definition of 'S42' in module 'first'}}
+// expected-note@first.h:* {{declaration of 'bar' does not match}}
+// expected-error@second.h:* {{'S42::foo' from module 'second' is not present in definition of 'S42' in module 'first'}}
+// expected-note@first.h:* {{declaration of 'foo' does not match}}
+#endif
+
+#if defined(FIRST)
+struct S43 {
+  static constexpr int x = 1;
+  int y = 1;
+};
+#elif defined(SECOND)
+struct S43 {
+  int x = 1;
+  static constexpr int y = 1;
+};
+#else
+S43 s43;
+// expected-error@second.h:* {{'S43::x' from module 'second' is not present in definition of 'S43' in module 'first'}}
+// expected-note@first.h:* {{declaration of 'x' does not match}}
+// expected-error@second.h:* {{'S43::y' from module 'second' is not present in definition of 'S43' in module 'first'}}
+// expected-note@first.h:* {{declaration of 'y' does not match}}
+//#endif
+#endif
+
+#if defined(FIRST)
+void f44();
+struct S44 {
+  friend void f44();
+};
+#elif defined(SECOND)
+void g44();
+struct S44 {
+  friend void g44();
+};
+#else
+S44 s44;
+// expected-error@first.h:* {{'S44' has different definitions in different modules; first difference is definition in module 'first' found friend 'f44'}}
+// expected-note@second.h:* {{but in 'second' found other friend 'g44'}}
+#endif
+
+#if defined(FIRST)
+struct S45 { int n : 1; };
+#elif defined(SECOND)
+struct S45 { int n = 1; };
+#else
+S45 s45;
+// expected-error@first.h:* {{'S45' has different definitions in different modules; first difference is definition in module 'first' found bitfield 'n'}}
+// expected-note@second.h:* {{but in 'second' found field 'n'}}
+#endif
+
+#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@second.h:* {{'S46::operator-' from module 'second' is not present in definition of 'S46' in module 'first'}}
+// expected-note@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@first.h:* {{'S47' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+template <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@first.h:* {{'S49' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+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@first.h:* {{'S51' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+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@first.h:* {{'S52' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+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@first.h:* {{'S53' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+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@first.h:* {{'S54' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+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@first.h:* {{'S55' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#endif
+
+#if defined(FIRST)
+template<int> 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@second.h:* {{'S57::a' from module 'second' is not present in definition of 'S57' in module 'first'}}
+// expected-note@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@first.h:* {{'S59' has different definitions in different modules; definition in module 'first' is here}}
+// expected-note@second.h:* {{definition in module 'second' is here}}
+#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
+
+// Keep macros contained to one file.
+#ifdef FIRST
+#undef FIRST
+#endif
+#ifdef SECOND
+#undef SECOND
+#endif
Index: cfe/trunk/test/Modules/merge-using-decls.cpp
===================================================================
--- cfe/trunk/test/Modules/merge-using-decls.cpp
+++ cfe/trunk/test/Modules/merge-using-decls.cpp
@@ -37,6 +37,10 @@
 // Here, we're instantiating the definition from 'A' and merging the definition
 // from 'B' into it.
 
+// expected-error@b.h:* {{'D::type' from module 'B' is not present in definition of 'D<T>' in module 'A'}}
+// expected-error@b.h:* {{'D::value' from module 'B' is not present in definition of 'D<T>' in module 'A'}}
+
+
 // expected-error@b.h:* {{'E::value' from module 'B' is not present in definition of 'E<T>' in module 'A'}}
 // expected-error@b.h:* {{'E::v' from module 'B' is not present in definition of 'E<T>' in module 'A'}}
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to