Index: cfe/trunk/include/clang/AST/ASTStructuralEquivalence.h =================================================================== --- cfe/trunk/include/clang/AST/ASTStructuralEquivalence.h +++ cfe/trunk/include/clang/AST/ASTStructuralEquivalence.h @@ -0,0 +1,101 @@ +//===--- ASTStructuralEquivalence.h - ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the StructuralEquivalenceContext class which checks for +// structural equivalence between types. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_ASTSTRUCTURALEQUIVALENCE_H +#define LLVM_CLANG_AST_ASTSTRUCTURALEQUIVALENCE_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/Optional.h" +#include + +namespace clang { + +class ASTContext; +class Decl; +class DiagnosticBuilder; +class QualType; +class RecordDecl; +class SourceLocation; + +struct StructuralEquivalenceContext { + /// AST contexts for which we are checking structural equivalence. + ASTContext &FromCtx, &ToCtx; + + /// The set of "tentative" equivalences between two canonical + /// declarations, mapping from a declaration in the first context to the + /// declaration in the second context that we believe to be equivalent. + llvm::DenseMap TentativeEquivalences; + + /// Queue of declarations in the first context whose equivalence + /// with a declaration in the second context still needs to be verified. + std::deque DeclsToCheck; + + /// Declaration (from, to) pairs that are known not to be equivalent + /// (which we have already complained about). + llvm::DenseSet> &NonEquivalentDecls; + + /// Whether we're being strict about the spelling of types when + /// unifying two types. + bool StrictTypeSpelling; + + /// Whether warn or error on tag type mismatches. + bool ErrorOnTagTypeMismatch; + + /// Whether to complain about failures. + bool Complain; + + /// \c true if the last diagnostic came from ToCtx. + bool LastDiagFromC2; + + StructuralEquivalenceContext( + ASTContext &FromCtx, ASTContext &ToCtx, + llvm::DenseSet> &NonEquivalentDecls, + bool StrictTypeSpelling = false, bool Complain = true) + : FromCtx(FromCtx), ToCtx(ToCtx), NonEquivalentDecls(NonEquivalentDecls), + StrictTypeSpelling(StrictTypeSpelling), Complain(Complain), + LastDiagFromC2(false) {} + + DiagnosticBuilder Diag1(SourceLocation Loc, unsigned DiagID); + DiagnosticBuilder Diag2(SourceLocation Loc, unsigned DiagID); + + /// Determine whether the two declarations are structurally + /// equivalent. + bool IsStructurallyEquivalent(Decl *D1, Decl *D2); + + /// Determine whether the two types are structurally equivalent. + bool IsStructurallyEquivalent(QualType T1, QualType T2); + + /// Find the index of the given anonymous struct/union within its + /// context. + /// + /// \returns Returns the index of this anonymous struct/union in its context, + /// including the next assigned index (if none of them match). Returns an + /// empty option if the context is not a record, i.e.. if the anonymous + /// struct/union is at namespace or block scope. + /// + /// FIXME: This is needed by ASTImporter and ASTStructureEquivalence. It + /// probably makes more sense in some other common place then here. + static llvm::Optional + findUntaggedStructOrUnionIndex(RecordDecl *Anon); + +private: + /// Finish checking all of the structural equivalences. + /// + /// \returns true if an error occurred, false otherwise. + bool Finish(); +}; +} // namespace clang + +#endif // LLVM_CLANG_AST_ASTSTRUCTURALEQUIVALENCE_H Index: cfe/trunk/lib/AST/ASTImporter.cpp =================================================================== --- cfe/trunk/lib/AST/ASTImporter.cpp +++ cfe/trunk/lib/AST/ASTImporter.cpp @@ -14,6 +14,7 @@ #include "clang/AST/ASTImporter.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTDiagnostic.h" +#include "clang/AST/ASTStructuralEquivalence.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclVisitor.h" @@ -321,1396 +322,12 @@ }; } -using namespace clang; - -//---------------------------------------------------------------------------- -// Structural Equivalence -//---------------------------------------------------------------------------- - -namespace { - struct StructuralEquivalenceContext { - /// \brief AST contexts for which we are checking structural equivalence. - ASTContext &C1, &C2; - - /// \brief The set of "tentative" equivalences between two canonical - /// declarations, mapping from a declaration in the first context to the - /// declaration in the second context that we believe to be equivalent. - llvm::DenseMap TentativeEquivalences; - - /// \brief Queue of declarations in the first context whose equivalence - /// with a declaration in the second context still needs to be verified. - std::deque DeclsToCheck; - - /// \brief Declaration (from, to) pairs that are known not to be equivalent - /// (which we have already complained about). - llvm::DenseSet > &NonEquivalentDecls; - - /// \brief Whether we're being strict about the spelling of types when - /// unifying two types. - bool StrictTypeSpelling; - - /// \brief Whether to complain about failures. - bool Complain; - - /// \brief \c true if the last diagnostic came from C2. - bool LastDiagFromC2; - - StructuralEquivalenceContext(ASTContext &C1, ASTContext &C2, - llvm::DenseSet > &NonEquivalentDecls, - bool StrictTypeSpelling = false, - bool Complain = true) - : C1(C1), C2(C2), NonEquivalentDecls(NonEquivalentDecls), - StrictTypeSpelling(StrictTypeSpelling), Complain(Complain), - LastDiagFromC2(false) {} - - /// \brief Determine whether the two declarations are structurally - /// equivalent. - bool IsStructurallyEquivalent(Decl *D1, Decl *D2); - - /// \brief Determine whether the two types are structurally equivalent. - bool IsStructurallyEquivalent(QualType T1, QualType T2); - - private: - /// \brief Finish checking all of the structural equivalences. - /// - /// \returns true if an error occurred, false otherwise. - bool Finish(); - - public: - DiagnosticBuilder Diag1(SourceLocation Loc, unsigned DiagID) { - assert(Complain && "Not allowed to complain"); - if (LastDiagFromC2) - C1.getDiagnostics().notePriorDiagnosticFrom(C2.getDiagnostics()); - LastDiagFromC2 = false; - return C1.getDiagnostics().Report(Loc, DiagID); - } - - DiagnosticBuilder Diag2(SourceLocation Loc, unsigned DiagID) { - assert(Complain && "Not allowed to complain"); - if (!LastDiagFromC2) - C2.getDiagnostics().notePriorDiagnosticFrom(C1.getDiagnostics()); - LastDiagFromC2 = true; - return C2.getDiagnostics().Report(Loc, DiagID); - } - }; -} - -static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, - QualType T1, QualType T2); -static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, - Decl *D1, Decl *D2); -static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, - const TemplateArgument &Arg1, - const TemplateArgument &Arg2); - -/// \brief Determine structural equivalence of two expressions. -static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, - Expr *E1, Expr *E2) { - if (!E1 || !E2) - return E1 == E2; - - // FIXME: Actually perform a structural comparison! - return true; -} - -/// \brief Determine whether two identifiers are equivalent. -static bool IsStructurallyEquivalent(const IdentifierInfo *Name1, - const IdentifierInfo *Name2) { - if (!Name1 || !Name2) - return Name1 == Name2; - - return Name1->getName() == Name2->getName(); -} - -/// \brief Determine whether two nested-name-specifiers are equivalent. -static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, - NestedNameSpecifier *NNS1, - NestedNameSpecifier *NNS2) { - if (NNS1->getKind() != NNS2->getKind()) - return false; - - NestedNameSpecifier *Prefix1 = NNS1->getPrefix(), - *Prefix2 = NNS2->getPrefix(); - if ((bool)Prefix1 != (bool)Prefix2) - return false; - - if (Prefix1) - if (!IsStructurallyEquivalent(Context, Prefix1, Prefix2)) - return false; - - switch (NNS1->getKind()) { - case NestedNameSpecifier::Identifier: - return IsStructurallyEquivalent(NNS1->getAsIdentifier(), - NNS2->getAsIdentifier()); - case NestedNameSpecifier::Namespace: - return IsStructurallyEquivalent(Context, NNS1->getAsNamespace(), - NNS2->getAsNamespace()); - case NestedNameSpecifier::NamespaceAlias: - return IsStructurallyEquivalent(Context, NNS1->getAsNamespaceAlias(), - NNS2->getAsNamespaceAlias()); - case NestedNameSpecifier::TypeSpec: - case NestedNameSpecifier::TypeSpecWithTemplate: - return IsStructurallyEquivalent(Context, QualType(NNS1->getAsType(), 0), - QualType(NNS2->getAsType(), 0)); - case NestedNameSpecifier::Global: - return true; - case NestedNameSpecifier::Super: - return IsStructurallyEquivalent(Context, NNS1->getAsRecordDecl(), - NNS2->getAsRecordDecl()); - } - return false; -} - -static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, - const TemplateName &N1, - const TemplateName &N2) { - if (N1.getKind() != N2.getKind()) - return false; - switch (N1.getKind()) { - case TemplateName::Template: - return IsStructurallyEquivalent(Context, N1.getAsTemplateDecl(), - N2.getAsTemplateDecl()); - - case TemplateName::OverloadedTemplate: { - OverloadedTemplateStorage *OS1 = N1.getAsOverloadedTemplate(), - *OS2 = N2.getAsOverloadedTemplate(); - OverloadedTemplateStorage::iterator I1 = OS1->begin(), I2 = OS2->begin(), - E1 = OS1->end(), E2 = OS2->end(); - for (; I1 != E1 && I2 != E2; ++I1, ++I2) - if (!IsStructurallyEquivalent(Context, *I1, *I2)) - return false; - return I1 == E1 && I2 == E2; - } - - case TemplateName::QualifiedTemplate: { - QualifiedTemplateName *QN1 = N1.getAsQualifiedTemplateName(), - *QN2 = N2.getAsQualifiedTemplateName(); - return IsStructurallyEquivalent(Context, QN1->getDecl(), QN2->getDecl()) && - IsStructurallyEquivalent(Context, QN1->getQualifier(), - QN2->getQualifier()); - } - - case TemplateName::DependentTemplate: { - DependentTemplateName *DN1 = N1.getAsDependentTemplateName(), - *DN2 = N2.getAsDependentTemplateName(); - if (!IsStructurallyEquivalent(Context, DN1->getQualifier(), - DN2->getQualifier())) - return false; - if (DN1->isIdentifier() && DN2->isIdentifier()) - return IsStructurallyEquivalent(DN1->getIdentifier(), - DN2->getIdentifier()); - else if (DN1->isOverloadedOperator() && DN2->isOverloadedOperator()) - return DN1->getOperator() == DN2->getOperator(); - return false; - } - - case TemplateName::SubstTemplateTemplateParm: { - SubstTemplateTemplateParmStorage *TS1 = N1.getAsSubstTemplateTemplateParm(), - *TS2 = N2.getAsSubstTemplateTemplateParm(); - return IsStructurallyEquivalent(Context, TS1->getParameter(), - TS2->getParameter()) && - IsStructurallyEquivalent(Context, TS1->getReplacement(), - TS2->getReplacement()); - } - case TemplateName::SubstTemplateTemplateParmPack: { - SubstTemplateTemplateParmPackStorage - *P1 = N1.getAsSubstTemplateTemplateParmPack(), - *P2 = N2.getAsSubstTemplateTemplateParmPack(); - return IsStructurallyEquivalent(Context, P1->getArgumentPack(), - P2->getArgumentPack()) && - IsStructurallyEquivalent(Context, P1->getParameterPack(), - P2->getParameterPack()); - } - } - return false; -} - -/// \brief Determine whether two template arguments are equivalent. -static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, - const TemplateArgument &Arg1, - const TemplateArgument &Arg2) { - if (Arg1.getKind() != Arg2.getKind()) - return false; - - switch (Arg1.getKind()) { - case TemplateArgument::Null: - return true; - - case TemplateArgument::Type: - return Context.IsStructurallyEquivalent(Arg1.getAsType(), Arg2.getAsType()); - - case TemplateArgument::Integral: - if (!Context.IsStructurallyEquivalent(Arg1.getIntegralType(), - Arg2.getIntegralType())) - return false; - - return llvm::APSInt::isSameValue(Arg1.getAsIntegral(), Arg2.getAsIntegral()); - - case TemplateArgument::Declaration: - return Context.IsStructurallyEquivalent(Arg1.getAsDecl(), Arg2.getAsDecl()); - - case TemplateArgument::NullPtr: - return true; // FIXME: Is this correct? - - case TemplateArgument::Template: - return IsStructurallyEquivalent(Context, - Arg1.getAsTemplate(), - Arg2.getAsTemplate()); - - case TemplateArgument::TemplateExpansion: - return IsStructurallyEquivalent(Context, - Arg1.getAsTemplateOrTemplatePattern(), - Arg2.getAsTemplateOrTemplatePattern()); - - case TemplateArgument::Expression: - return IsStructurallyEquivalent(Context, - Arg1.getAsExpr(), Arg2.getAsExpr()); - - case TemplateArgument::Pack: - if (Arg1.pack_size() != Arg2.pack_size()) - return false; - - for (unsigned I = 0, N = Arg1.pack_size(); I != N; ++I) - if (!IsStructurallyEquivalent(Context, - Arg1.pack_begin()[I], - Arg2.pack_begin()[I])) - return false; - - return true; - } - - llvm_unreachable("Invalid template argument kind"); -} - -/// \brief Determine structural equivalence for the common part of array -/// types. -static bool IsArrayStructurallyEquivalent(StructuralEquivalenceContext &Context, - const ArrayType *Array1, - const ArrayType *Array2) { - if (!IsStructurallyEquivalent(Context, - Array1->getElementType(), - Array2->getElementType())) - return false; - if (Array1->getSizeModifier() != Array2->getSizeModifier()) - return false; - if (Array1->getIndexTypeQualifiers() != Array2->getIndexTypeQualifiers()) - return false; - - return true; -} - -/// \brief Determine structural equivalence of two types. -static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, - QualType T1, QualType T2) { - if (T1.isNull() || T2.isNull()) - return T1.isNull() && T2.isNull(); - - if (!Context.StrictTypeSpelling) { - // We aren't being strict about token-to-token equivalence of types, - // so map down to the canonical type. - T1 = Context.C1.getCanonicalType(T1); - T2 = Context.C2.getCanonicalType(T2); - } - - if (T1.getQualifiers() != T2.getQualifiers()) - return false; - - Type::TypeClass TC = T1->getTypeClass(); - - if (T1->getTypeClass() != T2->getTypeClass()) { - // Compare function types with prototypes vs. without prototypes as if - // both did not have prototypes. - if (T1->getTypeClass() == Type::FunctionProto && - T2->getTypeClass() == Type::FunctionNoProto) - TC = Type::FunctionNoProto; - else if (T1->getTypeClass() == Type::FunctionNoProto && - T2->getTypeClass() == Type::FunctionProto) - TC = Type::FunctionNoProto; - else - return false; - } - - switch (TC) { - case Type::Builtin: - // FIXME: Deal with Char_S/Char_U. - if (cast(T1)->getKind() != cast(T2)->getKind()) - return false; - break; - - case Type::Complex: - if (!IsStructurallyEquivalent(Context, - cast(T1)->getElementType(), - cast(T2)->getElementType())) - return false; - break; - - case Type::Adjusted: - case Type::Decayed: - if (!IsStructurallyEquivalent(Context, - cast(T1)->getOriginalType(), - cast(T2)->getOriginalType())) - return false; - break; - - case Type::Pointer: - if (!IsStructurallyEquivalent(Context, - cast(T1)->getPointeeType(), - cast(T2)->getPointeeType())) - return false; - break; - - case Type::BlockPointer: - if (!IsStructurallyEquivalent(Context, - cast(T1)->getPointeeType(), - cast(T2)->getPointeeType())) - return false; - break; - - case Type::LValueReference: - case Type::RValueReference: { - const ReferenceType *Ref1 = cast(T1); - const ReferenceType *Ref2 = cast(T2); - if (Ref1->isSpelledAsLValue() != Ref2->isSpelledAsLValue()) - return false; - if (Ref1->isInnerRef() != Ref2->isInnerRef()) - return false; - if (!IsStructurallyEquivalent(Context, - Ref1->getPointeeTypeAsWritten(), - Ref2->getPointeeTypeAsWritten())) - return false; - break; - } - - case Type::MemberPointer: { - const MemberPointerType *MemPtr1 = cast(T1); - const MemberPointerType *MemPtr2 = cast(T2); - if (!IsStructurallyEquivalent(Context, - MemPtr1->getPointeeType(), - MemPtr2->getPointeeType())) - return false; - if (!IsStructurallyEquivalent(Context, - QualType(MemPtr1->getClass(), 0), - QualType(MemPtr2->getClass(), 0))) - return false; - break; - } - - case Type::ConstantArray: { - const ConstantArrayType *Array1 = cast(T1); - const ConstantArrayType *Array2 = cast(T2); - if (!llvm::APInt::isSameValue(Array1->getSize(), Array2->getSize())) - return false; - - if (!IsArrayStructurallyEquivalent(Context, Array1, Array2)) - return false; - break; - } - - case Type::IncompleteArray: - if (!IsArrayStructurallyEquivalent(Context, - cast(T1), - cast(T2))) - return false; - break; - - case Type::VariableArray: { - const VariableArrayType *Array1 = cast(T1); - const VariableArrayType *Array2 = cast(T2); - if (!IsStructurallyEquivalent(Context, - Array1->getSizeExpr(), Array2->getSizeExpr())) - return false; - - if (!IsArrayStructurallyEquivalent(Context, Array1, Array2)) - return false; - - break; - } - - case Type::DependentSizedArray: { - const DependentSizedArrayType *Array1 = cast(T1); - const DependentSizedArrayType *Array2 = cast(T2); - if (!IsStructurallyEquivalent(Context, - Array1->getSizeExpr(), Array2->getSizeExpr())) - return false; - - if (!IsArrayStructurallyEquivalent(Context, Array1, Array2)) - return false; - - break; - } - - case Type::DependentSizedExtVector: { - const DependentSizedExtVectorType *Vec1 - = cast(T1); - const DependentSizedExtVectorType *Vec2 - = cast(T2); - if (!IsStructurallyEquivalent(Context, - Vec1->getSizeExpr(), Vec2->getSizeExpr())) - return false; - if (!IsStructurallyEquivalent(Context, - Vec1->getElementType(), - Vec2->getElementType())) - return false; - break; - } - - case Type::Vector: - case Type::ExtVector: { - const VectorType *Vec1 = cast(T1); - const VectorType *Vec2 = cast(T2); - if (!IsStructurallyEquivalent(Context, - Vec1->getElementType(), - Vec2->getElementType())) - return false; - if (Vec1->getNumElements() != Vec2->getNumElements()) - return false; - if (Vec1->getVectorKind() != Vec2->getVectorKind()) - return false; - break; - } - - case Type::FunctionProto: { - const FunctionProtoType *Proto1 = cast(T1); - const FunctionProtoType *Proto2 = cast(T2); - if (Proto1->getNumParams() != Proto2->getNumParams()) - return false; - for (unsigned I = 0, N = Proto1->getNumParams(); I != N; ++I) { - if (!IsStructurallyEquivalent(Context, Proto1->getParamType(I), - Proto2->getParamType(I))) - return false; - } - if (Proto1->isVariadic() != Proto2->isVariadic()) - return false; - if (Proto1->getExceptionSpecType() != Proto2->getExceptionSpecType()) - return false; - if (Proto1->getExceptionSpecType() == EST_Dynamic) { - if (Proto1->getNumExceptions() != Proto2->getNumExceptions()) - return false; - for (unsigned I = 0, N = Proto1->getNumExceptions(); I != N; ++I) { - if (!IsStructurallyEquivalent(Context, - Proto1->getExceptionType(I), - Proto2->getExceptionType(I))) - return false; - } - } else if (Proto1->getExceptionSpecType() == EST_ComputedNoexcept) { - if (!IsStructurallyEquivalent(Context, - Proto1->getNoexceptExpr(), - Proto2->getNoexceptExpr())) - return false; - } - if (Proto1->getTypeQuals() != Proto2->getTypeQuals()) - return false; - - // Fall through to check the bits common with FunctionNoProtoType. - } - - case Type::FunctionNoProto: { - const FunctionType *Function1 = cast(T1); - const FunctionType *Function2 = cast(T2); - if (!IsStructurallyEquivalent(Context, Function1->getReturnType(), - Function2->getReturnType())) - return false; - if (Function1->getExtInfo() != Function2->getExtInfo()) - return false; - break; - } - - case Type::UnresolvedUsing: - if (!IsStructurallyEquivalent(Context, - cast(T1)->getDecl(), - cast(T2)->getDecl())) - return false; - - break; - - case Type::Attributed: - if (!IsStructurallyEquivalent(Context, - cast(T1)->getModifiedType(), - cast(T2)->getModifiedType())) - return false; - if (!IsStructurallyEquivalent(Context, - cast(T1)->getEquivalentType(), - cast(T2)->getEquivalentType())) - return false; - break; - - case Type::Paren: - if (!IsStructurallyEquivalent(Context, - cast(T1)->getInnerType(), - cast(T2)->getInnerType())) - return false; - break; - - case Type::Typedef: - if (!IsStructurallyEquivalent(Context, - cast(T1)->getDecl(), - cast(T2)->getDecl())) - return false; - break; - - case Type::TypeOfExpr: - if (!IsStructurallyEquivalent(Context, - cast(T1)->getUnderlyingExpr(), - cast(T2)->getUnderlyingExpr())) - return false; - break; - - case Type::TypeOf: - if (!IsStructurallyEquivalent(Context, - cast(T1)->getUnderlyingType(), - cast(T2)->getUnderlyingType())) - return false; - break; - - case Type::UnaryTransform: - if (!IsStructurallyEquivalent(Context, - cast(T1)->getUnderlyingType(), - cast(T1)->getUnderlyingType())) - return false; - break; - - case Type::Decltype: - if (!IsStructurallyEquivalent(Context, - cast(T1)->getUnderlyingExpr(), - cast(T2)->getUnderlyingExpr())) - return false; - break; - - case Type::Auto: - if (!IsStructurallyEquivalent(Context, - cast(T1)->getDeducedType(), - cast(T2)->getDeducedType())) - return false; - break; - - case Type::DeducedTemplateSpecialization: { - auto *DT1 = cast(T1); - auto *DT2 = cast(T2); - if (!IsStructurallyEquivalent(Context, - DT1->getTemplateName(), - DT2->getTemplateName())) - return false; - if (!IsStructurallyEquivalent(Context, - DT1->getDeducedType(), - DT2->getDeducedType())) - return false; - break; - } - - case Type::Record: - case Type::Enum: - if (!IsStructurallyEquivalent(Context, - cast(T1)->getDecl(), - cast(T2)->getDecl())) - return false; - break; - - case Type::TemplateTypeParm: { - const TemplateTypeParmType *Parm1 = cast(T1); - const TemplateTypeParmType *Parm2 = cast(T2); - if (Parm1->getDepth() != Parm2->getDepth()) - return false; - if (Parm1->getIndex() != Parm2->getIndex()) - return false; - if (Parm1->isParameterPack() != Parm2->isParameterPack()) - return false; - - // Names of template type parameters are never significant. - break; - } - - case Type::SubstTemplateTypeParm: { - const SubstTemplateTypeParmType *Subst1 - = cast(T1); - const SubstTemplateTypeParmType *Subst2 - = cast(T2); - if (!IsStructurallyEquivalent(Context, - QualType(Subst1->getReplacedParameter(), 0), - QualType(Subst2->getReplacedParameter(), 0))) - return false; - if (!IsStructurallyEquivalent(Context, - Subst1->getReplacementType(), - Subst2->getReplacementType())) - return false; - break; - } - - case Type::SubstTemplateTypeParmPack: { - const SubstTemplateTypeParmPackType *Subst1 - = cast(T1); - const SubstTemplateTypeParmPackType *Subst2 - = cast(T2); - if (!IsStructurallyEquivalent(Context, - QualType(Subst1->getReplacedParameter(), 0), - QualType(Subst2->getReplacedParameter(), 0))) - return false; - if (!IsStructurallyEquivalent(Context, - Subst1->getArgumentPack(), - Subst2->getArgumentPack())) - return false; - break; - } - case Type::TemplateSpecialization: { - const TemplateSpecializationType *Spec1 - = cast(T1); - const TemplateSpecializationType *Spec2 - = cast(T2); - if (!IsStructurallyEquivalent(Context, - Spec1->getTemplateName(), - Spec2->getTemplateName())) - return false; - if (Spec1->getNumArgs() != Spec2->getNumArgs()) - return false; - for (unsigned I = 0, N = Spec1->getNumArgs(); I != N; ++I) { - if (!IsStructurallyEquivalent(Context, - Spec1->getArg(I), Spec2->getArg(I))) - return false; - } - break; - } - - case Type::Elaborated: { - const ElaboratedType *Elab1 = cast(T1); - const ElaboratedType *Elab2 = cast(T2); - // CHECKME: what if a keyword is ETK_None or ETK_typename ? - if (Elab1->getKeyword() != Elab2->getKeyword()) - return false; - if (!IsStructurallyEquivalent(Context, - Elab1->getQualifier(), - Elab2->getQualifier())) - return false; - if (!IsStructurallyEquivalent(Context, - Elab1->getNamedType(), - Elab2->getNamedType())) - return false; - break; - } - - case Type::InjectedClassName: { - const InjectedClassNameType *Inj1 = cast(T1); - const InjectedClassNameType *Inj2 = cast(T2); - if (!IsStructurallyEquivalent(Context, - Inj1->getInjectedSpecializationType(), - Inj2->getInjectedSpecializationType())) - return false; - break; - } - - case Type::DependentName: { - const DependentNameType *Typename1 = cast(T1); - const DependentNameType *Typename2 = cast(T2); - if (!IsStructurallyEquivalent(Context, - Typename1->getQualifier(), - Typename2->getQualifier())) - return false; - if (!IsStructurallyEquivalent(Typename1->getIdentifier(), - Typename2->getIdentifier())) - return false; - - break; - } - - case Type::DependentTemplateSpecialization: { - const DependentTemplateSpecializationType *Spec1 = - cast(T1); - const DependentTemplateSpecializationType *Spec2 = - cast(T2); - if (!IsStructurallyEquivalent(Context, - Spec1->getQualifier(), - Spec2->getQualifier())) - return false; - if (!IsStructurallyEquivalent(Spec1->getIdentifier(), - Spec2->getIdentifier())) - return false; - if (Spec1->getNumArgs() != Spec2->getNumArgs()) - return false; - for (unsigned I = 0, N = Spec1->getNumArgs(); I != N; ++I) { - if (!IsStructurallyEquivalent(Context, - Spec1->getArg(I), Spec2->getArg(I))) - return false; - } - break; - } - - case Type::PackExpansion: - if (!IsStructurallyEquivalent(Context, - cast(T1)->getPattern(), - cast(T2)->getPattern())) - return false; - break; - - case Type::ObjCInterface: { - const ObjCInterfaceType *Iface1 = cast(T1); - const ObjCInterfaceType *Iface2 = cast(T2); - if (!IsStructurallyEquivalent(Context, - Iface1->getDecl(), Iface2->getDecl())) - return false; - break; - } - - case Type::ObjCTypeParam: { - const ObjCTypeParamType *Obj1 = cast(T1); - const ObjCTypeParamType *Obj2 = cast(T2); - if (!IsStructurallyEquivalent(Context, Obj1->getDecl(), - Obj2->getDecl())) - return false; - - if (Obj1->getNumProtocols() != Obj2->getNumProtocols()) - return false; - for (unsigned I = 0, N = Obj1->getNumProtocols(); I != N; ++I) { - if (!IsStructurallyEquivalent(Context, - Obj1->getProtocol(I), - Obj2->getProtocol(I))) - return false; - } - break; - } - case Type::ObjCObject: { - const ObjCObjectType *Obj1 = cast(T1); - const ObjCObjectType *Obj2 = cast(T2); - if (!IsStructurallyEquivalent(Context, - Obj1->getBaseType(), - Obj2->getBaseType())) - return false; - if (Obj1->getNumProtocols() != Obj2->getNumProtocols()) - return false; - for (unsigned I = 0, N = Obj1->getNumProtocols(); I != N; ++I) { - if (!IsStructurallyEquivalent(Context, - Obj1->getProtocol(I), - Obj2->getProtocol(I))) - return false; - } - break; - } - - case Type::ObjCObjectPointer: { - const ObjCObjectPointerType *Ptr1 = cast(T1); - const ObjCObjectPointerType *Ptr2 = cast(T2); - if (!IsStructurallyEquivalent(Context, - Ptr1->getPointeeType(), - Ptr2->getPointeeType())) - return false; - break; - } - - case Type::Atomic: { - if (!IsStructurallyEquivalent(Context, - cast(T1)->getValueType(), - cast(T2)->getValueType())) - return false; - break; - } - - case Type::Pipe: { - if (!IsStructurallyEquivalent(Context, - cast(T1)->getElementType(), - cast(T2)->getElementType())) - return false; - break; - } - - } // end switch - - return true; -} - -/// \brief Determine structural equivalence of two fields. -static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, - FieldDecl *Field1, FieldDecl *Field2) { - RecordDecl *Owner2 = cast(Field2->getDeclContext()); - - // For anonymous structs/unions, match up the anonymous struct/union type - // declarations directly, so that we don't go off searching for anonymous - // types - if (Field1->isAnonymousStructOrUnion() && - Field2->isAnonymousStructOrUnion()) { - RecordDecl *D1 = Field1->getType()->castAs()->getDecl(); - RecordDecl *D2 = Field2->getType()->castAs()->getDecl(); - return IsStructurallyEquivalent(Context, D1, D2); - } - - // Check for equivalent field names. - IdentifierInfo *Name1 = Field1->getIdentifier(); - IdentifierInfo *Name2 = Field2->getIdentifier(); - if (!::IsStructurallyEquivalent(Name1, Name2)) - return false; - - if (!IsStructurallyEquivalent(Context, - Field1->getType(), Field2->getType())) { - if (Context.Complain) { - Context.Diag2(Owner2->getLocation(), diag::warn_odr_tag_type_inconsistent) - << Context.C2.getTypeDeclType(Owner2); - Context.Diag2(Field2->getLocation(), diag::note_odr_field) - << Field2->getDeclName() << Field2->getType(); - Context.Diag1(Field1->getLocation(), diag::note_odr_field) - << Field1->getDeclName() << Field1->getType(); - } - return false; - } - - if (Field1->isBitField() != Field2->isBitField()) { - if (Context.Complain) { - Context.Diag2(Owner2->getLocation(), diag::warn_odr_tag_type_inconsistent) - << Context.C2.getTypeDeclType(Owner2); - if (Field1->isBitField()) { - Context.Diag1(Field1->getLocation(), diag::note_odr_bit_field) - << Field1->getDeclName() << Field1->getType() - << Field1->getBitWidthValue(Context.C1); - Context.Diag2(Field2->getLocation(), diag::note_odr_not_bit_field) - << Field2->getDeclName(); - } else { - Context.Diag2(Field2->getLocation(), diag::note_odr_bit_field) - << Field2->getDeclName() << Field2->getType() - << Field2->getBitWidthValue(Context.C2); - Context.Diag1(Field1->getLocation(), diag::note_odr_not_bit_field) - << Field1->getDeclName(); - } - } - return false; - } - - if (Field1->isBitField()) { - // Make sure that the bit-fields are the same length. - unsigned Bits1 = Field1->getBitWidthValue(Context.C1); - unsigned Bits2 = Field2->getBitWidthValue(Context.C2); - - if (Bits1 != Bits2) { - if (Context.Complain) { - Context.Diag2(Owner2->getLocation(), diag::warn_odr_tag_type_inconsistent) - << Context.C2.getTypeDeclType(Owner2); - Context.Diag2(Field2->getLocation(), diag::note_odr_bit_field) - << Field2->getDeclName() << Field2->getType() << Bits2; - Context.Diag1(Field1->getLocation(), diag::note_odr_bit_field) - << Field1->getDeclName() << Field1->getType() << Bits1; - } - return false; - } - } - - return true; -} - -/// \brief Find the index of the given anonymous struct/union within its -/// context. -/// -/// \returns Returns the index of this anonymous struct/union in its context, -/// including the next assigned index (if none of them match). Returns an -/// empty option if the context is not a record, i.e.. if the anonymous -/// struct/union is at namespace or block scope. -static Optional findUntaggedStructOrUnionIndex(RecordDecl *Anon) { - ASTContext &Context = Anon->getASTContext(); - QualType AnonTy = Context.getRecordType(Anon); - - RecordDecl *Owner = dyn_cast(Anon->getDeclContext()); - if (!Owner) - return None; - - unsigned Index = 0; - for (const auto *D : Owner->noload_decls()) { - const auto *F = dyn_cast(D); - if (!F) - continue; - - if (F->isAnonymousStructOrUnion()) { - if (Context.hasSameType(F->getType(), AnonTy)) - break; - ++Index; - continue; - } - - // If the field looks like this: - // struct { ... } A; - QualType FieldType = F->getType(); - if (const auto *RecType = dyn_cast(FieldType)) { - const RecordDecl *RecDecl = RecType->getDecl(); - if (RecDecl->getDeclContext() == Owner && - !RecDecl->getIdentifier()) { - if (Context.hasSameType(FieldType, AnonTy)) - break; - ++Index; - continue; - } - } - } - - return Index; -} - -/// \brief Determine structural equivalence of two records. -static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, - RecordDecl *D1, RecordDecl *D2) { - if (D1->isUnion() != D2->isUnion()) { - if (Context.Complain) { - Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) - << Context.C2.getTypeDeclType(D2); - Context.Diag1(D1->getLocation(), diag::note_odr_tag_kind_here) - << D1->getDeclName() << (unsigned)D1->getTagKind(); - } - return false; - } - - if (D1->isAnonymousStructOrUnion() && D2->isAnonymousStructOrUnion()) { - // If both anonymous structs/unions are in a record context, make sure - // they occur in the same location in the context records. - if (Optional Index1 = findUntaggedStructOrUnionIndex(D1)) { - if (Optional Index2 = findUntaggedStructOrUnionIndex(D2)) { - if (*Index1 != *Index2) - return false; - } - } - } - - // If both declarations are class template specializations, we know - // the ODR applies, so check the template and template arguments. - ClassTemplateSpecializationDecl *Spec1 - = dyn_cast(D1); - ClassTemplateSpecializationDecl *Spec2 - = dyn_cast(D2); - if (Spec1 && Spec2) { - // Check that the specialized templates are the same. - if (!IsStructurallyEquivalent(Context, Spec1->getSpecializedTemplate(), - Spec2->getSpecializedTemplate())) - return false; - - // Check that the template arguments are the same. - if (Spec1->getTemplateArgs().size() != Spec2->getTemplateArgs().size()) - return false; - - for (unsigned I = 0, N = Spec1->getTemplateArgs().size(); I != N; ++I) - if (!IsStructurallyEquivalent(Context, - Spec1->getTemplateArgs().get(I), - Spec2->getTemplateArgs().get(I))) - return false; - } - // If one is a class template specialization and the other is not, these - // structures are different. - else if (Spec1 || Spec2) - return false; - - // Compare the definitions of these two records. If either or both are - // incomplete, we assume that they are equivalent. - D1 = D1->getDefinition(); - D2 = D2->getDefinition(); - if (!D1 || !D2) - return true; - - if (CXXRecordDecl *D1CXX = dyn_cast(D1)) { - if (CXXRecordDecl *D2CXX = dyn_cast(D2)) { - if (D1CXX->getNumBases() != D2CXX->getNumBases()) { - if (Context.Complain) { - Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) - << Context.C2.getTypeDeclType(D2); - Context.Diag2(D2->getLocation(), diag::note_odr_number_of_bases) - << D2CXX->getNumBases(); - Context.Diag1(D1->getLocation(), diag::note_odr_number_of_bases) - << D1CXX->getNumBases(); - } - return false; - } - - // Check the base classes. - for (CXXRecordDecl::base_class_iterator Base1 = D1CXX->bases_begin(), - BaseEnd1 = D1CXX->bases_end(), - Base2 = D2CXX->bases_begin(); - Base1 != BaseEnd1; - ++Base1, ++Base2) { - if (!IsStructurallyEquivalent(Context, - Base1->getType(), Base2->getType())) { - if (Context.Complain) { - Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) - << Context.C2.getTypeDeclType(D2); - Context.Diag2(Base2->getLocStart(), diag::note_odr_base) - << Base2->getType() - << Base2->getSourceRange(); - Context.Diag1(Base1->getLocStart(), diag::note_odr_base) - << Base1->getType() - << Base1->getSourceRange(); - } - return false; - } - - // Check virtual vs. non-virtual inheritance mismatch. - if (Base1->isVirtual() != Base2->isVirtual()) { - if (Context.Complain) { - Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) - << Context.C2.getTypeDeclType(D2); - Context.Diag2(Base2->getLocStart(), - diag::note_odr_virtual_base) - << Base2->isVirtual() << Base2->getSourceRange(); - Context.Diag1(Base1->getLocStart(), diag::note_odr_base) - << Base1->isVirtual() - << Base1->getSourceRange(); - } - return false; - } - } - } else if (D1CXX->getNumBases() > 0) { - if (Context.Complain) { - Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) - << Context.C2.getTypeDeclType(D2); - const CXXBaseSpecifier *Base1 = D1CXX->bases_begin(); - Context.Diag1(Base1->getLocStart(), diag::note_odr_base) - << Base1->getType() - << Base1->getSourceRange(); - Context.Diag2(D2->getLocation(), diag::note_odr_missing_base); - } - return false; - } - } - - // Check the fields for consistency. - RecordDecl::field_iterator Field2 = D2->field_begin(), - Field2End = D2->field_end(); - for (RecordDecl::field_iterator Field1 = D1->field_begin(), - Field1End = D1->field_end(); - Field1 != Field1End; - ++Field1, ++Field2) { - if (Field2 == Field2End) { - if (Context.Complain) { - Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) - << Context.C2.getTypeDeclType(D2); - Context.Diag1(Field1->getLocation(), diag::note_odr_field) - << Field1->getDeclName() << Field1->getType(); - Context.Diag2(D2->getLocation(), diag::note_odr_missing_field); - } - return false; - } - - if (!IsStructurallyEquivalent(Context, *Field1, *Field2)) - return false; - } - - if (Field2 != Field2End) { - if (Context.Complain) { - Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) - << Context.C2.getTypeDeclType(D2); - Context.Diag2(Field2->getLocation(), diag::note_odr_field) - << Field2->getDeclName() << Field2->getType(); - Context.Diag1(D1->getLocation(), diag::note_odr_missing_field); - } - return false; - } - - return true; -} - -/// \brief Determine structural equivalence of two enums. -static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, - EnumDecl *D1, EnumDecl *D2) { - EnumDecl::enumerator_iterator EC2 = D2->enumerator_begin(), - EC2End = D2->enumerator_end(); - for (EnumDecl::enumerator_iterator EC1 = D1->enumerator_begin(), - EC1End = D1->enumerator_end(); - EC1 != EC1End; ++EC1, ++EC2) { - if (EC2 == EC2End) { - if (Context.Complain) { - Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) - << Context.C2.getTypeDeclType(D2); - Context.Diag1(EC1->getLocation(), diag::note_odr_enumerator) - << EC1->getDeclName() - << EC1->getInitVal().toString(10); - Context.Diag2(D2->getLocation(), diag::note_odr_missing_enumerator); - } - return false; - } - - llvm::APSInt Val1 = EC1->getInitVal(); - llvm::APSInt Val2 = EC2->getInitVal(); - if (!llvm::APSInt::isSameValue(Val1, Val2) || - !IsStructurallyEquivalent(EC1->getIdentifier(), EC2->getIdentifier())) { - if (Context.Complain) { - Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) - << Context.C2.getTypeDeclType(D2); - Context.Diag2(EC2->getLocation(), diag::note_odr_enumerator) - << EC2->getDeclName() - << EC2->getInitVal().toString(10); - Context.Diag1(EC1->getLocation(), diag::note_odr_enumerator) - << EC1->getDeclName() - << EC1->getInitVal().toString(10); - } - return false; - } - } - - if (EC2 != EC2End) { - if (Context.Complain) { - Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) - << Context.C2.getTypeDeclType(D2); - Context.Diag2(EC2->getLocation(), diag::note_odr_enumerator) - << EC2->getDeclName() - << EC2->getInitVal().toString(10); - Context.Diag1(D1->getLocation(), diag::note_odr_missing_enumerator); - } - return false; - } - - return true; -} - -static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, - TemplateParameterList *Params1, - TemplateParameterList *Params2) { - if (Params1->size() != Params2->size()) { - if (Context.Complain) { - Context.Diag2(Params2->getTemplateLoc(), - diag::err_odr_different_num_template_parameters) - << Params1->size() << Params2->size(); - Context.Diag1(Params1->getTemplateLoc(), - diag::note_odr_template_parameter_list); - } - return false; - } - - for (unsigned I = 0, N = Params1->size(); I != N; ++I) { - if (Params1->getParam(I)->getKind() != Params2->getParam(I)->getKind()) { - if (Context.Complain) { - Context.Diag2(Params2->getParam(I)->getLocation(), - diag::err_odr_different_template_parameter_kind); - Context.Diag1(Params1->getParam(I)->getLocation(), - diag::note_odr_template_parameter_here); - } - return false; - } - - if (!Context.IsStructurallyEquivalent(Params1->getParam(I), - Params2->getParam(I))) { - - return false; - } - } - - return true; -} - -static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, - TemplateTypeParmDecl *D1, - TemplateTypeParmDecl *D2) { - if (D1->isParameterPack() != D2->isParameterPack()) { - if (Context.Complain) { - Context.Diag2(D2->getLocation(), diag::err_odr_parameter_pack_non_pack) - << D2->isParameterPack(); - Context.Diag1(D1->getLocation(), diag::note_odr_parameter_pack_non_pack) - << D1->isParameterPack(); - } - return false; - } - - return true; -} - -static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, - NonTypeTemplateParmDecl *D1, - NonTypeTemplateParmDecl *D2) { - if (D1->isParameterPack() != D2->isParameterPack()) { - if (Context.Complain) { - Context.Diag2(D2->getLocation(), diag::err_odr_parameter_pack_non_pack) - << D2->isParameterPack(); - Context.Diag1(D1->getLocation(), diag::note_odr_parameter_pack_non_pack) - << D1->isParameterPack(); - } - return false; - } - - // Check types. - if (!Context.IsStructurallyEquivalent(D1->getType(), D2->getType())) { - if (Context.Complain) { - Context.Diag2(D2->getLocation(), - diag::err_odr_non_type_parameter_type_inconsistent) - << D2->getType() << D1->getType(); - Context.Diag1(D1->getLocation(), diag::note_odr_value_here) - << D1->getType(); - } - return false; - } - - return true; -} - -static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, - TemplateTemplateParmDecl *D1, - TemplateTemplateParmDecl *D2) { - if (D1->isParameterPack() != D2->isParameterPack()) { - if (Context.Complain) { - Context.Diag2(D2->getLocation(), diag::err_odr_parameter_pack_non_pack) - << D2->isParameterPack(); - Context.Diag1(D1->getLocation(), diag::note_odr_parameter_pack_non_pack) - << D1->isParameterPack(); - } - return false; - } - - // Check template parameter lists. - return IsStructurallyEquivalent(Context, D1->getTemplateParameters(), - D2->getTemplateParameters()); -} - -static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, - ClassTemplateDecl *D1, - ClassTemplateDecl *D2) { - // Check template parameters. - if (!IsStructurallyEquivalent(Context, - D1->getTemplateParameters(), - D2->getTemplateParameters())) - return false; - - // Check the templated declaration. - return Context.IsStructurallyEquivalent(D1->getTemplatedDecl(), - D2->getTemplatedDecl()); -} - -/// \brief Determine structural equivalence of two declarations. -static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, - Decl *D1, Decl *D2) { - // FIXME: Check for known structural equivalences via a callback of some sort. - - // Check whether we already know that these two declarations are not - // structurally equivalent. - if (Context.NonEquivalentDecls.count(std::make_pair(D1->getCanonicalDecl(), - D2->getCanonicalDecl()))) - return false; - - // Determine whether we've already produced a tentative equivalence for D1. - Decl *&EquivToD1 = Context.TentativeEquivalences[D1->getCanonicalDecl()]; - if (EquivToD1) - return EquivToD1 == D2->getCanonicalDecl(); - - // Produce a tentative equivalence D1 <-> D2, which will be checked later. - EquivToD1 = D2->getCanonicalDecl(); - Context.DeclsToCheck.push_back(D1->getCanonicalDecl()); - return true; -} - -bool StructuralEquivalenceContext::IsStructurallyEquivalent(Decl *D1, - Decl *D2) { - if (!::IsStructurallyEquivalent(*this, D1, D2)) - return false; - - return !Finish(); -} - -bool StructuralEquivalenceContext::IsStructurallyEquivalent(QualType T1, - QualType T2) { - if (!::IsStructurallyEquivalent(*this, T1, T2)) - return false; - - return !Finish(); -} - -bool StructuralEquivalenceContext::Finish() { - while (!DeclsToCheck.empty()) { - // Check the next declaration. - Decl *D1 = DeclsToCheck.front(); - DeclsToCheck.pop_front(); - - Decl *D2 = TentativeEquivalences[D1]; - assert(D2 && "Unrecorded tentative equivalence?"); - - bool Equivalent = true; - - // FIXME: Switch on all declaration kinds. For now, we're just going to - // check the obvious ones. - if (RecordDecl *Record1 = dyn_cast(D1)) { - if (RecordDecl *Record2 = dyn_cast(D2)) { - // Check for equivalent structure names. - IdentifierInfo *Name1 = Record1->getIdentifier(); - if (!Name1 && Record1->getTypedefNameForAnonDecl()) - Name1 = Record1->getTypedefNameForAnonDecl()->getIdentifier(); - IdentifierInfo *Name2 = Record2->getIdentifier(); - if (!Name2 && Record2->getTypedefNameForAnonDecl()) - Name2 = Record2->getTypedefNameForAnonDecl()->getIdentifier(); - if (!::IsStructurallyEquivalent(Name1, Name2) || - !::IsStructurallyEquivalent(*this, Record1, Record2)) - Equivalent = false; - } else { - // Record/non-record mismatch. - Equivalent = false; - } - } else if (EnumDecl *Enum1 = dyn_cast(D1)) { - if (EnumDecl *Enum2 = dyn_cast(D2)) { - // Check for equivalent enum names. - IdentifierInfo *Name1 = Enum1->getIdentifier(); - if (!Name1 && Enum1->getTypedefNameForAnonDecl()) - Name1 = Enum1->getTypedefNameForAnonDecl()->getIdentifier(); - IdentifierInfo *Name2 = Enum2->getIdentifier(); - if (!Name2 && Enum2->getTypedefNameForAnonDecl()) - Name2 = Enum2->getTypedefNameForAnonDecl()->getIdentifier(); - if (!::IsStructurallyEquivalent(Name1, Name2) || - !::IsStructurallyEquivalent(*this, Enum1, Enum2)) - Equivalent = false; - } else { - // Enum/non-enum mismatch - Equivalent = false; - } - } else if (TypedefNameDecl *Typedef1 = dyn_cast(D1)) { - if (TypedefNameDecl *Typedef2 = dyn_cast(D2)) { - if (!::IsStructurallyEquivalent(Typedef1->getIdentifier(), - Typedef2->getIdentifier()) || - !::IsStructurallyEquivalent(*this, - Typedef1->getUnderlyingType(), - Typedef2->getUnderlyingType())) - Equivalent = false; - } else { - // Typedef/non-typedef mismatch. - Equivalent = false; - } - } else if (ClassTemplateDecl *ClassTemplate1 - = dyn_cast(D1)) { - if (ClassTemplateDecl *ClassTemplate2 = dyn_cast(D2)) { - if (!::IsStructurallyEquivalent(ClassTemplate1->getIdentifier(), - ClassTemplate2->getIdentifier()) || - !::IsStructurallyEquivalent(*this, ClassTemplate1, ClassTemplate2)) - Equivalent = false; - } else { - // Class template/non-class-template mismatch. - Equivalent = false; - } - } else if (TemplateTypeParmDecl *TTP1= dyn_cast(D1)) { - if (TemplateTypeParmDecl *TTP2 = dyn_cast(D2)) { - if (!::IsStructurallyEquivalent(*this, TTP1, TTP2)) - Equivalent = false; - } else { - // Kind mismatch. - Equivalent = false; - } - } else if (NonTypeTemplateParmDecl *NTTP1 - = dyn_cast(D1)) { - if (NonTypeTemplateParmDecl *NTTP2 - = dyn_cast(D2)) { - if (!::IsStructurallyEquivalent(*this, NTTP1, NTTP2)) - Equivalent = false; - } else { - // Kind mismatch. - Equivalent = false; - } - } else if (TemplateTemplateParmDecl *TTP1 - = dyn_cast(D1)) { - if (TemplateTemplateParmDecl *TTP2 - = dyn_cast(D2)) { - if (!::IsStructurallyEquivalent(*this, TTP1, TTP2)) - Equivalent = false; - } else { - // Kind mismatch. - Equivalent = false; - } - } - - if (!Equivalent) { - // Note that these two declarations are not equivalent (and we already - // know about it). - NonEquivalentDecls.insert(std::make_pair(D1->getCanonicalDecl(), - D2->getCanonicalDecl())); - return true; - } - // FIXME: Check other declaration kinds! - } - - return false; -} - //---------------------------------------------------------------------------- // Import Types //---------------------------------------------------------------------------- +using namespace clang; + QualType ASTNodeImporter::VisitType(const Type *T) { Importer.FromDiag(SourceLocation(), diag::err_unsupported_ast_node) << T->getTypeClassName(); @@ -2634,7 +1251,7 @@ StructuralEquivalenceContext Ctx(Importer.getFromContext(), Importer.getToContext(), Importer.getNonEquivalentDecls()); - return Ctx.IsStructurallyEquivalent(From, To); + return Ctx.IsStructurallyEquivalent(From, To); } bool ASTNodeImporter::IsStructuralMatch(VarTemplateDecl *From, @@ -2813,10 +1430,10 @@ FoundTypedef->getUnderlyingType())) return Importer.Imported(D, FoundTypedef); } - + ConflictingDecls.push_back(FoundDecls[I]); } - + if (!ConflictingDecls.empty()) { Name = Importer.HandleNameConflict(Name, DC, IDNS, ConflictingDecls.data(), @@ -2825,7 +1442,7 @@ return nullptr; } } - + // Import the underlying type of this typedef; QualType T = Importer.Import(D->getUnderlyingType()); if (T.isNull()) @@ -2845,12 +1462,12 @@ StartL, Loc, Name.getAsIdentifierInfo(), TInfo); - + ToTypedef->setAccess(D->getAccess()); ToTypedef->setLexicalDeclContext(LexicalDC); Importer.Imported(D, ToTypedef); LexicalDC->addDeclInternal(ToTypedef); - + return ToTypedef; } @@ -3024,9 +1641,10 @@ FoundRecord->isAnonymousStructOrUnion()) { // If both anonymous structs/unions are in a record context, make sure // they occur in the same location in the context records. - if (Optional Index1 - = findUntaggedStructOrUnionIndex(D)) { - if (Optional Index2 = + if (Optional Index1 = + StructuralEquivalenceContext::findUntaggedStructOrUnionIndex( + D)) { + if (Optional Index2 = StructuralEquivalenceContext:: findUntaggedStructOrUnionIndex(FoundRecord)) { if (*Index1 != *Index2) continue; @@ -3216,7 +1834,7 @@ for (unsigned I = 0, N = FoundDecls.size(); I != N; ++I) { if (!FoundDecls[I]->isInIdentifierNamespace(IDNS)) continue; - + if (FunctionDecl *FoundFunction = dyn_cast(FoundDecls[I])) { if (FoundFunction->hasExternalFormalLinkage() && D->hasExternalFormalLinkage()) { @@ -3225,14 +1843,14 @@ // FIXME: Actually try to merge the body and other attributes. return Importer.Imported(D, FoundFunction); } - + // FIXME: Check for overloading more carefully, e.g., by boosting // Sema::IsOverload out to the AST library. - + // Function overloading is okay in C++. if (Importer.getToContext().getLangOpts().CPlusPlus) continue; - + // Complain about inconsistent function types. Importer.ToDiag(Loc, diag::err_odr_function_type_inconsistent) << Name << D->getType() << FoundFunction->getType(); @@ -3241,10 +1859,10 @@ << FoundFunction->getType(); } } - + ConflictingDecls.push_back(FoundDecls[I]); } - + if (!ConflictingDecls.empty()) { Name = Importer.HandleNameConflict(Name, DC, IDNS, ConflictingDecls.data(), @@ -3449,12 +2067,12 @@ if (!Name && getFieldIndex(D) != getFieldIndex(FoundField)) continue; - if (Importer.IsStructurallyEquivalent(D->getType(), + if (Importer.IsStructurallyEquivalent(D->getType(), FoundField->getType())) { Importer.Imported(D, FoundField); return FoundField; } - + Importer.ToDiag(Loc, diag::err_odr_field_type_inconsistent) << Name << D->getType() << FoundField->getType(); Importer.ToDiag(FoundField->getLocation(), diag::note_odr_value_here) @@ -3514,7 +2132,7 @@ if (!Name && getFieldIndex(D) != getFieldIndex(FoundField)) continue; - if (Importer.IsStructurallyEquivalent(D->getType(), + if (Importer.IsStructurallyEquivalent(D->getType(), FoundField->getType(), !Name.isEmpty())) { Importer.Imported(D, FoundField); @@ -3638,12 +2256,12 @@ if (ToD) return ToD; - // Determine whether we've already imported this ivar + // Determine whether we've already imported this ivar SmallVector FoundDecls; DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls); for (unsigned I = 0, N = FoundDecls.size(); I != N; ++I) { if (ObjCIvarDecl *FoundIvar = dyn_cast(FoundDecls[I])) { - if (Importer.IsStructurallyEquivalent(D->getType(), + if (Importer.IsStructurallyEquivalent(D->getType(), FoundIvar->getType())) { Importer.Imported(D, FoundIvar); return FoundIvar; @@ -3702,12 +2320,12 @@ for (unsigned I = 0, N = FoundDecls.size(); I != N; ++I) { if (!FoundDecls[I]->isInIdentifierNamespace(IDNS)) continue; - + if (VarDecl *FoundVar = dyn_cast(FoundDecls[I])) { // We have found a variable that we may need to merge with. Check it. if (FoundVar->hasExternalFormalLinkage() && D->hasExternalFormalLinkage()) { - if (Importer.IsStructurallyEquivalent(D->getType(), + if (Importer.IsStructurallyEquivalent(D->getType(), FoundVar->getType())) { MergeWithVar = FoundVar; break; @@ -3931,12 +2549,12 @@ } // Check parameter types. - for (ObjCMethodDecl::param_iterator P = D->param_begin(), + for (ObjCMethodDecl::param_iterator P = D->param_begin(), PEnd = D->param_end(), FoundP = FoundMethod->param_begin(); P != PEnd; ++P, ++FoundP) { - if (!Importer.IsStructurallyEquivalent((*P)->getType(), + if (!Importer.IsStructurallyEquivalent((*P)->getType(), (*FoundP)->getType())) { - Importer.FromDiag((*P)->getLocation(), + Importer.FromDiag((*P)->getLocation(), diag::err_odr_objc_method_param_type_inconsistent) << D->isInstanceMethod() << Name << (*P)->getType() << (*FoundP)->getType(); @@ -4542,7 +3160,7 @@ if (ObjCPropertyDecl *FoundProp = dyn_cast(FoundDecls[I])) { // Check property types. - if (!Importer.IsStructurallyEquivalent(D->getType(), + if (!Importer.IsStructurallyEquivalent(D->getType(), FoundProp->getType())) { Importer.ToDiag(Loc, diag::err_odr_objc_property_type_inconsistent) << Name << D->getType() << FoundProp->getType(); @@ -7597,7 +6215,7 @@ = ImportedTypes.find(From.getTypePtr()); if (Pos != ImportedTypes.end() && ToContext.hasSameType(Import(From), To)) return true; - + StructuralEquivalenceContext Ctx(FromContext, ToContext, NonEquivalentDecls, false, Complain); return Ctx.IsStructurallyEquivalent(From, To); Index: cfe/trunk/lib/AST/ASTStructuralEquivalence.cpp =================================================================== --- cfe/trunk/lib/AST/ASTStructuralEquivalence.cpp +++ cfe/trunk/lib/AST/ASTStructuralEquivalence.cpp @@ -0,0 +1,1318 @@ +//===--- ASTStructuralEquivalence.cpp - -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implement StructuralEquivalenceContext class and helper functions +// for layout matching. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTStructuralEquivalence.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTDiagnostic.h" +#include "clang/AST/ASTImporter.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclVisitor.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/AST/TypeVisitor.h" +#include "clang/Basic/SourceManager.h" + +namespace { + +using namespace clang; + +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + QualType T1, QualType T2); +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + Decl *D1, Decl *D2); +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + const TemplateArgument &Arg1, + const TemplateArgument &Arg2); + +/// Determine structural equivalence of two expressions. +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + Expr *E1, Expr *E2) { + if (!E1 || !E2) + return E1 == E2; + + // FIXME: Actually perform a structural comparison! + return true; +} + +/// Determine whether two identifiers are equivalent. +static bool IsStructurallyEquivalent(const IdentifierInfo *Name1, + const IdentifierInfo *Name2) { + if (!Name1 || !Name2) + return Name1 == Name2; + + return Name1->getName() == Name2->getName(); +} + +/// Determine whether two nested-name-specifiers are equivalent. +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + NestedNameSpecifier *NNS1, + NestedNameSpecifier *NNS2) { + if (NNS1->getKind() != NNS2->getKind()) + return false; + + NestedNameSpecifier *Prefix1 = NNS1->getPrefix(), + *Prefix2 = NNS2->getPrefix(); + if ((bool)Prefix1 != (bool)Prefix2) + return false; + + if (Prefix1) + if (!IsStructurallyEquivalent(Context, Prefix1, Prefix2)) + return false; + + switch (NNS1->getKind()) { + case NestedNameSpecifier::Identifier: + return IsStructurallyEquivalent(NNS1->getAsIdentifier(), + NNS2->getAsIdentifier()); + case NestedNameSpecifier::Namespace: + return IsStructurallyEquivalent(Context, NNS1->getAsNamespace(), + NNS2->getAsNamespace()); + case NestedNameSpecifier::NamespaceAlias: + return IsStructurallyEquivalent(Context, NNS1->getAsNamespaceAlias(), + NNS2->getAsNamespaceAlias()); + case NestedNameSpecifier::TypeSpec: + case NestedNameSpecifier::TypeSpecWithTemplate: + return IsStructurallyEquivalent(Context, QualType(NNS1->getAsType(), 0), + QualType(NNS2->getAsType(), 0)); + case NestedNameSpecifier::Global: + return true; + case NestedNameSpecifier::Super: + return IsStructurallyEquivalent(Context, NNS1->getAsRecordDecl(), + NNS2->getAsRecordDecl()); + } + return false; +} + +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + const TemplateName &N1, + const TemplateName &N2) { + if (N1.getKind() != N2.getKind()) + return false; + switch (N1.getKind()) { + case TemplateName::Template: + return IsStructurallyEquivalent(Context, N1.getAsTemplateDecl(), + N2.getAsTemplateDecl()); + + case TemplateName::OverloadedTemplate: { + OverloadedTemplateStorage *OS1 = N1.getAsOverloadedTemplate(), + *OS2 = N2.getAsOverloadedTemplate(); + OverloadedTemplateStorage::iterator I1 = OS1->begin(), I2 = OS2->begin(), + E1 = OS1->end(), E2 = OS2->end(); + for (; I1 != E1 && I2 != E2; ++I1, ++I2) + if (!IsStructurallyEquivalent(Context, *I1, *I2)) + return false; + return I1 == E1 && I2 == E2; + } + + case TemplateName::QualifiedTemplate: { + QualifiedTemplateName *QN1 = N1.getAsQualifiedTemplateName(), + *QN2 = N2.getAsQualifiedTemplateName(); + return IsStructurallyEquivalent(Context, QN1->getDecl(), QN2->getDecl()) && + IsStructurallyEquivalent(Context, QN1->getQualifier(), + QN2->getQualifier()); + } + + case TemplateName::DependentTemplate: { + DependentTemplateName *DN1 = N1.getAsDependentTemplateName(), + *DN2 = N2.getAsDependentTemplateName(); + if (!IsStructurallyEquivalent(Context, DN1->getQualifier(), + DN2->getQualifier())) + return false; + if (DN1->isIdentifier() && DN2->isIdentifier()) + return IsStructurallyEquivalent(DN1->getIdentifier(), + DN2->getIdentifier()); + else if (DN1->isOverloadedOperator() && DN2->isOverloadedOperator()) + return DN1->getOperator() == DN2->getOperator(); + return false; + } + + case TemplateName::SubstTemplateTemplateParm: { + SubstTemplateTemplateParmStorage *TS1 = N1.getAsSubstTemplateTemplateParm(), + *TS2 = N2.getAsSubstTemplateTemplateParm(); + return IsStructurallyEquivalent(Context, TS1->getParameter(), + TS2->getParameter()) && + IsStructurallyEquivalent(Context, TS1->getReplacement(), + TS2->getReplacement()); + } + case TemplateName::SubstTemplateTemplateParmPack: { + SubstTemplateTemplateParmPackStorage + *P1 = N1.getAsSubstTemplateTemplateParmPack(), + *P2 = N2.getAsSubstTemplateTemplateParmPack(); + return IsStructurallyEquivalent(Context, P1->getArgumentPack(), + P2->getArgumentPack()) && + IsStructurallyEquivalent(Context, P1->getParameterPack(), + P2->getParameterPack()); + } + } + return false; +} + +/// Determine whether two template arguments are equivalent. +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + const TemplateArgument &Arg1, + const TemplateArgument &Arg2) { + if (Arg1.getKind() != Arg2.getKind()) + return false; + + switch (Arg1.getKind()) { + case TemplateArgument::Null: + return true; + + case TemplateArgument::Type: + return Context.IsStructurallyEquivalent(Arg1.getAsType(), Arg2.getAsType()); + + case TemplateArgument::Integral: + if (!Context.IsStructurallyEquivalent(Arg1.getIntegralType(), + Arg2.getIntegralType())) + return false; + + return llvm::APSInt::isSameValue(Arg1.getAsIntegral(), + Arg2.getAsIntegral()); + + case TemplateArgument::Declaration: + return Context.IsStructurallyEquivalent(Arg1.getAsDecl(), Arg2.getAsDecl()); + + case TemplateArgument::NullPtr: + return true; // FIXME: Is this correct? + + case TemplateArgument::Template: + return IsStructurallyEquivalent(Context, Arg1.getAsTemplate(), + Arg2.getAsTemplate()); + + case TemplateArgument::TemplateExpansion: + return IsStructurallyEquivalent(Context, + Arg1.getAsTemplateOrTemplatePattern(), + Arg2.getAsTemplateOrTemplatePattern()); + + case TemplateArgument::Expression: + return IsStructurallyEquivalent(Context, Arg1.getAsExpr(), + Arg2.getAsExpr()); + + case TemplateArgument::Pack: + if (Arg1.pack_size() != Arg2.pack_size()) + return false; + + for (unsigned I = 0, N = Arg1.pack_size(); I != N; ++I) + if (!IsStructurallyEquivalent(Context, Arg1.pack_begin()[I], + Arg2.pack_begin()[I])) + return false; + + return true; + } + + llvm_unreachable("Invalid template argument kind"); +} + +/// Determine structural equivalence for the common part of array +/// types. +static bool IsArrayStructurallyEquivalent(StructuralEquivalenceContext &Context, + const ArrayType *Array1, + const ArrayType *Array2) { + if (!IsStructurallyEquivalent(Context, Array1->getElementType(), + Array2->getElementType())) + return false; + if (Array1->getSizeModifier() != Array2->getSizeModifier()) + return false; + if (Array1->getIndexTypeQualifiers() != Array2->getIndexTypeQualifiers()) + return false; + + return true; +} + +/// Determine structural equivalence of two types. +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + QualType T1, QualType T2) { + if (T1.isNull() || T2.isNull()) + return T1.isNull() && T2.isNull(); + + if (!Context.StrictTypeSpelling) { + // We aren't being strict about token-to-token equivalence of types, + // so map down to the canonical type. + T1 = Context.FromCtx.getCanonicalType(T1); + T2 = Context.ToCtx.getCanonicalType(T2); + } + + if (T1.getQualifiers() != T2.getQualifiers()) + return false; + + Type::TypeClass TC = T1->getTypeClass(); + + if (T1->getTypeClass() != T2->getTypeClass()) { + // Compare function types with prototypes vs. without prototypes as if + // both did not have prototypes. + if (T1->getTypeClass() == Type::FunctionProto && + T2->getTypeClass() == Type::FunctionNoProto) + TC = Type::FunctionNoProto; + else if (T1->getTypeClass() == Type::FunctionNoProto && + T2->getTypeClass() == Type::FunctionProto) + TC = Type::FunctionNoProto; + else + return false; + } + + switch (TC) { + case Type::Builtin: + // FIXME: Deal with Char_S/Char_U. + if (cast(T1)->getKind() != cast(T2)->getKind()) + return false; + break; + + case Type::Complex: + if (!IsStructurallyEquivalent(Context, + cast(T1)->getElementType(), + cast(T2)->getElementType())) + return false; + break; + + case Type::Adjusted: + case Type::Decayed: + if (!IsStructurallyEquivalent(Context, + cast(T1)->getOriginalType(), + cast(T2)->getOriginalType())) + return false; + break; + + case Type::Pointer: + if (!IsStructurallyEquivalent(Context, + cast(T1)->getPointeeType(), + cast(T2)->getPointeeType())) + return false; + break; + + case Type::BlockPointer: + if (!IsStructurallyEquivalent(Context, + cast(T1)->getPointeeType(), + cast(T2)->getPointeeType())) + return false; + break; + + case Type::LValueReference: + case Type::RValueReference: { + const ReferenceType *Ref1 = cast(T1); + const ReferenceType *Ref2 = cast(T2); + if (Ref1->isSpelledAsLValue() != Ref2->isSpelledAsLValue()) + return false; + if (Ref1->isInnerRef() != Ref2->isInnerRef()) + return false; + if (!IsStructurallyEquivalent(Context, Ref1->getPointeeTypeAsWritten(), + Ref2->getPointeeTypeAsWritten())) + return false; + break; + } + + case Type::MemberPointer: { + const MemberPointerType *MemPtr1 = cast(T1); + const MemberPointerType *MemPtr2 = cast(T2); + if (!IsStructurallyEquivalent(Context, MemPtr1->getPointeeType(), + MemPtr2->getPointeeType())) + return false; + if (!IsStructurallyEquivalent(Context, QualType(MemPtr1->getClass(), 0), + QualType(MemPtr2->getClass(), 0))) + return false; + break; + } + + case Type::ConstantArray: { + const ConstantArrayType *Array1 = cast(T1); + const ConstantArrayType *Array2 = cast(T2); + if (!llvm::APInt::isSameValue(Array1->getSize(), Array2->getSize())) + return false; + + if (!IsArrayStructurallyEquivalent(Context, Array1, Array2)) + return false; + break; + } + + case Type::IncompleteArray: + if (!IsArrayStructurallyEquivalent(Context, cast(T1), + cast(T2))) + return false; + break; + + case Type::VariableArray: { + const VariableArrayType *Array1 = cast(T1); + const VariableArrayType *Array2 = cast(T2); + if (!IsStructurallyEquivalent(Context, Array1->getSizeExpr(), + Array2->getSizeExpr())) + return false; + + if (!IsArrayStructurallyEquivalent(Context, Array1, Array2)) + return false; + + break; + } + + case Type::DependentSizedArray: { + const DependentSizedArrayType *Array1 = cast(T1); + const DependentSizedArrayType *Array2 = cast(T2); + if (!IsStructurallyEquivalent(Context, Array1->getSizeExpr(), + Array2->getSizeExpr())) + return false; + + if (!IsArrayStructurallyEquivalent(Context, Array1, Array2)) + return false; + + break; + } + + case Type::DependentSizedExtVector: { + const DependentSizedExtVectorType *Vec1 = + cast(T1); + const DependentSizedExtVectorType *Vec2 = + cast(T2); + if (!IsStructurallyEquivalent(Context, Vec1->getSizeExpr(), + Vec2->getSizeExpr())) + return false; + if (!IsStructurallyEquivalent(Context, Vec1->getElementType(), + Vec2->getElementType())) + return false; + break; + } + + case Type::Vector: + case Type::ExtVector: { + const VectorType *Vec1 = cast(T1); + const VectorType *Vec2 = cast(T2); + if (!IsStructurallyEquivalent(Context, Vec1->getElementType(), + Vec2->getElementType())) + return false; + if (Vec1->getNumElements() != Vec2->getNumElements()) + return false; + if (Vec1->getVectorKind() != Vec2->getVectorKind()) + return false; + break; + } + + case Type::FunctionProto: { + const FunctionProtoType *Proto1 = cast(T1); + const FunctionProtoType *Proto2 = cast(T2); + if (Proto1->getNumParams() != Proto2->getNumParams()) + return false; + for (unsigned I = 0, N = Proto1->getNumParams(); I != N; ++I) { + if (!IsStructurallyEquivalent(Context, Proto1->getParamType(I), + Proto2->getParamType(I))) + return false; + } + if (Proto1->isVariadic() != Proto2->isVariadic()) + return false; + if (Proto1->getExceptionSpecType() != Proto2->getExceptionSpecType()) + return false; + if (Proto1->getExceptionSpecType() == EST_Dynamic) { + if (Proto1->getNumExceptions() != Proto2->getNumExceptions()) + return false; + for (unsigned I = 0, N = Proto1->getNumExceptions(); I != N; ++I) { + if (!IsStructurallyEquivalent(Context, Proto1->getExceptionType(I), + Proto2->getExceptionType(I))) + return false; + } + } else if (Proto1->getExceptionSpecType() == EST_ComputedNoexcept) { + if (!IsStructurallyEquivalent(Context, Proto1->getNoexceptExpr(), + Proto2->getNoexceptExpr())) + return false; + } + if (Proto1->getTypeQuals() != Proto2->getTypeQuals()) + return false; + + // Fall through to check the bits common with FunctionNoProtoType. + } + + case Type::FunctionNoProto: { + const FunctionType *Function1 = cast(T1); + const FunctionType *Function2 = cast(T2); + if (!IsStructurallyEquivalent(Context, Function1->getReturnType(), + Function2->getReturnType())) + return false; + if (Function1->getExtInfo() != Function2->getExtInfo()) + return false; + break; + } + + case Type::UnresolvedUsing: + if (!IsStructurallyEquivalent(Context, + cast(T1)->getDecl(), + cast(T2)->getDecl())) + return false; + + break; + + case Type::Attributed: + if (!IsStructurallyEquivalent(Context, + cast(T1)->getModifiedType(), + cast(T2)->getModifiedType())) + return false; + if (!IsStructurallyEquivalent( + Context, cast(T1)->getEquivalentType(), + cast(T2)->getEquivalentType())) + return false; + break; + + case Type::Paren: + if (!IsStructurallyEquivalent(Context, cast(T1)->getInnerType(), + cast(T2)->getInnerType())) + return false; + break; + + case Type::Typedef: + if (!IsStructurallyEquivalent(Context, cast(T1)->getDecl(), + cast(T2)->getDecl())) + return false; + break; + + case Type::TypeOfExpr: + if (!IsStructurallyEquivalent( + Context, cast(T1)->getUnderlyingExpr(), + cast(T2)->getUnderlyingExpr())) + return false; + break; + + case Type::TypeOf: + if (!IsStructurallyEquivalent(Context, + cast(T1)->getUnderlyingType(), + cast(T2)->getUnderlyingType())) + return false; + break; + + case Type::UnaryTransform: + if (!IsStructurallyEquivalent( + Context, cast(T1)->getUnderlyingType(), + cast(T1)->getUnderlyingType())) + return false; + break; + + case Type::Decltype: + if (!IsStructurallyEquivalent(Context, + cast(T1)->getUnderlyingExpr(), + cast(T2)->getUnderlyingExpr())) + return false; + break; + + case Type::Auto: + if (!IsStructurallyEquivalent(Context, cast(T1)->getDeducedType(), + cast(T2)->getDeducedType())) + return false; + break; + + case Type::DeducedTemplateSpecialization: { + auto *DT1 = cast(T1); + auto *DT2 = cast(T2); + if (!IsStructurallyEquivalent(Context, DT1->getTemplateName(), + DT2->getTemplateName())) + return false; + if (!IsStructurallyEquivalent(Context, DT1->getDeducedType(), + DT2->getDeducedType())) + return false; + break; + } + + case Type::Record: + case Type::Enum: + if (!IsStructurallyEquivalent(Context, cast(T1)->getDecl(), + cast(T2)->getDecl())) + return false; + break; + + case Type::TemplateTypeParm: { + const TemplateTypeParmType *Parm1 = cast(T1); + const TemplateTypeParmType *Parm2 = cast(T2); + if (Parm1->getDepth() != Parm2->getDepth()) + return false; + if (Parm1->getIndex() != Parm2->getIndex()) + return false; + if (Parm1->isParameterPack() != Parm2->isParameterPack()) + return false; + + // Names of template type parameters are never significant. + break; + } + + case Type::SubstTemplateTypeParm: { + const SubstTemplateTypeParmType *Subst1 = + cast(T1); + const SubstTemplateTypeParmType *Subst2 = + cast(T2); + if (!IsStructurallyEquivalent(Context, + QualType(Subst1->getReplacedParameter(), 0), + QualType(Subst2->getReplacedParameter(), 0))) + return false; + if (!IsStructurallyEquivalent(Context, Subst1->getReplacementType(), + Subst2->getReplacementType())) + return false; + break; + } + + case Type::SubstTemplateTypeParmPack: { + const SubstTemplateTypeParmPackType *Subst1 = + cast(T1); + const SubstTemplateTypeParmPackType *Subst2 = + cast(T2); + if (!IsStructurallyEquivalent(Context, + QualType(Subst1->getReplacedParameter(), 0), + QualType(Subst2->getReplacedParameter(), 0))) + return false; + if (!IsStructurallyEquivalent(Context, Subst1->getArgumentPack(), + Subst2->getArgumentPack())) + return false; + break; + } + case Type::TemplateSpecialization: { + const TemplateSpecializationType *Spec1 = + cast(T1); + const TemplateSpecializationType *Spec2 = + cast(T2); + if (!IsStructurallyEquivalent(Context, Spec1->getTemplateName(), + Spec2->getTemplateName())) + return false; + if (Spec1->getNumArgs() != Spec2->getNumArgs()) + return false; + for (unsigned I = 0, N = Spec1->getNumArgs(); I != N; ++I) { + if (!IsStructurallyEquivalent(Context, Spec1->getArg(I), + Spec2->getArg(I))) + return false; + } + break; + } + + case Type::Elaborated: { + const ElaboratedType *Elab1 = cast(T1); + const ElaboratedType *Elab2 = cast(T2); + // CHECKME: what if a keyword is ETK_None or ETK_typename ? + if (Elab1->getKeyword() != Elab2->getKeyword()) + return false; + if (!IsStructurallyEquivalent(Context, Elab1->getQualifier(), + Elab2->getQualifier())) + return false; + if (!IsStructurallyEquivalent(Context, Elab1->getNamedType(), + Elab2->getNamedType())) + return false; + break; + } + + case Type::InjectedClassName: { + const InjectedClassNameType *Inj1 = cast(T1); + const InjectedClassNameType *Inj2 = cast(T2); + if (!IsStructurallyEquivalent(Context, + Inj1->getInjectedSpecializationType(), + Inj2->getInjectedSpecializationType())) + return false; + break; + } + + case Type::DependentName: { + const DependentNameType *Typename1 = cast(T1); + const DependentNameType *Typename2 = cast(T2); + if (!IsStructurallyEquivalent(Context, Typename1->getQualifier(), + Typename2->getQualifier())) + return false; + if (!IsStructurallyEquivalent(Typename1->getIdentifier(), + Typename2->getIdentifier())) + return false; + + break; + } + + case Type::DependentTemplateSpecialization: { + const DependentTemplateSpecializationType *Spec1 = + cast(T1); + const DependentTemplateSpecializationType *Spec2 = + cast(T2); + if (!IsStructurallyEquivalent(Context, Spec1->getQualifier(), + Spec2->getQualifier())) + return false; + if (!IsStructurallyEquivalent(Spec1->getIdentifier(), + Spec2->getIdentifier())) + return false; + if (Spec1->getNumArgs() != Spec2->getNumArgs()) + return false; + for (unsigned I = 0, N = Spec1->getNumArgs(); I != N; ++I) { + if (!IsStructurallyEquivalent(Context, Spec1->getArg(I), + Spec2->getArg(I))) + return false; + } + break; + } + + case Type::PackExpansion: + if (!IsStructurallyEquivalent(Context, + cast(T1)->getPattern(), + cast(T2)->getPattern())) + return false; + break; + + case Type::ObjCInterface: { + const ObjCInterfaceType *Iface1 = cast(T1); + const ObjCInterfaceType *Iface2 = cast(T2); + if (!IsStructurallyEquivalent(Context, Iface1->getDecl(), + Iface2->getDecl())) + return false; + break; + } + + case Type::ObjCTypeParam: { + const ObjCTypeParamType *Obj1 = cast(T1); + const ObjCTypeParamType *Obj2 = cast(T2); + if (!IsStructurallyEquivalent(Context, Obj1->getDecl(), Obj2->getDecl())) + return false; + + if (Obj1->getNumProtocols() != Obj2->getNumProtocols()) + return false; + for (unsigned I = 0, N = Obj1->getNumProtocols(); I != N; ++I) { + if (!IsStructurallyEquivalent(Context, Obj1->getProtocol(I), + Obj2->getProtocol(I))) + return false; + } + break; + } + case Type::ObjCObject: { + const ObjCObjectType *Obj1 = cast(T1); + const ObjCObjectType *Obj2 = cast(T2); + if (!IsStructurallyEquivalent(Context, Obj1->getBaseType(), + Obj2->getBaseType())) + return false; + if (Obj1->getNumProtocols() != Obj2->getNumProtocols()) + return false; + for (unsigned I = 0, N = Obj1->getNumProtocols(); I != N; ++I) { + if (!IsStructurallyEquivalent(Context, Obj1->getProtocol(I), + Obj2->getProtocol(I))) + return false; + } + break; + } + + case Type::ObjCObjectPointer: { + const ObjCObjectPointerType *Ptr1 = cast(T1); + const ObjCObjectPointerType *Ptr2 = cast(T2); + if (!IsStructurallyEquivalent(Context, Ptr1->getPointeeType(), + Ptr2->getPointeeType())) + return false; + break; + } + + case Type::Atomic: { + if (!IsStructurallyEquivalent(Context, cast(T1)->getValueType(), + cast(T2)->getValueType())) + return false; + break; + } + + case Type::Pipe: { + if (!IsStructurallyEquivalent(Context, cast(T1)->getElementType(), + cast(T2)->getElementType())) + return false; + break; + } + + } // end switch + + return true; +} + +/// Determine structural equivalence of two fields. +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + FieldDecl *Field1, FieldDecl *Field2) { + RecordDecl *Owner2 = cast(Field2->getDeclContext()); + + // For anonymous structs/unions, match up the anonymous struct/union type + // declarations directly, so that we don't go off searching for anonymous + // types + if (Field1->isAnonymousStructOrUnion() && + Field2->isAnonymousStructOrUnion()) { + RecordDecl *D1 = Field1->getType()->castAs()->getDecl(); + RecordDecl *D2 = Field2->getType()->castAs()->getDecl(); + return IsStructurallyEquivalent(Context, D1, D2); + } + + // Check for equivalent field names. + IdentifierInfo *Name1 = Field1->getIdentifier(); + IdentifierInfo *Name2 = Field2->getIdentifier(); + if (!::IsStructurallyEquivalent(Name1, Name2)) + return false; + + if (!IsStructurallyEquivalent(Context, Field1->getType(), + Field2->getType())) { + if (Context.Complain) { + Context.Diag2(Owner2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.ToCtx.getTypeDeclType(Owner2); + Context.Diag2(Field2->getLocation(), diag::note_odr_field) + << Field2->getDeclName() << Field2->getType(); + Context.Diag1(Field1->getLocation(), diag::note_odr_field) + << Field1->getDeclName() << Field1->getType(); + } + return false; + } + + if (Field1->isBitField() != Field2->isBitField()) { + if (Context.Complain) { + Context.Diag2(Owner2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.ToCtx.getTypeDeclType(Owner2); + if (Field1->isBitField()) { + Context.Diag1(Field1->getLocation(), diag::note_odr_bit_field) + << Field1->getDeclName() << Field1->getType() + << Field1->getBitWidthValue(Context.FromCtx); + Context.Diag2(Field2->getLocation(), diag::note_odr_not_bit_field) + << Field2->getDeclName(); + } else { + Context.Diag2(Field2->getLocation(), diag::note_odr_bit_field) + << Field2->getDeclName() << Field2->getType() + << Field2->getBitWidthValue(Context.ToCtx); + Context.Diag1(Field1->getLocation(), diag::note_odr_not_bit_field) + << Field1->getDeclName(); + } + } + return false; + } + + if (Field1->isBitField()) { + // Make sure that the bit-fields are the same length. + unsigned Bits1 = Field1->getBitWidthValue(Context.FromCtx); + unsigned Bits2 = Field2->getBitWidthValue(Context.ToCtx); + + if (Bits1 != Bits2) { + if (Context.Complain) { + Context.Diag2(Owner2->getLocation(), + diag::warn_odr_tag_type_inconsistent) + << Context.ToCtx.getTypeDeclType(Owner2); + Context.Diag2(Field2->getLocation(), diag::note_odr_bit_field) + << Field2->getDeclName() << Field2->getType() << Bits2; + Context.Diag1(Field1->getLocation(), diag::note_odr_bit_field) + << Field1->getDeclName() << Field1->getType() << Bits1; + } + return false; + } + } + + return true; +} + +/// Determine structural equivalence of two records. +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + RecordDecl *D1, RecordDecl *D2) { + if (D1->isUnion() != D2->isUnion()) { + if (Context.Complain) { + Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.ToCtx.getTypeDeclType(D2); + Context.Diag1(D1->getLocation(), diag::note_odr_tag_kind_here) + << D1->getDeclName() << (unsigned)D1->getTagKind(); + } + return false; + } + + if (D1->isAnonymousStructOrUnion() && D2->isAnonymousStructOrUnion()) { + // If both anonymous structs/unions are in a record context, make sure + // they occur in the same location in the context records. + if (Optional Index1 = + StructuralEquivalenceContext::findUntaggedStructOrUnionIndex(D1)) { + if (Optional Index2 = + StructuralEquivalenceContext::findUntaggedStructOrUnionIndex( + D2)) { + if (*Index1 != *Index2) + return false; + } + } + } + + // If both declarations are class template specializations, we know + // the ODR applies, so check the template and template arguments. + ClassTemplateSpecializationDecl *Spec1 = + dyn_cast(D1); + ClassTemplateSpecializationDecl *Spec2 = + dyn_cast(D2); + if (Spec1 && Spec2) { + // Check that the specialized templates are the same. + if (!IsStructurallyEquivalent(Context, Spec1->getSpecializedTemplate(), + Spec2->getSpecializedTemplate())) + return false; + + // Check that the template arguments are the same. + if (Spec1->getTemplateArgs().size() != Spec2->getTemplateArgs().size()) + return false; + + for (unsigned I = 0, N = Spec1->getTemplateArgs().size(); I != N; ++I) + if (!IsStructurallyEquivalent(Context, Spec1->getTemplateArgs().get(I), + Spec2->getTemplateArgs().get(I))) + return false; + } + // If one is a class template specialization and the other is not, these + // structures are different. + else if (Spec1 || Spec2) + return false; + + // Compare the definitions of these two records. If either or both are + // incomplete, we assume that they are equivalent. + D1 = D1->getDefinition(); + D2 = D2->getDefinition(); + if (!D1 || !D2) + return true; + + if (CXXRecordDecl *D1CXX = dyn_cast(D1)) { + if (CXXRecordDecl *D2CXX = dyn_cast(D2)) { + if (D1CXX->getNumBases() != D2CXX->getNumBases()) { + if (Context.Complain) { + Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.ToCtx.getTypeDeclType(D2); + Context.Diag2(D2->getLocation(), diag::note_odr_number_of_bases) + << D2CXX->getNumBases(); + Context.Diag1(D1->getLocation(), diag::note_odr_number_of_bases) + << D1CXX->getNumBases(); + } + return false; + } + + // Check the base classes. + for (CXXRecordDecl::base_class_iterator Base1 = D1CXX->bases_begin(), + BaseEnd1 = D1CXX->bases_end(), + Base2 = D2CXX->bases_begin(); + Base1 != BaseEnd1; ++Base1, ++Base2) { + if (!IsStructurallyEquivalent(Context, Base1->getType(), + Base2->getType())) { + if (Context.Complain) { + Context.Diag2(D2->getLocation(), + diag::warn_odr_tag_type_inconsistent) + << Context.ToCtx.getTypeDeclType(D2); + Context.Diag2(Base2->getLocStart(), diag::note_odr_base) + << Base2->getType() << Base2->getSourceRange(); + Context.Diag1(Base1->getLocStart(), diag::note_odr_base) + << Base1->getType() << Base1->getSourceRange(); + } + return false; + } + + // Check virtual vs. non-virtual inheritance mismatch. + if (Base1->isVirtual() != Base2->isVirtual()) { + if (Context.Complain) { + Context.Diag2(D2->getLocation(), + diag::warn_odr_tag_type_inconsistent) + << Context.ToCtx.getTypeDeclType(D2); + Context.Diag2(Base2->getLocStart(), diag::note_odr_virtual_base) + << Base2->isVirtual() << Base2->getSourceRange(); + Context.Diag1(Base1->getLocStart(), diag::note_odr_base) + << Base1->isVirtual() << Base1->getSourceRange(); + } + return false; + } + } + } else if (D1CXX->getNumBases() > 0) { + if (Context.Complain) { + Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.ToCtx.getTypeDeclType(D2); + const CXXBaseSpecifier *Base1 = D1CXX->bases_begin(); + Context.Diag1(Base1->getLocStart(), diag::note_odr_base) + << Base1->getType() << Base1->getSourceRange(); + Context.Diag2(D2->getLocation(), diag::note_odr_missing_base); + } + return false; + } + } + + // Check the fields for consistency. + RecordDecl::field_iterator Field2 = D2->field_begin(), + Field2End = D2->field_end(); + for (RecordDecl::field_iterator Field1 = D1->field_begin(), + Field1End = D1->field_end(); + Field1 != Field1End; ++Field1, ++Field2) { + if (Field2 == Field2End) { + if (Context.Complain) { + Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.ToCtx.getTypeDeclType(D2); + Context.Diag1(Field1->getLocation(), diag::note_odr_field) + << Field1->getDeclName() << Field1->getType(); + Context.Diag2(D2->getLocation(), diag::note_odr_missing_field); + } + return false; + } + + if (!IsStructurallyEquivalent(Context, *Field1, *Field2)) + return false; + } + + if (Field2 != Field2End) { + if (Context.Complain) { + Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.ToCtx.getTypeDeclType(D2); + Context.Diag2(Field2->getLocation(), diag::note_odr_field) + << Field2->getDeclName() << Field2->getType(); + Context.Diag1(D1->getLocation(), diag::note_odr_missing_field); + } + return false; + } + + return true; +} + +/// Determine structural equivalence of two enums. +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + EnumDecl *D1, EnumDecl *D2) { + EnumDecl::enumerator_iterator EC2 = D2->enumerator_begin(), + EC2End = D2->enumerator_end(); + for (EnumDecl::enumerator_iterator EC1 = D1->enumerator_begin(), + EC1End = D1->enumerator_end(); + EC1 != EC1End; ++EC1, ++EC2) { + if (EC2 == EC2End) { + if (Context.Complain) { + Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.ToCtx.getTypeDeclType(D2); + Context.Diag1(EC1->getLocation(), diag::note_odr_enumerator) + << EC1->getDeclName() << EC1->getInitVal().toString(10); + Context.Diag2(D2->getLocation(), diag::note_odr_missing_enumerator); + } + return false; + } + + llvm::APSInt Val1 = EC1->getInitVal(); + llvm::APSInt Val2 = EC2->getInitVal(); + if (!llvm::APSInt::isSameValue(Val1, Val2) || + !IsStructurallyEquivalent(EC1->getIdentifier(), EC2->getIdentifier())) { + if (Context.Complain) { + Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.ToCtx.getTypeDeclType(D2); + Context.Diag2(EC2->getLocation(), diag::note_odr_enumerator) + << EC2->getDeclName() << EC2->getInitVal().toString(10); + Context.Diag1(EC1->getLocation(), diag::note_odr_enumerator) + << EC1->getDeclName() << EC1->getInitVal().toString(10); + } + return false; + } + } + + if (EC2 != EC2End) { + if (Context.Complain) { + Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) + << Context.ToCtx.getTypeDeclType(D2); + Context.Diag2(EC2->getLocation(), diag::note_odr_enumerator) + << EC2->getDeclName() << EC2->getInitVal().toString(10); + Context.Diag1(D1->getLocation(), diag::note_odr_missing_enumerator); + } + return false; + } + + return true; +} + +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + TemplateParameterList *Params1, + TemplateParameterList *Params2) { + if (Params1->size() != Params2->size()) { + if (Context.Complain) { + Context.Diag2(Params2->getTemplateLoc(), + diag::err_odr_different_num_template_parameters) + << Params1->size() << Params2->size(); + Context.Diag1(Params1->getTemplateLoc(), + diag::note_odr_template_parameter_list); + } + return false; + } + + for (unsigned I = 0, N = Params1->size(); I != N; ++I) { + if (Params1->getParam(I)->getKind() != Params2->getParam(I)->getKind()) { + if (Context.Complain) { + Context.Diag2(Params2->getParam(I)->getLocation(), + diag::err_odr_different_template_parameter_kind); + Context.Diag1(Params1->getParam(I)->getLocation(), + diag::note_odr_template_parameter_here); + } + return false; + } + + if (!Context.IsStructurallyEquivalent(Params1->getParam(I), + Params2->getParam(I))) { + + return false; + } + } + + return true; +} + +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + TemplateTypeParmDecl *D1, + TemplateTypeParmDecl *D2) { + if (D1->isParameterPack() != D2->isParameterPack()) { + if (Context.Complain) { + Context.Diag2(D2->getLocation(), diag::err_odr_parameter_pack_non_pack) + << D2->isParameterPack(); + Context.Diag1(D1->getLocation(), diag::note_odr_parameter_pack_non_pack) + << D1->isParameterPack(); + } + return false; + } + + return true; +} + +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + NonTypeTemplateParmDecl *D1, + NonTypeTemplateParmDecl *D2) { + if (D1->isParameterPack() != D2->isParameterPack()) { + if (Context.Complain) { + Context.Diag2(D2->getLocation(), diag::err_odr_parameter_pack_non_pack) + << D2->isParameterPack(); + Context.Diag1(D1->getLocation(), diag::note_odr_parameter_pack_non_pack) + << D1->isParameterPack(); + } + return false; + } + + // Check types. + if (!Context.IsStructurallyEquivalent(D1->getType(), D2->getType())) { + if (Context.Complain) { + Context.Diag2(D2->getLocation(), + diag::err_odr_non_type_parameter_type_inconsistent) + << D2->getType() << D1->getType(); + Context.Diag1(D1->getLocation(), diag::note_odr_value_here) + << D1->getType(); + } + return false; + } + + return true; +} + +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + TemplateTemplateParmDecl *D1, + TemplateTemplateParmDecl *D2) { + if (D1->isParameterPack() != D2->isParameterPack()) { + if (Context.Complain) { + Context.Diag2(D2->getLocation(), diag::err_odr_parameter_pack_non_pack) + << D2->isParameterPack(); + Context.Diag1(D1->getLocation(), diag::note_odr_parameter_pack_non_pack) + << D1->isParameterPack(); + } + return false; + } + + // Check template parameter lists. + return IsStructurallyEquivalent(Context, D1->getTemplateParameters(), + D2->getTemplateParameters()); +} + +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + ClassTemplateDecl *D1, + ClassTemplateDecl *D2) { + // Check template parameters. + if (!IsStructurallyEquivalent(Context, D1->getTemplateParameters(), + D2->getTemplateParameters())) + return false; + + // Check the templated declaration. + return Context.IsStructurallyEquivalent(D1->getTemplatedDecl(), + D2->getTemplatedDecl()); +} + +/// Determine structural equivalence of two declarations. +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + Decl *D1, Decl *D2) { + // FIXME: Check for known structural equivalences via a callback of some sort. + + // Check whether we already know that these two declarations are not + // structurally equivalent. + if (Context.NonEquivalentDecls.count( + std::make_pair(D1->getCanonicalDecl(), D2->getCanonicalDecl()))) + return false; + + // Determine whether we've already produced a tentative equivalence for D1. + Decl *&EquivToD1 = Context.TentativeEquivalences[D1->getCanonicalDecl()]; + if (EquivToD1) + return EquivToD1 == D2->getCanonicalDecl(); + + // Produce a tentative equivalence D1 <-> D2, which will be checked later. + EquivToD1 = D2->getCanonicalDecl(); + Context.DeclsToCheck.push_back(D1->getCanonicalDecl()); + return true; +} +} // namespace + +namespace clang { + +DiagnosticBuilder StructuralEquivalenceContext::Diag1(SourceLocation Loc, + unsigned DiagID) { + assert(Complain && "Not allowed to complain"); + if (LastDiagFromC2) + FromCtx.getDiagnostics().notePriorDiagnosticFrom(ToCtx.getDiagnostics()); + LastDiagFromC2 = false; + return FromCtx.getDiagnostics().Report(Loc, DiagID); +} + +DiagnosticBuilder StructuralEquivalenceContext::Diag2(SourceLocation Loc, + unsigned DiagID) { + assert(Complain && "Not allowed to complain"); + if (!LastDiagFromC2) + ToCtx.getDiagnostics().notePriorDiagnosticFrom(FromCtx.getDiagnostics()); + LastDiagFromC2 = true; + return ToCtx.getDiagnostics().Report(Loc, DiagID); +} + +Optional +StructuralEquivalenceContext::findUntaggedStructOrUnionIndex(RecordDecl *Anon) { + ASTContext &Context = Anon->getASTContext(); + QualType AnonTy = Context.getRecordType(Anon); + + RecordDecl *Owner = dyn_cast(Anon->getDeclContext()); + if (!Owner) + return None; + + unsigned Index = 0; + for (const auto *D : Owner->noload_decls()) { + const auto *F = dyn_cast(D); + if (!F) + continue; + + if (F->isAnonymousStructOrUnion()) { + if (Context.hasSameType(F->getType(), AnonTy)) + break; + ++Index; + continue; + } + + // If the field looks like this: + // struct { ... } A; + QualType FieldType = F->getType(); + if (const auto *RecType = dyn_cast(FieldType)) { + const RecordDecl *RecDecl = RecType->getDecl(); + if (RecDecl->getDeclContext() == Owner && !RecDecl->getIdentifier()) { + if (Context.hasSameType(FieldType, AnonTy)) + break; + ++Index; + continue; + } + } + } + + return Index; +} + +bool StructuralEquivalenceContext::IsStructurallyEquivalent(Decl *D1, + Decl *D2) { + if (!::IsStructurallyEquivalent(*this, D1, D2)) + return false; + + return !Finish(); +} + +bool StructuralEquivalenceContext::IsStructurallyEquivalent(QualType T1, + QualType T2) { + if (!::IsStructurallyEquivalent(*this, T1, T2)) + return false; + + return !Finish(); +} + +bool StructuralEquivalenceContext::Finish() { + while (!DeclsToCheck.empty()) { + // Check the next declaration. + Decl *D1 = DeclsToCheck.front(); + DeclsToCheck.pop_front(); + + Decl *D2 = TentativeEquivalences[D1]; + assert(D2 && "Unrecorded tentative equivalence?"); + + bool Equivalent = true; + + // FIXME: Switch on all declaration kinds. For now, we're just going to + // check the obvious ones. + if (RecordDecl *Record1 = dyn_cast(D1)) { + if (RecordDecl *Record2 = dyn_cast(D2)) { + // Check for equivalent structure names. + IdentifierInfo *Name1 = Record1->getIdentifier(); + if (!Name1 && Record1->getTypedefNameForAnonDecl()) + Name1 = Record1->getTypedefNameForAnonDecl()->getIdentifier(); + IdentifierInfo *Name2 = Record2->getIdentifier(); + if (!Name2 && Record2->getTypedefNameForAnonDecl()) + Name2 = Record2->getTypedefNameForAnonDecl()->getIdentifier(); + if (!::IsStructurallyEquivalent(Name1, Name2) || + !::IsStructurallyEquivalent(*this, Record1, Record2)) + Equivalent = false; + } else { + // Record/non-record mismatch. + Equivalent = false; + } + } else if (EnumDecl *Enum1 = dyn_cast(D1)) { + if (EnumDecl *Enum2 = dyn_cast(D2)) { + // Check for equivalent enum names. + IdentifierInfo *Name1 = Enum1->getIdentifier(); + if (!Name1 && Enum1->getTypedefNameForAnonDecl()) + Name1 = Enum1->getTypedefNameForAnonDecl()->getIdentifier(); + IdentifierInfo *Name2 = Enum2->getIdentifier(); + if (!Name2 && Enum2->getTypedefNameForAnonDecl()) + Name2 = Enum2->getTypedefNameForAnonDecl()->getIdentifier(); + if (!::IsStructurallyEquivalent(Name1, Name2) || + !::IsStructurallyEquivalent(*this, Enum1, Enum2)) + Equivalent = false; + } else { + // Enum/non-enum mismatch + Equivalent = false; + } + } else if (TypedefNameDecl *Typedef1 = dyn_cast(D1)) { + if (TypedefNameDecl *Typedef2 = dyn_cast(D2)) { + if (!::IsStructurallyEquivalent(Typedef1->getIdentifier(), + Typedef2->getIdentifier()) || + !::IsStructurallyEquivalent(*this, Typedef1->getUnderlyingType(), + Typedef2->getUnderlyingType())) + Equivalent = false; + } else { + // Typedef/non-typedef mismatch. + Equivalent = false; + } + } else if (ClassTemplateDecl *ClassTemplate1 = + dyn_cast(D1)) { + if (ClassTemplateDecl *ClassTemplate2 = dyn_cast(D2)) { + if (!::IsStructurallyEquivalent(ClassTemplate1->getIdentifier(), + ClassTemplate2->getIdentifier()) || + !::IsStructurallyEquivalent(*this, ClassTemplate1, ClassTemplate2)) + Equivalent = false; + } else { + // Class template/non-class-template mismatch. + Equivalent = false; + } + } else if (TemplateTypeParmDecl *TTP1 = + dyn_cast(D1)) { + if (TemplateTypeParmDecl *TTP2 = dyn_cast(D2)) { + if (!::IsStructurallyEquivalent(*this, TTP1, TTP2)) + Equivalent = false; + } else { + // Kind mismatch. + Equivalent = false; + } + } else if (NonTypeTemplateParmDecl *NTTP1 = + dyn_cast(D1)) { + if (NonTypeTemplateParmDecl *NTTP2 = + dyn_cast(D2)) { + if (!::IsStructurallyEquivalent(*this, NTTP1, NTTP2)) + Equivalent = false; + } else { + // Kind mismatch. + Equivalent = false; + } + } else if (TemplateTemplateParmDecl *TTP1 = + dyn_cast(D1)) { + if (TemplateTemplateParmDecl *TTP2 = + dyn_cast(D2)) { + if (!::IsStructurallyEquivalent(*this, TTP1, TTP2)) + Equivalent = false; + } else { + // Kind mismatch. + Equivalent = false; + } + } + + if (!Equivalent) { + // Note that these two declarations are not equivalent (and we already + // know about it). + NonEquivalentDecls.insert( + std::make_pair(D1->getCanonicalDecl(), D2->getCanonicalDecl())); + return true; + } + // FIXME: Check other declaration kinds! + } + + return false; +} +} // namespace clang Index: cfe/trunk/lib/AST/CMakeLists.txt =================================================================== --- cfe/trunk/lib/AST/CMakeLists.txt +++ cfe/trunk/lib/AST/CMakeLists.txt @@ -7,6 +7,7 @@ ASTDiagnostic.cpp ASTDumper.cpp ASTImporter.cpp + ASTStructuralEquivalence.cpp ASTTypeTraits.cpp AttrImpl.cpp CXXInheritance.cpp