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