r293585 - Add better ODR checking for modules.

Sam McCall via cfe-commits cfe-commits at lists.llvm.org
Tue Jan 31 00:35:58 PST 2017


Hi Richard,

We're seeing lots of dubious errors from this internally. (I'll send you
repro instructions separately). I'm reverting because these are hard errors
- if this could be made into a warning then we could silence it while we
clean up the code or fix the checks.

Cheers,
Sam

On Tue, Jan 31, 2017 at 2:44 AM, Richard Trieu via cfe-commits <
cfe-commits at lists.llvm.org> wrote:

> Author: rtrieu
> Date: Mon Jan 30 19:44:15 2017
> New Revision: 293585
>
> URL: http://llvm.org/viewvc/llvm-project?rev=293585&view=rev
> Log:
> Add better ODR checking for modules.
>
> When objects are imported for modules, there is a chance that a name
> collision
> will cause an ODR violation.  Previously, only a small number of such
> violations were detected.  This patch provides a stronger check based on
> AST nodes.
>
> The information needed to uniquely identify an object is taked from the
> AST and
> put into a one-dimensional byte stream.  This stream is then hashed to give
> a value to represent the object, which is stored with the other object data
> in the module.
>
> When modules are loaded, and Decl's are merged, the hash values of the two
> Decl's are compared.  Only Decl's with matched hash values will be merged.
> Mismatch hashes will generate a module error, and if possible, point to the
> first difference between the two objects.
>
> The transform from AST to byte stream is a modified depth first algorithm.
> Due to references between some AST nodes, a pure depth first algorithm
> could
> generate loops.  For Stmt nodes, a straight depth first processing occurs.
> For Type and Decl nodes, they are replaced with an index number and only on
> first visit will these nodes be processed.  As an optimization, boolean
> values are saved and stored together in reverse order at the end of the
> byte stream to lower the ammount of data that needs to be hashed.
>
> Compile time impact was measured at 1.5-2.0% during module building, and
> negligible during builds without module building.
>
> Differential Revision: https://reviews.llvm.org/D21675
>
> Added:
>     cfe/trunk/include/clang/AST/ODRHash.h
>     cfe/trunk/lib/AST/ODRHash.cpp
>     cfe/trunk/test/Modules/odr_hash.cpp
> Modified:
>     cfe/trunk/include/clang/AST/DeclCXX.h
>     cfe/trunk/include/clang/AST/Stmt.h
>     cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
>     cfe/trunk/lib/AST/CMakeLists.txt
>     cfe/trunk/lib/AST/DeclCXX.cpp
>     cfe/trunk/lib/AST/StmtProfile.cpp
>     cfe/trunk/lib/Sema/SemaDecl.cpp
>     cfe/trunk/lib/Serialization/ASTReader.cpp
>     cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
>     cfe/trunk/lib/Serialization/ASTWriter.cpp
>     cfe/trunk/test/Modules/merge-using-decls.cpp
>
> Modified: cfe/trunk/include/clang/AST/DeclCXX.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/
> clang/AST/DeclCXX.h?rev=293585&r1=293584&r2=293585&view=diff
> ============================================================
> ==================
> --- cfe/trunk/include/clang/AST/DeclCXX.h (original)
> +++ cfe/trunk/include/clang/AST/DeclCXX.h Mon Jan 30 19:44:15 2017
> @@ -458,6 +458,9 @@ class CXXRecordDecl : public RecordDecl
>      /// \brief Whether we are currently parsing base specifiers.
>      unsigned IsParsingBaseSpecifiers : 1;
>
> +    /// \brief A hash of parts of the class to help in ODR checking.
> +    unsigned ODRHash;
> +
>      /// \brief The number of base class specifiers in Bases.
>      unsigned NumBases;
>
> @@ -703,6 +706,9 @@ public:
>      return data().IsParsingBaseSpecifiers;
>    }
>
> +  void computeODRHash();
> +  unsigned getODRHash() const { return data().ODRHash; }
> +
>    /// \brief Sets the base classes of this struct or class.
>    void setBases(CXXBaseSpecifier const * const *Bases, unsigned NumBases);
>
>
> Added: cfe/trunk/include/clang/AST/ODRHash.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/
> clang/AST/ODRHash.h?rev=293585&view=auto
> ============================================================
> ==================
> --- cfe/trunk/include/clang/AST/ODRHash.h (added)
> +++ cfe/trunk/include/clang/AST/ODRHash.h Mon Jan 30 19:44:15 2017
> @@ -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
>
> Modified: cfe/trunk/include/clang/AST/Stmt.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/
> clang/AST/Stmt.h?rev=293585&r1=293584&r2=293585&view=diff
> ============================================================
> ==================
> --- cfe/trunk/include/clang/AST/Stmt.h (original)
> +++ cfe/trunk/include/clang/AST/Stmt.h Mon Jan 30 19:44:15 2017
> @@ -39,6 +39,7 @@ namespace clang {
>    class Expr;
>    class IdentifierInfo;
>    class LabelDecl;
> +  class ODRHash;
>    class ParmVarDecl;
>    class PrinterHelper;
>    struct PrintingPolicy;
> @@ -436,6 +437,8 @@ public:
>    /// written in the source.
>    void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
>                 bool Canonical) const;
> +
> +  void ProcessODRHash(llvm::FoldingSetNodeID &ID, ODRHash& Hash) const;
>  };
>
>  /// DeclStmt - Adaptor class for mixing declarations with statements and
>
> Modified: cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/
> DiagnosticSerializationKinds.td?rev=293585&r1=293584&r2=293585&view=diff
> ============================================================
> ==================
> --- cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
> (original)
> +++ cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td Mon Jan
> 30 19:44:15 2017
> @@ -117,6 +117,70 @@ def note_module_odr_violation_different_
>  def err_module_odr_violation_different_instantiations : Error<
>    "instantiation of %q0 is different in different modules">;
>
> +def err_module_odr_violation_mismatch_decl : Error<
> +  "%q0 has different definitions in different modules; first difference
> is "
> +  "%select{definition in module '%2'|defined here}1 found "
> +  "%select{end of class|public access specifier|private access specifier|"
> +  "protected access specifier|friend declaration|enum|"
> +  "static assert|typedef|type alias|method|constructor|destructor|"
> +  "conversion operator|field|other}3">;
> +def note_module_odr_violation_mismatch_decl : Note<"but in '%0' found "
> +  "%select{end of class|public access specifier|private access specifier|"
> +  "protected access specifier|friend declaration|enum|"
> +  "static assert|typedef|type alias|method|constructor|destructor|"
> +  "conversion operator|field|other}1">;
> +
> +def err_module_odr_violation_mismatch_decl_diff : Error<
> +  "%q0 has different definitions in different modules; first difference
> is "
> +  "%select{definition in module '%2'|defined here}1 found "
> +  "%select{friend %4|enum %4|element %4 in enum %5|"
> +  "element %4 in enum %5 with initializer|"
> +  "element %4 in enum %5 with no initializer|"
> +  "element %4 in enum %5 with initializer|"
> +  "enum %4 has %5 element%s5|"
> +  "static assert with condition|"
> +  "static assert with message|"
> +  "static assert with %select{|no}4 message|"
> +  "%select{typedef|type alias}4 name %5|"
> +  "method named %4|"
> +  "method %4 is %select{non-|}5static|"
> +  "method %4 is %select{not |}5inline|"
> +  "method %4 is %select{not |}5const|"
> +  "method %4 has %5 parameter%s5|"
> +  "method %4 has %ordinal5 parameter %select{named %7|with no name}6|"
> +  "method %4 has %ordinal5 parameter with type %6|"
> +  "method %4 has %ordinal5 parameter with default argument|"
> +  "method %4 has %ordinal5 parameter with %select{no |}6 default
> argument|"
> +  "method %4 has %select{|no }5body|"
> +  "method %4 has different body|"
> +  "field %4|"
> +  "%select{field|bitfield}5 %4|"
> +  "%select{non-mutable|mutable}5 %4}3">;
> +def note_module_odr_violation_mismatch_decl_diff : Note<"but in '%0'
> found "
> +  "%select{other friend %2|other enum %2|different element %2 in enum %3|"
> +  "element %2 in enum %3 with initializer|"
> +  "element %2 in enum %3 with no initializer|"
> +  "element %2 in enum %3 with different initializer|"
> +  "enum %2 has %3 element%s3|"
> +  "static assert with different condition|"
> +  "static assert with different message|"
> +  "static assert with %select{|no}2 message|"
> +  "different %select{typedef|type alias}2 name %3|"
> +  "method named %2|"
> +  "method %2 is %select{non-|}3static|"
> +  "method %2 is %select{not |}3inline|"
> +  "method %2 is %select{not |}3const|"
> +  "method %2 has %3 parameter%s3|"
> +  "method %2 has %ordinal3 parameter %select{named %5|with no name}4|"
> +  "method %2 has %ordinal3 parameter with type %4|"
> +  "method %2 has %ordinal3 parameter with different default argument|"
> +  "method %2 has %ordinal3 parameter with %select{no |}4default argument|"
> +  "method %2 has %select{|no }3body|"
> +  "method %2 has different body|"
> +  "field %2|"
> +  "%select{field|bitfield}3 %2|"
> +  "%select{non-mutable|mutable}3 %2}1">;
> +
>  def warn_module_uses_date_time : Warning<
>    "%select{precompiled header|module}0 uses __DATE__ or __TIME__">,
>    InGroup<DiagGroup<"pch-date-time">>;
>
> Modified: cfe/trunk/lib/AST/CMakeLists.txt
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/
> CMakeLists.txt?rev=293585&r1=293584&r2=293585&view=diff
> ============================================================
> ==================
> --- cfe/trunk/lib/AST/CMakeLists.txt (original)
> +++ cfe/trunk/lib/AST/CMakeLists.txt Mon Jan 30 19:44:15 2017
> @@ -40,6 +40,7 @@ add_clang_library(clangAST
>    MicrosoftMangle.cpp
>    NestedNameSpecifier.cpp
>    NSAPI.cpp
> +  ODRHash.cpp
>    OpenMPClause.cpp
>    ParentMap.cpp
>    RawCommentList.cpp
>
> Modified: cfe/trunk/lib/AST/DeclCXX.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/
> DeclCXX.cpp?rev=293585&r1=293584&r2=293585&view=diff
> ============================================================
> ==================
> --- cfe/trunk/lib/AST/DeclCXX.cpp (original)
> +++ cfe/trunk/lib/AST/DeclCXX.cpp Mon Jan 30 19:44:15 2017
> @@ -18,6 +18,7 @@
>  #include "clang/AST/DeclTemplate.h"
>  #include "clang/AST/Expr.h"
>  #include "clang/AST/ExprCXX.h"
> +#include "clang/AST/ODRHash.h"
>  #include "clang/AST/TypeLoc.h"
>  #include "clang/Basic/IdentifierTable.h"
>  #include "llvm/ADT/STLExtras.h"
> @@ -71,8 +72,8 @@ CXXRecordDecl::DefinitionData::Definitio
>        ImplicitCopyAssignmentHasConstParam(true),
>        HasDeclaredCopyConstructorWithConstParam(false),
>        HasDeclaredCopyAssignmentWithConstParam(false), IsLambda(false),
> -      IsParsingBaseSpecifiers(false), NumBases(0), NumVBases(0), Bases(),
> -      VBases(), Definition(D), FirstFriend() {}
> +      IsParsingBaseSpecifiers(false), ODRHash(0), NumBases(0),
> NumVBases(0),
> +      Bases(), VBases(), Definition(D), FirstFriend() {}
>
>  CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase()
> const {
>    return Bases.get(Definition->getASTContext().getExternalSource());
> @@ -371,6 +372,16 @@ CXXRecordDecl::setBases(CXXBaseSpecifier
>    data().IsParsingBaseSpecifiers = false;
>  }
>
> +void CXXRecordDecl::computeODRHash() {
> +  if (!DefinitionData)
> +    return;
> +
> +  ODRHash Hash;
> +  Hash.AddCXXRecordDecl(this);
> +
> +  DefinitionData->ODRHash = Hash.CalculateHash();
> +}
> +
>  void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) {
>    // C++11 [class.copy]p11:
>    //   A defaulted copy/move constructor for a class X is defined as
>
> Added: cfe/trunk/lib/AST/ODRHash.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/
> ODRHash.cpp?rev=293585&view=auto
> ============================================================
> ==================
> --- cfe/trunk/lib/AST/ODRHash.cpp (added)
> +++ cfe/trunk/lib/AST/ODRHash.cpp Mon Jan 30 19:44:15 2017
> @@ -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);
> +}
>
> Modified: cfe/trunk/lib/AST/StmtProfile.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/
> StmtProfile.cpp?rev=293585&r1=293584&r2=293585&view=diff
> ============================================================
> ==================
> --- cfe/trunk/lib/AST/StmtProfile.cpp (original)
> +++ cfe/trunk/lib/AST/StmtProfile.cpp Mon Jan 30 19:44:15 2017
> @@ -19,20 +19,22 @@
>  #include "clang/AST/ExprCXX.h"
>  #include "clang/AST/ExprObjC.h"
>  #include "clang/AST/ExprOpenMP.h"
> +#include "clang/AST/ODRHash.h"
>  #include "clang/AST/StmtVisitor.h"
>  #include "llvm/ADT/FoldingSet.h"
>  using namespace clang;
>
>  namespace {
>    class StmtProfiler : public ConstStmtVisitor<StmtProfiler> {
> +   protected:
>      llvm::FoldingSetNodeID &ID;
> -    const ASTContext &Context;
>      bool Canonical;
>
>    public:
> -    StmtProfiler(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
> -                 bool Canonical)
> -      : ID(ID), Context(Context), Canonical(Canonical) { }
> +    StmtProfiler(llvm::FoldingSetNodeID &ID, bool Canonical)
> +        : ID(ID), Canonical(Canonical) {}
> +
> +    virtual ~StmtProfiler() {}
>
>      void VisitStmt(const Stmt *S);
>
> @@ -41,22 +43,25 @@ namespace {
>
>      /// \brief Visit a declaration that is referenced within an expression
>      /// or statement.
> -    void VisitDecl(const Decl *D);
> +    virtual void VisitDecl(const Decl *D) = 0;
>
>      /// \brief Visit a type that is referenced within an expression or
>      /// statement.
> -    void VisitType(QualType T);
> +    virtual void VisitType(QualType T) = 0;
>
>      /// \brief Visit a name that occurs within an expression or statement.
> -    void VisitName(DeclarationName Name);
> +    virtual void VisitName(DeclarationName Name) = 0;
> +
> +    /// \brief Visit identifiers that are not in Decl's or Type's.
> +    virtual void VisitIdentifierInfo(IdentifierInfo *II) = 0;
>
>      /// \brief Visit a nested-name-specifier that occurs within an
> expression
>      /// or statement.
> -    void VisitNestedNameSpecifier(NestedNameSpecifier *NNS);
> +    virtual void VisitNestedNameSpecifier(NestedNameSpecifier *NNS) = 0;
>
>      /// \brief Visit a template name that occurs within an expression or
>      /// statement.
> -    void VisitTemplateName(TemplateName Name);
> +    virtual void VisitTemplateName(TemplateName Name) = 0;
>
>      /// \brief Visit template arguments that occur within an expression or
>      /// statement.
> @@ -66,6 +71,127 @@ namespace {
>      /// \brief Visit a single template argument.
>      void VisitTemplateArgument(const TemplateArgument &Arg);
>    };
> +
> +  class StmtProfilerWithPointers : public StmtProfiler {
> +    const ASTContext &Context;
> +
> +  public:
> +    StmtProfilerWithPointers(llvm::FoldingSetNodeID &ID,
> +                             const ASTContext &Context, bool Canonical)
> +        : StmtProfiler(ID, Canonical), Context(Context) {}
> +  private:
> +    void VisitDecl(const Decl *D) override {
> +      ID.AddInteger(D ? D->getKind() : 0);
> +
> +      if (Canonical && D) {
> +        if (const NonTypeTemplateParmDecl *NTTP =
> +                dyn_cast<NonTypeTemplateParmDecl>(D)) {
> +          ID.AddInteger(NTTP->getDepth());
> +          ID.AddInteger(NTTP->getIndex());
> +          ID.AddBoolean(NTTP->isParameterPack());
> +          VisitType(NTTP->getType());
> +          return;
> +        }
> +
> +        if (const ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D)) {
> +          // The Itanium C++ ABI uses the type, scope depth, and scope
> +          // index of a parameter when mangling expressions that involve
> +          // function parameters, so we will use the parameter's type for
> +          // establishing function parameter identity. That way, our
> +          // definition of "equivalent" (per C++ [temp.over.link]) is at
> +          // least as strong as the definition of "equivalent" used for
> +          // name mangling.
> +          VisitType(Parm->getType());
> +          ID.AddInteger(Parm->getFunctionScopeDepth());
> +          ID.AddInteger(Parm->getFunctionScopeIndex());
> +          return;
> +        }
> +
> +        if (const TemplateTypeParmDecl *TTP =
> +                dyn_cast<TemplateTypeParmDecl>(D)) {
> +          ID.AddInteger(TTP->getDepth());
> +          ID.AddInteger(TTP->getIndex());
> +          ID.AddBoolean(TTP->isParameterPack());
> +          return;
> +        }
> +
> +        if (const TemplateTemplateParmDecl *TTP =
> +                dyn_cast<TemplateTemplateParmDecl>(D)) {
> +          ID.AddInteger(TTP->getDepth());
> +          ID.AddInteger(TTP->getIndex());
> +          ID.AddBoolean(TTP->isParameterPack());
> +          return;
> +        }
> +      }
> +
> +      ID.AddPointer(D ? D->getCanonicalDecl() : nullptr);
> +    }
> +
> +    void VisitType(QualType T) override {
> +      if (Canonical)
> +        T = Context.getCanonicalType(T);
> +
> +      ID.AddPointer(T.getAsOpaquePtr());
> +    }
> +
> +    void VisitName(DeclarationName Name) override {
> +      ID.AddPointer(Name.getAsOpaquePtr());
> +    }
> +
> +    void VisitIdentifierInfo(IdentifierInfo *II) override {
> +      ID.AddPointer(II);
> +    }
> +
> +    void VisitNestedNameSpecifier(NestedNameSpecifier *NNS) override {
> +      if (Canonical)
> +        NNS = Context.getCanonicalNestedNameSpecifier(NNS);
> +      ID.AddPointer(NNS);
> +    }
> +
> +    void VisitTemplateName(TemplateName Name) override {
> +      if (Canonical)
> +        Name = Context.getCanonicalTemplateName(Name);
> +
> +      Name.Profile(ID);
> +    }
> +  };
> +
> +  class StmtProfilerWithoutPointers : public StmtProfiler {
> +    ODRHash &Hash;
> +  public:
> +    StmtProfilerWithoutPointers(llvm::FoldingSetNodeID &ID, ODRHash
> &Hash)
> +        : StmtProfiler(ID, false), Hash(Hash) {}
> +
> +  private:
> +    void VisitType(QualType T) override {
> +      Hash.AddQualType(T);
> +    }
> +
> +    void VisitName(DeclarationName Name) override {
> +      Hash.AddDeclarationName(Name);
> +    }
> +    void VisitIdentifierInfo(IdentifierInfo *II) override {
> +      ID.AddBoolean(II);
> +      if (II) {
> +        Hash.AddIdentifierInfo(II);
> +      }
> +    }
> +    void VisitDecl(const Decl *D) override {
> +      ID.AddBoolean(D);
> +      if (D) {
> +        Hash.AddDecl(D);
> +      }
> +    }
> +    void VisitTemplateName(TemplateName Name) override {
> +      Hash.AddTemplateName(Name);
> +    }
> +    void VisitNestedNameSpecifier(NestedNameSpecifier *NNS) override {
> +      ID.AddBoolean(NNS);
> +      if (NNS) {
> +        Hash.AddNestedNameSpecifier(NNS);
> +      }
> +    }
> +  };
>  }
>
>  void StmtProfiler::VisitStmt(const Stmt *S) {
> @@ -853,7 +979,7 @@ void StmtProfiler::VisitOffsetOfExpr(con
>        break;
>
>      case OffsetOfNode::Identifier:
> -      ID.AddPointer(ON.getFieldName());
> +      VisitIdentifierInfo(ON.getFieldName());
>        break;
>
>      case OffsetOfNode::Base:
> @@ -861,7 +987,7 @@ void StmtProfiler::VisitOffsetOfExpr(con
>        break;
>      }
>    }
> -
> +
>    VisitExpr(S);
>  }
>
> @@ -1451,7 +1577,7 @@ StmtProfiler::VisitCXXPseudoDestructorEx
>    if (S->getDestroyedTypeInfo())
>      VisitType(S->getDestroyedType());
>    else
> -    ID.AddPointer(S->getDestroyedTypeIdentifier());
> +    VisitIdentifierInfo(S->getDestroyedTypeIdentifier());
>  }
>
>  void StmtProfiler::VisitOverloadExpr(const OverloadExpr *S) {
> @@ -1701,77 +1827,6 @@ void StmtProfiler::VisitObjCAvailability
>    VisitExpr(S);
>  }
>
> -void StmtProfiler::VisitDecl(const Decl *D) {
> -  ID.AddInteger(D? D->getKind() : 0);
> -
> -  if (Canonical && D) {
> -    if (const NonTypeTemplateParmDecl *NTTP =
> -          dyn_cast<NonTypeTemplateParmDecl>(D)) {
> -      ID.AddInteger(NTTP->getDepth());
> -      ID.AddInteger(NTTP->getIndex());
> -      ID.AddBoolean(NTTP->isParameterPack());
> -      VisitType(NTTP->getType());
> -      return;
> -    }
> -
> -    if (const ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D)) {
> -      // The Itanium C++ ABI uses the type, scope depth, and scope
> -      // index of a parameter when mangling expressions that involve
> -      // function parameters, so we will use the parameter's type for
> -      // establishing function parameter identity. That way, our
> -      // definition of "equivalent" (per C++ [temp.over.link]) is at
> -      // least as strong as the definition of "equivalent" used for
> -      // name mangling.
> -      VisitType(Parm->getType());
> -      ID.AddInteger(Parm->getFunctionScopeDepth());
> -      ID.AddInteger(Parm->getFunctionScopeIndex());
> -      return;
> -    }
> -
> -    if (const TemplateTypeParmDecl *TTP =
> -          dyn_cast<TemplateTypeParmDecl>(D)) {
> -      ID.AddInteger(TTP->getDepth());
> -      ID.AddInteger(TTP->getIndex());
> -      ID.AddBoolean(TTP->isParameterPack());
> -      return;
> -    }
> -
> -    if (const TemplateTemplateParmDecl *TTP =
> -          dyn_cast<TemplateTemplateParmDecl>(D)) {
> -      ID.AddInteger(TTP->getDepth());
> -      ID.AddInteger(TTP->getIndex());
> -      ID.AddBoolean(TTP->isParameterPack());
> -      return;
> -    }
> -  }
> -
> -  ID.AddPointer(D? D->getCanonicalDecl() : nullptr);
> -}
> -
> -void StmtProfiler::VisitType(QualType T) {
> -  if (Canonical)
> -    T = Context.getCanonicalType(T);
> -
> -  ID.AddPointer(T.getAsOpaquePtr());
> -}
> -
> -void StmtProfiler::VisitName(DeclarationName Name) {
> -  ID.AddPointer(Name.getAsOpaquePtr());
> -}
> -
> -void StmtProfiler::VisitNestedNameSpecifier(NestedNameSpecifier *NNS) {
> -  if (Canonical)
> -    NNS = Context.getCanonicalNestedNameSpecifier(NNS);
> -  ID.AddPointer(NNS);
> -}
> -
> -void StmtProfiler::VisitTemplateName(TemplateName Name) {
> -  if (Canonical)
> -    Name = Context.getCanonicalTemplateName(Name);
> -
> -  Name.Profile(ID);
> -}
> -
>  void StmtProfiler::VisitTemplateArguments(const TemplateArgumentLoc
> *Args,
>                                            unsigned NumArgs) {
>    ID.AddInteger(NumArgs);
> @@ -1821,6 +1876,12 @@ void StmtProfiler::VisitTemplateArgument
>
>  void Stmt::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
>                     bool Canonical) const {
> -  StmtProfiler Profiler(ID, Context, Canonical);
> +  StmtProfilerWithPointers Profiler(ID, Context, Canonical);
> +  Profiler.Visit(this);
> +}
> +
> +void Stmt::ProcessODRHash(llvm::FoldingSetNodeID &ID,
> +                          class ODRHash &Hash) const {
> +  StmtProfilerWithoutPointers Profiler(ID, Hash);
>    Profiler.Visit(this);
>  }
>
> Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/
> SemaDecl.cpp?rev=293585&r1=293584&r2=293585&view=diff
> ============================================================
> ==================
> --- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaDecl.cpp Mon Jan 30 19:44:15 2017
> @@ -13705,8 +13705,11 @@ void Sema::ActOnTagFinishDefinition(Scop
>        RD->completeDefinition();
>    }
>
> -  if (isa<CXXRecordDecl>(Tag))
> +  if (auto *RD = dyn_cast<CXXRecordDecl>(Tag)) {
>      FieldCollector->FinishClass();
> +    if (Context.getLangOpts().Modules)
> +      RD->computeODRHash();
> +  }
>
>    // Exit this scope of this tag's definition.
>    PopDeclContext();
>
> Modified: cfe/trunk/lib/Serialization/ASTReader.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/
> Serialization/ASTReader.cpp?rev=293585&r1=293584&r2=293585&view=diff
> ============================================================
> ==================
> --- cfe/trunk/lib/Serialization/ASTReader.cpp (original)
> +++ cfe/trunk/lib/Serialization/ASTReader.cpp Mon Jan 30 19:44:15 2017
> @@ -26,6 +26,7 @@
>  #include "clang/AST/Expr.h"
>  #include "clang/AST/ExprCXX.h"
>  #include "clang/AST/NestedNameSpecifier.h"
> +#include "clang/AST/ODRHash.h"
>  #include "clang/AST/RawCommentList.h"
>  #include "clang/AST/Type.h"
>  #include "clang/AST/TypeLocVisitor.h"
> @@ -8884,21 +8885,638 @@ void ASTReader::diagnoseOdrViolations()
>      for (auto *RD : Merge.second) {
>        // Multiple different declarations got merged together; tell the
> user
>        // where they came from.
> -      if (Merge.first != RD) {
> -        // FIXME: Walk the definition, figure out what's different,
> -        // and diagnose that.
> -        if (!Diagnosed) {
> -          std::string Module = getOwningModuleNameForDiagnost
> ic(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 = getOwningModuleNameForDiagnost
> ic(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 = getOwningModuleNameForDiagnost
> ic(Merge.first);
> +      std::string SecondModule = getOwningModuleNameForDiagnostic(RD);
> +
> +      if (FirstDiffType != SecondDiffType) {
> +        SourceLocation FirstLoc;
> +        SourceRange FirstRange;
> +        if (FirstDiffType == EndOfClass) {
> +          FirstLoc = Merge.first->getBraceRange().getEnd();
> +        } else {
> +          FirstLoc = FirstIt->first->getLocation();
> +          FirstRange = FirstIt->first->getSourceRange();
> +        }
> +        Diag(FirstLoc, diag::err_module_odr_violation_mismatch_decl)
> +            << Merge.first << FirstModule.empty() << FirstModule <<
> FirstRange
> +            << FirstDiffType;
> +
> +        SourceLocation SecondLoc;
> +        SourceRange SecondRange;
> +        if (SecondDiffType == EndOfClass) {
> +          SecondLoc = RD->getBraceRange().getEnd();
> +        } else {
> +          SecondLoc = SecondDecl->getLocation();
> +          SecondRange = SecondDecl->getSourceRange();
> +        }
> +        Diag(SecondLoc, diag::note_module_odr_violation_mismatch_decl)
> +            << SecondModule << SecondRange << SecondDiffType;
> +        Diagnosed = true;
> +        break;
> +      }
> +
> +      // Used with err_module_odr_violation_mismatch_decl_diff and
> +      // note_module_odr_violation_mismatch_decl_diff
> +      enum ODRDeclDifference{
> +        FriendName,
> +        EnumName,
> +        EnumConstantName,
> +        EnumConstantInit,
> +        EnumConstantNoInit,
> +        EnumConstantDiffInit,
> +        EnumNumberOfConstants,
> +        StaticAssertCondition,
> +        StaticAssertMessage,
> +        StaticAssertOnlyMessage,
> +        TypedefName,
> +        MethodName,
> +        MethodStatic,
> +        MethodInline,
> +        MethodConst,
> +        MethodNumParams,
> +        MethodParamName,
> +        MethodParamType,
> +        MethodDefaultArg,
> +        MethodOnlyDefaultArg,
> +        MethodOnlyBody,
> +        MethodBody,
> +        FieldName,
> +        FieldSingleBitField,
> +        FieldMutable,
> +      };
> +
> +      // These lambdas have the common portions of the ODR diagnostics.
> This
> +      // has the same return as Diag(), so addition parameters can be
> passed
> +      // in with operator<<
> +      auto ODRDiagError = [&Merge, &FirstModule, this](
> +          SourceLocation Loc, SourceRange Range, ODRDeclDifference
> DiffType) {
> +        return Diag(Loc, diag::err_module_odr_
> violation_mismatch_decl_diff)
> +               << Merge.first << FirstModule.empty() << FirstModule <<
> Range
> +               << DiffType;
> +      };
> +      auto ODRDiagNote = [&SecondModule, this](
> +          SourceLocation Loc, SourceRange Range, ODRDeclDifference
> DiffType) {
> +        return Diag(Loc, diag::note_module_odr_
> violation_mismatch_decl_diff)
> +               << SecondModule << Range << DiffType;
> +      };
> +
> +      auto ComputeODRHash = [&Hash](const Stmt* S) {
> +        assert(S);
> +        Hash.clear();
> +        Hash.AddStmt(S);
> +        return Hash.CalculateHash();
> +      };
> +
> +      // At this point, both decls are of the same type.  Dive down
> deeper into
> +      // the Decl to determine where the first difference is located.
> +      switch (FirstDiffType) {
> +      case Friend: {
> +        FriendDecl *FirstFriend = cast<FriendDecl>(FirstDecl);
> +        FriendDecl *SecondFriend = cast<FriendDecl>(SecondDecl);
> +        {
> +          auto D = ODRDiagError(FirstFriend->getFriendLoc(),
> +                                FirstFriend->getSourceRange(),
> FriendName);
> +          if (TypeSourceInfo *FirstTSI = FirstFriend->getFriendType())
> +            D << FirstTSI->getType();
> +          else
> +            D << FirstFriend->getFriendDecl();
> +        }
> +        {
> +          auto D = ODRDiagNote(SecondFriend->getFriendLoc(),
> +                               SecondFriend->getSourceRange(),
> FriendName);
> +          if (TypeSourceInfo *SecondTSI = SecondFriend->getFriendType())
> +            D << SecondTSI->getType();
> +          else
> +            D << SecondFriend->getFriendDecl();
> +        }
> +        Diagnosed = true;
> +        break;
> +      }
> +      case Enum: {
> +        EnumDecl *FirstEnum = cast<EnumDecl>(FirstDecl);
> +        EnumDecl *SecondEnum = cast<EnumDecl>(SecondDecl);
> +        if (FirstEnum->getName() != SecondEnum->getName()) {
> +          ODRDiagError(FirstEnum->getLocStart(),
> FirstEnum->getSourceRange(),
> +                       EnumName)
> +              << FirstEnum;
> +          ODRDiagNote(SecondEnum->getLocStart(),
> SecondEnum->getSourceRange(),
> +                      EnumName)
> +              << SecondEnum;
> +          Diagnosed = true;
> +          break;
> +        }
> +
> +        // Don't use EnumDecl::enumerator_{begin,end}.  Decl merging can
> +        // cause the iterators from them to be the same for both Decl's.
> +        EnumDecl::enumerator_iterator FirstEnumIt(FirstEnum->decls_
> begin());
> +        EnumDecl::enumerator_iterator FirstEnumEnd(FirstEnum->decls_
> end());
> +        EnumDecl::enumerator_iterator SecondEnumIt(SecondEnum->
> decls_begin());
> +        EnumDecl::enumerator_iterator SecondEnumEnd(SecondEnum->
> decls_end());
> +        int NumElements = 0;
> +        for (; FirstEnumIt != FirstEnumEnd && SecondEnumIt !=
> SecondEnumEnd;
> +             ++FirstEnumIt, ++SecondEnumIt, ++NumElements) {
> +          if (FirstEnumIt->getName() != SecondEnumIt->getName()) {
> +            ODRDiagError(FirstEnumIt->getLocStart(),
> +                         FirstEnumIt->getSourceRange(), EnumConstantName)
> +                << *FirstEnumIt << FirstEnum;
> +            ODRDiagNote(SecondEnumIt->getLocStart(),
> +                        SecondEnumIt->getSourceRange(), EnumConstantName)
> +                << *SecondEnumIt << SecondEnum;
> +            Diagnosed = true;
> +            break;
> +          }
> +          Expr *FirstInit = FirstEnumIt->getInitExpr();
> +          Expr *SecondInit = SecondEnumIt->getInitExpr();
> +
> +          if (FirstInit && !SecondInit) {
> +            ODRDiagError(FirstEnumIt->getLocStart(),
> +                         FirstEnumIt->getSourceRange(), EnumConstantInit)
> +                << *FirstEnumIt << FirstEnum;
> +
> +            ODRDiagNote(SecondEnumIt->getLocStart(),
> +                        SecondEnumIt->getSourceRange(),
> EnumConstantNoInit)
> +                << *SecondEnumIt << SecondEnum;
> +            Diagnosed = true;
> +            break;
> +          }
> +
> +          if (!FirstInit && SecondInit) {
> +            ODRDiagError(FirstEnumIt->getLocStart(),
> +                         FirstEnumIt->getSourceRange(),
> EnumConstantNoInit)
> +                << *FirstEnumIt << FirstEnum;
> +            ODRDiagNote(SecondEnumIt->getLocStart(),
> +                        SecondEnumIt->getSourceRange(), EnumConstantInit)
> +                << *SecondEnumIt << SecondEnum;
> +            Diagnosed = true;
> +            break;
> +          }
> +
> +          if (FirstInit == SecondInit)
> +            continue;
> +
> +          unsigned FirstODRHash = ComputeODRHash(FirstInit);
> +          unsigned SecondODRHash = ComputeODRHash(SecondInit);
> +
> +          if (FirstODRHash != SecondODRHash) {
> +            ODRDiagError(FirstEnumIt->getLocStart(),
> +                         FirstEnumIt->getSourceRange(),
> EnumConstantDiffInit)
> +                << *FirstEnumIt << FirstEnum;
> +            ODRDiagNote(SecondEnumIt->getLocStart(),
> +                        SecondEnumIt->getSourceRange(),
> EnumConstantDiffInit)
> +                << *SecondEnumIt << SecondEnum;
> +            Diagnosed = true;
> +            break;
> +          }
> +        }
> +
> +        if (FirstEnumIt == FirstEnumEnd && SecondEnumIt != SecondEnumEnd)
> {
> +          unsigned FirstEnumSize = NumElements;
> +          unsigned SecondEnumSize = NumElements;
> +          for (; SecondEnumIt != SecondEnumEnd; ++SecondEnumIt)
> +            ++SecondEnumSize;
> +          ODRDiagError(FirstEnum->getLocStart(),
> FirstEnum->getSourceRange(),
> +                       EnumNumberOfConstants)
> +              << FirstEnum << FirstEnumSize;
> +          ODRDiagNote(SecondEnum->getLocStart(),
> SecondEnum->getSourceRange(),
> +                      EnumNumberOfConstants)
> +              << SecondEnum << SecondEnumSize;
> +          Diagnosed = true;
> +          break;
> +        }
> +
> +        if (FirstEnumIt != FirstEnumEnd && SecondEnumIt == SecondEnumEnd)
> {
> +          unsigned FirstEnumSize = NumElements;
> +          unsigned SecondEnumSize = NumElements;
> +          for (; FirstEnumIt != FirstEnumEnd; ++FirstEnumIt)
> +            ++FirstEnumSize;
> +          ODRDiagError(FirstEnum->getLocStart(),
> FirstEnum->getSourceRange(),
> +                       EnumNumberOfConstants)
> +              << FirstEnum << FirstEnumSize;
> +          ODRDiagNote(SecondEnum->getLocStart(),
> SecondEnum->getSourceRange(),
> +                      EnumNumberOfConstants)
> +              << SecondEnum << SecondEnumSize;
> +          Diagnosed = true;
> +          break;
> +        }
> +
> +        break;
> +      }
> +      case StaticAssert: {
> +        StaticAssertDecl *FirstSA = cast<StaticAssertDecl>(FirstDecl);
> +        StaticAssertDecl *SecondSA = cast<StaticAssertDecl>(SecondDecl);
> +
> +        Expr *FirstExpr = FirstSA->getAssertExpr();
> +        Expr *SecondExpr = SecondSA->getAssertExpr();
> +        unsigned FirstODRHash = ComputeODRHash(FirstExpr);
> +        unsigned SecondODRHash = ComputeODRHash(SecondExpr);
> +        if (FirstODRHash != SecondODRHash) {
> +          ODRDiagError(FirstExpr->getLocStart(),
> FirstExpr->getSourceRange(),
> +                       StaticAssertCondition);
> +          ODRDiagNote(SecondExpr->getLocStart(),
> +                      SecondExpr->getSourceRange(),
> StaticAssertCondition);
> +          Diagnosed = true;
> +          break;
> +        }
> +
> +        StringLiteral *FirstStr = FirstSA->getMessage();
> +        StringLiteral *SecondStr = SecondSA->getMessage();
> +        if ((FirstStr && !SecondStr) || (!FirstStr && SecondStr)) {
> +          SourceLocation FirstLoc, SecondLoc;
> +          SourceRange FirstRange, SecondRange;
> +          if (FirstStr) {
> +            FirstLoc = FirstStr->getLocStart();
> +            FirstRange = FirstStr->getSourceRange();
> +          } else {
> +            FirstLoc = FirstSA->getLocStart();
> +            FirstRange = FirstSA->getSourceRange();
> +          }
> +          if (SecondStr) {
> +            SecondLoc = SecondStr->getLocStart();
> +            SecondRange = SecondStr->getSourceRange();
> +          } else {
> +            SecondLoc = SecondSA->getLocStart();
> +            SecondRange = SecondSA->getSourceRange();
> +          }
> +          ODRDiagError(FirstLoc, FirstRange, StaticAssertOnlyMessage)
> +              << (FirstStr == nullptr);
> +          ODRDiagNote(SecondLoc, SecondRange, StaticAssertOnlyMessage)
> +              << (SecondStr == nullptr);
> +          Diagnosed = true;
> +          break;
> +        }
> +
> +        if (FirstStr && SecondStr &&
> +            FirstStr->getString() != SecondStr->getString()) {
> +          ODRDiagError(FirstStr->getLocStart(),
> FirstStr->getSourceRange(),
> +                       StaticAssertMessage);
> +          ODRDiagNote(SecondStr->getLocStart(),
> SecondStr->getSourceRange(),
> +                      StaticAssertMessage);
> +          Diagnosed = true;
> +          break;
> +        }
> +        break;
> +      }
> +      case Typedef:
> +      case TypeAlias: {
> +        TypedefNameDecl *FirstTD = cast<TypedefNameDecl>(FirstDecl);
> +        TypedefNameDecl *SecondTD = cast<TypedefNameDecl>(SecondDecl);
> +        IdentifierInfo *FirstII = FirstTD->getIdentifier();
> +        IdentifierInfo *SecondII = SecondTD->getIdentifier();
> +        if (FirstII && SecondII && FirstII->getName() !=
> SecondII->getName()) {
> +          ODRDiagError(FirstTD->getLocation(), FirstTD->getSourceRange(),
> +                       TypedefName)
> +              << (FirstDiffType == TypeAlias) << FirstII;
> +          ODRDiagNote(SecondTD->getLocation(),
> SecondTD->getSourceRange(),
> +                      TypedefName)
> +              << (FirstDiffType == TypeAlias) << SecondII;
> +          Diagnosed = true;
> +          break;
> +        }
> +        break;
> +      }
> +      case CXXMethod:
> +      case CXXConstructor:
> +      case CXXConversion:
> +      case CXXDestructor: {
> +        // TODO: Merge with existing method diff logic.
> +        CXXMethodDecl *FirstMD = cast<CXXMethodDecl>(FirstDecl);
> +        CXXMethodDecl *SecondMD = cast<CXXMethodDecl>(SecondDecl);
> +        IdentifierInfo *FirstII = FirstMD->getIdentifier();
> +        IdentifierInfo *SecondII = SecondMD->getIdentifier();
> +        if (FirstII && SecondII && FirstII->getName() !=
> SecondII->getName()) {
> +          ODRDiagError(FirstMD->getLocation(), FirstMD->getSourceRange(),
> +                       MethodName)
> +              << FirstII;
> +          ODRDiagNote(SecondMD->getLocation(),
> SecondMD->getSourceRange(),
> +                      MethodName)
> +              << SecondII;
> +          Diagnosed = true;
> +          break;
> +        }
> +
> +        bool FirstStatic = FirstMD->getStorageClass() == SC_Static;
> +        bool SecondStatic = SecondMD->getStorageClass() == SC_Static;
> +        if (FirstStatic != SecondStatic) {
> +          ODRDiagError(FirstMD->getLocation(), FirstMD->getSourceRange(),
> +                       MethodStatic)
> +              << FirstMD << FirstStatic;
> +          ODRDiagNote(SecondMD->getLocation(),
> SecondMD->getSourceRange(),
> +                      MethodStatic)
> +              << SecondMD << SecondStatic;
> +          Diagnosed = true;
> +          break;
> +        }
> +
> +        bool FirstInline = FirstMD->isInlineSpecified();
> +        bool SecondInline = SecondMD->isInlineSpecified();
> +        if (FirstInline != SecondInline) {
> +          ODRDiagError(FirstMD->getLocation(), FirstMD->getSourceRange(),
> +                       MethodInline)
> +              << FirstMD << FirstInline;
> +          ODRDiagNote(SecondMD->getLocation(),
> SecondMD->getSourceRange(),
> +                      MethodInline)
> +              << SecondMD << SecondInline;
> +          Diagnosed = true;
> +          break;
> +        }
> +
> +        bool FirstConst = FirstMD->isConst();
> +        bool SecondConst = SecondMD->isConst();
> +        if (FirstConst != SecondConst) {
> +          ODRDiagError(FirstMD->getLocation(), FirstMD->getSourceRange(),
> +                       MethodConst)
> +              << FirstMD << FirstInline;
> +          ODRDiagNote(SecondMD->getLocation(),
> SecondMD->getSourceRange(),
> +                      MethodConst)
> +              << SecondMD << SecondInline;
> +          Diagnosed = true;
> +          break;
> +        }
> +
> +        if (FirstMD->getNumParams() != SecondMD->getNumParams()) {
> +          ODRDiagError(FirstMD->getLocation(), FirstMD->getSourceRange(),
> +                       MethodNumParams)
> +              << SecondMD << FirstMD->getNumParams();
> +          ODRDiagNote(SecondMD->getLocation(),
> SecondMD->getSourceRange(),
> +                      MethodNumParams)
> +              << SecondMD << SecondMD->getNumParams();
> +          Diagnosed = true;
> +          break;
> +        }
> +
> +        for (unsigned i = 0, e = FirstMD->getNumParams(); i < e; ++i) {
> +          ParmVarDecl *FirstParam = FirstMD->getParamDecl(i);
> +          ParmVarDecl *SecondParam = SecondMD->getParamDecl(i);
> +          IdentifierInfo *FirstII = FirstParam->getIdentifier();
> +          IdentifierInfo *SecondII = SecondParam->getIdentifier();
> +          if ((!FirstII && SecondII) || (FirstII && !SecondII) ||
> +              (FirstII && SecondII &&
> +               FirstII->getName() != SecondII->getName())) {
> +            ODRDiagError(FirstParam->getLocation(),
> +                         FirstParam->getSourceRange(), MethodParamName)
> +                << SecondMD << i + 1 << (FirstII == nullptr) << FirstII;
> +            ODRDiagNote(SecondParam->getLocation(),
> +                        SecondParam->getSourceRange(), MethodParamName)
> +                << SecondMD << i + 1 << (SecondII == nullptr) << SecondII;
> +            Diagnosed = true;
> +            break;
> +          }
> +
> +          if (FirstParam->getType() != SecondParam->getType()) {
> +            ODRDiagError(FirstParam->getLocation(),
> +                         FirstParam->getSourceRange(), MethodParamType)
> +                << SecondMD << i + 1 << FirstParam->getType();
> +            ODRDiagNote(SecondParam->getLocation(),
> +                        SecondParam->getSourceRange(), MethodParamType)
> +                << SecondMD << i + 1 << SecondParam->getType();
> +            Diagnosed = true;
> +            break;
> +          }
> +
> +          Expr *FirstDefaultArg = FirstParam->getDefaultArg();
> +          Expr *SecondDefaultArg = SecondParam->getDefaultArg();
> +          if ((!FirstDefaultArg && SecondDefaultArg) ||
> +              (FirstDefaultArg && !SecondDefaultArg)) {
> +            ODRDiagError(FirstParam->getLocation(),
> +                         FirstParam->getSourceRange(),
> MethodOnlyDefaultArg)
> +                << SecondMD << i + 1 << (FirstDefaultArg != nullptr);
> +            ODRDiagNote(SecondParam->getLocation(),
> +                        SecondParam->getSourceRange(),
> MethodOnlyDefaultArg)
> +                << SecondMD << i + 1 << (SecondDefaultArg != nullptr);
> +            Diagnosed = true;
> +            break;
> +          }
> +
> +          if (FirstDefaultArg && SecondDefaultArg) {
> +            unsigned FirstODRHash = ComputeODRHash(FirstDefaultArg);
> +            unsigned SecondODRHash = ComputeODRHash(SecondDefaultArg);
> +            if (FirstODRHash != SecondODRHash) {
> +              ODRDiagError(FirstParam->getLocation(),
> +                           FirstParam->getSourceRange(), MethodDefaultArg)
> +                  << SecondMD << i + 1;
> +              ODRDiagNote(SecondParam->getLocation(),
> +                          SecondParam->getSourceRange(), MethodDefaultArg)
> +                  << SecondMD << i + 1;
> +              Diagnosed = true;
> +              break;
> +            }
> +          }
> +        }
> +
> +        // TODO: Figure out how to diagnose different function bodies.
> +        // Deserialization does not import the second function body.
> +
> +        break;
>        }
> +      case Field: {
> +        // TODO: Merge with exising field diff logic.
> +        FieldDecl *FirstField = cast<FieldDecl>(FirstDecl);
> +        FieldDecl *SecondField = cast<FieldDecl>(SecondDecl);
> +        IdentifierInfo *FirstII = FirstField->getIdentifier();
> +        IdentifierInfo *SecondII = SecondField->getIdentifier();
> +        if (FirstII->getName() != SecondII->getName()) {
> +          ODRDiagError(FirstField->getLocation(),
> FirstField->getSourceRange(),
> +                       FieldName)
> +              << FirstII;
> +          ODRDiagNote(SecondField->getLocation(),
> SecondField->getSourceRange(),
> +                      FieldName)
> +              << SecondII;
> +          Diagnosed = true;
> +          break;
> +        }
> +
> +        // This case is handled elsewhere.
> +        if (FirstField->getType() != SecondField->getType()) {
> +          break;
> +        }
> +
> +        bool FirstBitField = FirstField->isBitField();
> +        bool SecondBitField = SecondField->isBitField();
> +        if (FirstBitField != SecondBitField) {
> +          ODRDiagError(FirstField->getLocation(),
> FirstField->getSourceRange(),
> +                       FieldSingleBitField)
> +              << FirstII << FirstBitField;
> +          ODRDiagNote(SecondField->getLocation(),
> SecondField->getSourceRange(),
> +                      FieldSingleBitField)
> +              << SecondII << SecondBitField;
> +          Diagnosed = true;
> +          break;
> +        }
> +
> +        if (FirstBitField && SecondBitField) {
> +          Expr* FirstWidth = FirstField->getBitWidth();
> +          Expr *SecondWidth = SecondField->getBitWidth();
> +          unsigned FirstODRHash = ComputeODRHash(FirstWidth);
> +          unsigned SecondODRHash = ComputeODRHash(SecondWidth);
> +          if (FirstODRHash != SecondODRHash) {
> +            ODRDiagError(FirstField->getLocation(),
> +                         FirstField->getSourceRange(),
> FieldSingleBitField)
> +                << FirstII << FirstBitField;
> +            ODRDiagNote(SecondField->getLocation(),
> +                        SecondField->getSourceRange(),
> FieldSingleBitField)
> +                << SecondII << SecondBitField;
> +            Diagnosed = true;
> +            break;
> +          }
> +        }
> +
> +        bool FirstMutable = FirstField->isMutable();
> +        bool SecondMutable = SecondField->isMutable();
> +        if (FirstMutable != SecondMutable) {
> +          ODRDiagError(FirstField->getLocation(),
> FirstField->getSourceRange(),
> +                       FieldMutable)
> +              << FirstII << FirstMutable;
> +          ODRDiagNote(SecondField->getLocation(),
> SecondField->getSourceRange(),
> +                      FieldMutable)
> +              << SecondII << SecondMutable;
> +          Diagnosed = true;
> +          break;
> +        }
> +        break;
> +      }
> +      case Other:
> +      case EndOfClass:
> +      case PublicSpecifer:
> +      case PrivateSpecifer:
> +      case ProtectedSpecifer:
> +        llvm_unreachable("Invalid diff type");
> +      }
> +
> +      if (Diagnosed == true)
> +        continue;
> +
> +      // Unable to find difference in Decl's, print simple different
> +      // definitions diagnostic.
> +      Diag(Merge.first->getLocation(),
> +           diag::err_module_odr_violation_different_definitions)
> +          << Merge.first << FirstModule.empty() << FirstModule;
> +      Diag(RD->getLocation(),
> +           diag::note_module_odr_violation_different_definitions)
> +          << SecondModule;
> +      Diagnosed = true;
>      }
>
>      if (!Diagnosed) {
>
> Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/
> Serialization/ASTReaderDecl.cpp?rev=293585&r1=293584&r2=293585&view=diff
> ============================================================
> ==================
> --- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)
> +++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Mon Jan 30 19:44:15 2017
> @@ -1521,6 +1521,7 @@ void ASTDeclReader::ReadCXXDefinitionDat
>    Data.ImplicitCopyAssignmentHasConstParam = Record.readInt();
>    Data.HasDeclaredCopyConstructorWithConstParam = Record.readInt();
>    Data.HasDeclaredCopyAssignmentWithConstParam = Record.readInt();
> +  Data.ODRHash = Record.readInt();
>
>    Data.NumBases = Record.readInt();
>    if (Data.NumBases)
> @@ -1651,6 +1652,7 @@ void ASTDeclReader::MergeDefinitionData(
>    OR_FIELD(HasDeclaredCopyConstructorWithConstParam)
>    OR_FIELD(HasDeclaredCopyAssignmentWithConstParam)
>    MATCH_FIELD(IsLambda)
> +  MATCH_FIELD(ODRHash)
>  #undef OR_FIELD
>  #undef MATCH_FIELD
>
>
> Modified: cfe/trunk/lib/Serialization/ASTWriter.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/
> Serialization/ASTWriter.cpp?rev=293585&r1=293584&r2=293585&view=diff
> ============================================================
> ==================
> --- cfe/trunk/lib/Serialization/ASTWriter.cpp (original)
> +++ cfe/trunk/lib/Serialization/ASTWriter.cpp Mon Jan 30 19:44:15 2017
> @@ -5699,6 +5699,7 @@ void ASTRecordWriter::AddCXXDefinitionDa
>    Record->push_back(Data.ImplicitCopyAssignmentHasConstParam);
>    Record->push_back(Data.HasDeclaredCopyConstructorWithConstParam);
>    Record->push_back(Data.HasDeclaredCopyAssignmentWithConstParam);
> +  Record->push_back(Data.ODRHash);
>    // IsLambda bit is already saved.
>
>    Record->push_back(Data.NumBases);
>
> Modified: cfe/trunk/test/Modules/merge-using-decls.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/
> Modules/merge-using-decls.cpp?rev=293585&r1=293584&r2=293585&view=diff
> ============================================================
> ==================
> --- cfe/trunk/test/Modules/merge-using-decls.cpp (original)
> +++ cfe/trunk/test/Modules/merge-using-decls.cpp Mon Jan 30 19:44:15 2017
> @@ -37,6 +37,10 @@ template int UseAll<Y>();
>  // Here, we're instantiating the definition from 'A' and merging the
> definition
>  // from 'B' into it.
>
> +// expected-error at b.h:* {{'D::type' from module 'B' is not present in
> definition of 'D<T>' in module 'A'}}
> +// expected-error at b.h:* {{'D::value' from module 'B' is not present in
> definition of 'D<T>' in module 'A'}}
> +
> +
>  // expected-error at b.h:* {{'E::value' from module 'B' is not present in
> definition of 'E<T>' in module 'A'}}
>  // expected-error at b.h:* {{'E::v' from module 'B' is not present in
> definition of 'E<T>' in module 'A'}}
>
>
> Added: cfe/trunk/test/Modules/odr_hash.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/
> Modules/odr_hash.cpp?rev=293585&view=auto
> ============================================================
> ==================
> --- cfe/trunk/test/Modules/odr_hash.cpp (added)
> +++ cfe/trunk/test/Modules/odr_hash.cpp Mon Jan 30 19:44:15 2017
> @@ -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 at first.h:* {{'S1' has different definitions in
> different modules; first difference is definition in module 'first' found
> public access specifier}}
> +// expected-note at second.h:* {{but in 'second' found private access
> specifier}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S2Friend2 {};
> +struct S2 {
> +  friend S2Friend2;
> +};
> +#elif defined(SECOND)
> +struct S2Friend1 {};
> +struct S2 {
> +  friend S2Friend1;
> +};
> +#else
> +S2 s2;
> +// expected-error at first.h:* {{'S2' has different definitions in
> different modules; first difference is definition in module 'first' found
> friend 'S2Friend2'}}
> +// expected-note at second.h:* {{but in 'second' found other friend
> 'S2Friend1'}}
> +#endif
> +
> +#if defined(FIRST)
> +template<class>
> +struct S3Template {};
> +struct S3 {
> +  friend S3Template<int>;
> +};
> +#elif defined(SECOND)
> +template<class>
> +struct S3Template {};
> +struct S3 {
> +  friend S3Template<double>;
> +};
> +#else
> +S3 s3;
> +// expected-error at first.h:* {{'S3' has different definitions in
> different modules; first difference is definition in module 'first' found
> friend 'S3Template<int>'}}
> +// expected-note at second.h:* {{but in 'second' found other friend
> 'S3Template<double>'}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S4 {
> +  static_assert(1 == 1, "First");
> +};
> +#elif defined(SECOND)
> +struct S4 {
> +  static_assert(1 == 1, "Second");
> +};
> +#else
> +S4 s4;
> +// expected-error at first.h:* {{'S4' has different definitions in
> different modules; first difference is definition in module 'first' found
> static assert with message}}
> +// expected-note at second.h:* {{but in 'second' found static assert with
> different message}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S5 {
> +  static_assert(1 == 1, "Message");
> +};
> +#elif defined(SECOND)
> +struct S5 {
> +  static_assert(2 == 2, "Message");
> +};
> +#else
> +S5 s5;
> +// expected-error at first.h:* {{'S5' has different definitions in
> different modules; first difference is definition in module 'first' found
> static assert with condition}}
> +// expected-note at second.h:* {{but in 'second' found static assert with
> different condition}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S6 {
> +  int First();
> +};
> +#elif defined(SECOND)
> +struct S6 {
> +  int Second();
> +};
> +#else
> +S6 s6;
> +// expected-error at second.h:* {{'S6::Second' from module 'second' is not
> present in definition of 'S6' in module 'first'}}
> +// expected-note at first.h:* {{definition has no member 'Second'}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S7 {
> +  double foo();
> +};
> +#elif defined(SECOND)
> +struct S7 {
> +  int foo();
> +};
> +#else
> +S7 s7;
> +// expected-error at second.h:* {{'S7::foo' from module 'second' is not
> present in definition of 'S7' in module 'first'}}
> +// expected-note at first.h:* {{declaration of 'foo' does not match}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S8 {
> +  void foo();
> +};
> +#elif defined(SECOND)
> +struct S8 {
> +  void foo() {}
> +};
> +#else
> +S8 s8;
> +// expected-error at first.h:* {{'S8' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S9 {
> +  void foo() { int y = 5; }
> +};
> +#elif defined(SECOND)
> +struct S9 {
> +  void foo() { int x = 5; }
> +};
> +#else
> +S9 s9;
> +// expected-error at first.h:* {{'S9' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S10 {
> +  struct {
> +    int x;
> +  } a;
> +};
> +#elif defined(SECOND)
> +struct S10 {
> +  struct {
> +    int x;
> +    int y;
> +  } a;
> +};
> +#else
> +S10 s10;
> +// expected-error-re at second.h:* {{'S10::(anonymous struct)::y' from
> module 'second' is not present in definition of 'S10::(anonymous struct at
> {{.*}}first.h:{{[0-9]*}}:{{[0-9]*}})' in module 'first'}}
> +// expected-note at first.h:* {{definition has no member 'y'}}
> +
> +// expected-error at first.h:* {{'S10' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S11 {
> +  void foo() { int y = sizeof(int); }
> +};
> +#elif defined(SECOND)
> +struct S11 {
> +  void foo() { int y = sizeof(double); }
> +};
> +#else
> +S11 s11;
> +// expected-error at first.h:* {{'S11' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S12 {
> +  int x = sizeof(x);
> +  int y = sizeof(x);
> +};
> +#elif defined(SECOND)
> +struct S12 {
> +  int x = sizeof(x);
> +  int y = sizeof(y);
> +};
> +#else
> +S12 s12;
> +// expected-error at first.h:* {{'S12' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S13 {
> +  template <typename A> void foo();
> +};
> +#elif defined(SECOND)
> +struct S13 {
> +  template <typename B> void foo();
> +};
> +#else
> +S13 s13;
> +// expected-error at first.h:* {{'S13' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S14 {
> +  template <typename A, typename B> void foo();
> +};
> +#elif defined(SECOND)
> +struct S14 {
> +  template <typename A> void foo();
> +};
> +#else
> +S14 s14;
> +// expected-error at second.h:* {{'S14::foo' from module 'second' is not
> present in definition of 'S14' in module 'first'}}
> +// expected-note at first.h:* {{declaration of 'foo' does not match}}
> +#endif
> +
> +#if defined(FIRST)
> +template <typename T>
> +struct S15 : T {
> +  void foo() {
> +    int x = __builtin_offsetof(T, first);
> +  }
> +};
> +#elif defined(SECOND)
> +template <typename T>
> +struct S15 : T {
> +  void foo() {
> +    int x = __builtin_offsetof(T, second);
> +  }
> +};
> +#else
> +template <typename T>
> +void Test15() {
> +  S15<T> s15;
> +// expected-error at first.h:* {{'S15' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +}
> +#endif
> +
> +#if defined(FIRST)
> +struct S16 {
> +  template <template<int = 0> class Y>
> +  void foo() {
> +    Y<> y;
> +  }
> +};
> +#elif defined(SECOND)
> +struct S16 {
> +  template <template<int = 1> class Y>
> +  void foo() {
> +    Y<> y;
> +  }
> +};
> +#else
> +void TestS16() {
> +  S16 s16;
> +}
> +// expected-error at first.h:* {{'S16' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S17 {
> +  template <template <typename> class T>
> +  static int foo(int a = 1);
> +  template <template <typename> class T, template <typename> class U>
> +  using Q_type = T<int>;
> +};
> +#elif defined(SECOND)
> +struct S17 {
> +  template <template <typename> class T>
> +  static int foo(int a = 1);
> +  template <template <typename> class T, template <typename> class U>
> +  using Q_type = U<int>;
> +};
> +#else
> +S17 s17;
> +// expected-error at second.h:* {{'S17::Q_type' from module 'second' is not
> present in definition of 'S17' in module 'first'}}
> +// expected-note at first.h:* {{declaration of 'Q_type' does not match}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S18 {
> +  enum E { X1 };
> +};
> +#elif defined(SECOND)
> +struct S18 {
> +  enum X { X1 };
> +};
> +#else
> +S18 s18;
> +// expected-error at second.h:* {{'S18::X' from module 'second' is not
> present in definition of 'S18' in module 'first'}}
> +// expected-note at first.h:* {{definition has no member 'X'}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S19 {
> +  enum E { X1 };
> +};
> +#elif defined(SECOND)
> +struct S19 {
> +  enum E { X1, X2 };
> +};
> +#else
> +S19 s19;
> +// expected-error at first.h:* {{'S19' has different definitions in
> different modules; first difference is definition in module 'first' found
> enum 'E' has 1 element}}
> +// expected-note at second.h:* {{but in 'second' found enum 'E' has 2
> elements}}
> +// expected-error at second.h:* {{'S19::E::X2' from module 'second' is not
> present in definition of 'S19::E' in module 'first'}}
> +// expected-note at first.h:* {{definition has no member 'X2'}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S20 {
> +  enum E { X1 = 1 };
> +};
> +#elif defined(SECOND)
> +struct S20 {
> +  enum E { X1 = 5};
> +};
> +#else
> +S20 s20;
> +// expected-error at first.h:* {{'S20' has different definitions in
> different modules; first difference is definition in module 'first' found
> element 'X1' in enum 'E' with initializer}}
> +// expected-note at second.h:* {{but in 'second' found element 'X1' in enum
> 'E' with different initializer}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S21 {
> +  void foo() {
> +    label:
> +    ;
> +  }
> +};
> +#elif defined(SECOND)
> +struct S21 {
> +  void foo() {
> +    ;
> +  }
> +};
> +#else
> +S21 s21;
> +// expected-error at first.h:* {{'S21' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S22 {
> +  void foo() {
> +    label_first:
> +    ;
> +  }
> +};
> +#elif defined(SECOND)
> +struct S22 {
> +  void foo() {
> +    label_second:
> +    ;
> +  }
> +};
> +#else
> +S22 s22;
> +// expected-error at first.h:* {{'S22' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S23 {
> +  typedef int a;
> +  typedef char b;
> +};
> +#elif defined(SECOND)
> +struct S23 {
> +  typedef char a;
> +  typedef int b;
> +};
> +#else
> +S23 s23;
> +// expected-error at second.h:* {{'S23::a' from module 'second' is not
> present in definition of 'S23' in module 'first'}}
> +// expected-note at first.h:* {{declaration of 'a' does not match}}
> +// expected-error at second.h:* {{'S23::b' from module 'second' is not
> present in definition of 'S23' in module 'first'}}
> +// expected-note at first.h:* {{declaration of 'b' does not match}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S24 {
> +  inline int foo();
> +};
> +#elif defined(SECOND)
> +struct S24 {
> +  int foo();
> +};
> +#else
> +S24 s24;
> +// expected-error at first.h:* {{'S24' has different definitions in
> different modules; first difference is definition in module 'first' found
> method 'foo' is inline}}
> +// expected-note at second.h:* {{but in 'second' found method 'foo' is not
> inline}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S25 {
> +  int x;
> +  S25() : x(5) {}
> +};
> +#elif defined(SECOND)
> +struct S25 {
> +  int x;
> +  S25() {}
> +};
> +#else
> +S25 s25;
> +// expected-error at first.h:* {{'S25' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S26 {
> +  int x;
> +  S26() : x(5) {}
> +};
> +#elif defined(SECOND)
> +struct S26 {
> +  int x;
> +  S26() : x(2) {}
> +};
> +#else
> +S26 s26;
> +// expected-error at first.h:* {{'S26' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S27 {
> +  explicit S27(int) {}
> +  S27() {}
> +};
> +#elif defined(SECOND)
> +struct S27 {
> +  S27(int) {}
> +  S27() {}
> +};
> +#else
> +S27 s27;
> +// expected-error at first.h:* {{'S27' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST) || defined(SECOND)
> +struct Base1 {
> +  Base1();
> +  Base1(int);
> +  Base1(double);
> +};
> +
> +struct Base2 {
> +  Base2();
> +  Base2(int);
> +  Base2(double);
> +};
> +#endif
> +
> +#if defined(FIRST)
> +struct S28 : public Base1 {};
> +#elif defined(SECOND)
> +struct S28 : public Base2 {};
> +#else
> +S28 s28;
> +// expected-error at first.h:* {{'S28' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S29 : virtual Base1 {};
> +#elif defined(SECOND)
> +struct S29 : virtual Base2 {};
> +#else
> +S29 s29;
> +// expected-error at first.h:* {{'S29' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S30 : public Base1 {
> +  S30() : Base1(1) {}
> +};
> +#elif defined(SECOND)
> +struct S30 : public Base1 {
> +  S30() : Base1(1.0) {}
> +};
> +#else
> +S30 s30;
> +// expected-error at first.h:* {{'S30' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S31 : virtual Base1 {
> +  S31() : Base1(1) {}
> +};
> +#elif defined(SECOND)
> +struct S31 : virtual Base1 {
> +  S31() : Base1(1.0) {}
> +};
> +#else
> +S31 s31;
> +// expected-error at first.h:* {{'S31' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S32 : public Base1, Base2 {
> +  S32() : Base1(1), Base2(1.0) {}
> +};
> +#elif defined(SECOND)
> +struct S32 : public Base2, Base1 {
> +  S32() : Base2(1), Base1(1.0) {}
> +};
> +#else
> +S32 s32;
> +// expected-error at first.h:* {{'S32' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S33 {
> +  S33() : S33(5) {}
> +  S33(int) {int a;}
> +};
> +#elif defined(SECOND)
> +struct S33 {
> +  S33() : S33(5) {}
> +  S33(int) {}
> +};
> +#else
> +S33 s33;
> +// expected-error at first.h:* {{'S33' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S34 {
> +  operator bool();
> +};
> +#elif defined(SECOND)
> +struct S34 {
> +  operator int();
> +};
> +#else
> +S34 s34;
> +// expected-error at second.h:* {{'S34::operator int' from module 'second'
> is not present in definition of 'S34' in module 'first'}}
> +// expected-note at first.h:* {{definition has no member 'operator int'}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S35 {
> +  explicit operator bool();
> +};
> +#elif defined(SECOND)
> +struct S35 {
> +  operator bool();
> +};
> +#else
> +S35 s35;
> +// expected-error at first.h:* {{'S35' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S36 {
> +  int x : 3;
> +};
> +#elif defined(SECOND)
> +struct S36 {
> +  int x : 4;
> +};
> +#else
> +S36 s36;
> +// expected-error at first.h:* {{'S36' has different definitions in
> different modules; first difference is definition in module 'first' found
> bitfield 'x'}}
> +// expected-note at second.h:* {{but in 'second' found bitfield 'x'}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S37 {
> +  mutable int x;
> +  int y;
> +};
> +#elif defined(SECOND)
> +struct S37 {
> +  int x;
> +  mutable int y;
> +};
> +#else
> +S37 s37;
> +// expected-error at first.h:* {{'S37' has different definitions in
> different modules; first difference is definition in module 'first' found
> mutable 'x'}}
> +// expected-note at second.h:* {{but in 'second' found non-mutable 'x'}}
> +#endif
> +
> +#if defined(FIRST)
> +template <class X>
> +struct S38 { };
> +#elif defined(SECOND)
> +template <class Y>
> +struct S38 { };
> +#else
> +S38<int> s38;
> +// expected-error at first.h:* {{'S38' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +template <class X = int>
> +struct S39 { X x; };
> +#elif defined(SECOND)
> +template <class X = double>
> +struct S39 { X x; };
> +#else
> +S39<> s39;
> +// expected-error at first.h:* {{'S39' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +template <int X = 5>
> +struct S40 { int x = X; };
> +#elif defined(SECOND)
> +template <int X = 7>
> +struct S40 { int x = X; };
> +#else
> +S40<> s40;
> +// expected-error at first.h:* {{'S40' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +
> +#endif
> +
> +#if defined(FIRST)
> +template <int> class T41a{};
> +template <template<int> class T = T41a>
> +struct S41 {};
> +#elif defined(SECOND)
> +template <int> class T41b{};
> +template <template<int> class T = T41b>
> +struct S41 {};
> +#else
> +using ::S41;
> +// expected-error at first.h:* {{'S41' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S42 {
> +  void foo() const {}
> +  void bar() {}
> +};
> +#elif defined(SECOND)
> +struct S42 {
> +  void foo() {}
> +  void bar() const {}
> +};
> +#else
> +S42 s42;
> +// expected-error at second.h:* {{'S42::bar' from module 'second' is not
> present in definition of 'S42' in module 'first'}}
> +// expected-note at first.h:* {{declaration of 'bar' does not match}}
> +// expected-error at second.h:* {{'S42::foo' from module 'second' is not
> present in definition of 'S42' in module 'first'}}
> +// expected-note at first.h:* {{declaration of 'foo' does not match}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S43 {
> +  static constexpr int x = 1;
> +  int y = 1;
> +};
> +#elif defined(SECOND)
> +struct S43 {
> +  int x = 1;
> +  static constexpr int y = 1;
> +};
> +#else
> +S43 s43;
> +// expected-error at second.h:* {{'S43::x' from module 'second' is not
> present in definition of 'S43' in module 'first'}}
> +// expected-note at first.h:* {{declaration of 'x' does not match}}
> +// expected-error at second.h:* {{'S43::y' from module 'second' is not
> present in definition of 'S43' in module 'first'}}
> +// expected-note at first.h:* {{declaration of 'y' does not match}}
> +//#endif
> +#endif
> +
> +#if defined(FIRST)
> +void f44();
> +struct S44 {
> +  friend void f44();
> +};
> +#elif defined(SECOND)
> +void g44();
> +struct S44 {
> +  friend void g44();
> +};
> +#else
> +S44 s44;
> +// expected-error at first.h:* {{'S44' has different definitions in
> different modules; first difference is definition in module 'first' found
> friend 'f44'}}
> +// expected-note at second.h:* {{but in 'second' found other friend 'g44'}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S45 { int n : 1; };
> +#elif defined(SECOND)
> +struct S45 { int n = 1; };
> +#else
> +S45 s45;
> +// expected-error at first.h:* {{'S45' has different definitions in
> different modules; first difference is definition in module 'first' found
> bitfield 'n'}}
> +// expected-note at second.h:* {{but in 'second' found field 'n'}}
> +#endif
> +
> +#if defined(FIRST)
> +struct S46 {
> +  int operator+(int) { return 0; }
> +};
> +#elif defined(SECOND)
> +struct S46 {
> +  int operator-(int) { return 0; }
> +};
> +#else
> +S46 s46;
> +// expected-error at second.h:* {{'S46::operator-' from module 'second' is
> not present in definition of 'S46' in module 'first'}}
> +// expected-note at first.h:* {{definition has no member 'operator-'}}
> +#endif
> +
> +#if defined(FIRST)
> +template <typename T>
> +struct S47 {
> +  int foo(int);
> +  float foo(float);
> +  int bar(int);
> +  float bar(float);
> +  int x = foo(T());
> +};
> +#elif defined(SECOND)
> +template <typename T>
> +struct S47 {
> +  int foo(int);
> +  float foo(float);
> +  int bar(int);
> +  float bar(float);
> +  int x = bar(T());
> +};
> +#else
> +template <typename T>
> +using S48 = S47<T>;
> +// expected-error at first.h:* {{'S47' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +template <typename T>
> +struct S49 {
> +  int operator+(int);
> +  float operator+(float);
> +  int operator-(int);
> +  float operator-(float);
> +  int x = S49() + T();
> +};
> +#elif defined(SECOND)
> +template <typename T>
> +struct S49 {
> +  int operator+(int);
> +  float operator+(float);
> +  int operator-(int);
> +  float operator-(float);
> +  int x = S49() - T();
> +};
> +#else
> +template <typename T>
> +using S50 = S49<T>;
> +// expected-error at first.h:* {{'S49' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +namespace A51 {
> +  void foo();
> +}
> +struct S51 {
> +  S51() {
> +    A51::foo();
> +  }
> +};
> +#elif defined(SECOND)
> +namespace B51 {
> +  void foo();
> +}
> +struct S51 {
> +  S51() {
> +    B51::foo();
> +  }
> +};
> +#else
> +S51 s51;
> +// expected-error at first.h:* {{'S51' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +namespace N52 {
> +  void foo();
> +}
> +struct S52 {
> +  S52() {
> +    N52::foo();
> +  }
> +};
> +#elif defined(SECOND)
> +namespace N52 {
> +  void foo();
> +}
> +struct S52 {
> +  S52() {
> +    ::N52::foo();
> +  }
> +};
> +#else
> +S52 s52;
> +// expected-error at first.h:* {{'S52' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +namespace N53 {
> +  struct foo {
> +    static int bar();
> +  };
> +  using A = foo;
> +}
> +struct S53 {
> +  S53() {
> +    N53::A::bar();
> +  }
> +};
> +#elif defined(SECOND)
> +namespace N53 {
> +  struct foo {
> +    static int bar();
> +  };
> +  using B = foo;
> +}
> +struct S53 {
> +  S53() {
> +    N53::B::bar();
> +  }
> +};
> +#else
> +S53 s53;
> +// expected-error at first.h:* {{'S53' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +namespace N54 {
> +namespace A {
> +void foo();
> +}
> +namespace AA = A;
> +}
> +
> +struct S54 {
> +  S54() {
> +    N54::AA::foo();
> +  }
> +};
> +#elif defined(SECOND)
> +namespace N54 {
> +namespace B {
> +void foo();
> +}
> +namespace BB = B;
> +}
> +
> +struct S54 {
> +  S54() {
> +    N54::BB::foo();
> +  }
> +};
> +#else
> +S54 s54;
> +// expected-error at first.h:* {{'S54' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +namespace N55 {
> +namespace A {
> +void foo();
> +}
> +namespace X = A;
> +}
> +
> +struct S55 {
> +  S55() {
> +    N55::X::foo();
> +  }
> +};
> +#elif defined(SECOND)
> +namespace N55 {
> +namespace B {
> +void foo();
> +}
> +namespace X = B;
> +}
> +
> +struct S55 {
> +  S55() {
> +    N55::X::foo();
> +  }
> +};
> +#else
> +S55 s55;
> +// expected-error at first.h:* {{'S55' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +#if defined(FIRST)
> +template<int> struct foo56{};
> +template <template<int> class T>
> +struct S56 {};
> +struct S57 {
> +  S56<foo56> a;
> +};
> +#elif defined(SECOND)
> +template<int> struct bar56{};
> +template <template<int> class T>
> +struct S56 {};
> +struct S57 {
> +  S56<bar56> a;
> +};
> +#else
> +S57 s57;
> +// expected-error at second.h:* {{'S57::a' from module 'second' is not
> present in definition of 'S57' in module 'first'}}
> +// expected-note at first.h:* {{declaration of 'a' does not match}}
> +#endif
> +
> +#if defined(FIRST)
> +template<int> struct foo58{};
> +template <template<int> class T>
> +struct S58 {};
> +struct S59 {
> +  S58<foo58> a;
> +};
> +#elif defined(SECOND)
> +template<int> struct foo58{};
> +template <template<int> class T>
> +struct S58 {};
> +struct S59 {
> +  S58<::foo58> a;
> +};
> +#else
> +S59 s59;
> +// expected-error at first.h:* {{'S59' has different definitions in
> different modules; definition in module 'first' is here}}
> +// expected-note at second.h:* {{definition in module 'second' is here}}
> +#endif
> +
> +
> +// 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
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20170131/fdf359af/attachment-0001.html>


More information about the cfe-commits mailing list