Index: include/clang/AST/ASTStructuralEquivalence.h =================================================================== --- include/clang/AST/ASTStructuralEquivalence.h +++ include/clang/AST/ASTStructuralEquivalence.h @@ -114,8 +114,19 @@ private: /// Finish checking all of the structural equivalences. /// - /// \returns true if an error occurred, false otherwise. + /// \returns true if the equivalence check failed (non-equivalence detected), + /// false if equivalence was detected. bool Finish(); + + /// Check for common properties at Finish. + /// \returns true if D1 and D2 may be equivalent, + /// false if they are for sure not. + bool CheckCommonEquivalence(Decl *D1, Decl *D2); + + /// Check for class dependent properties at Finish. + /// \returns true if D1 and D2 may be equivalent, + /// false if they are for sure not. + bool CheckKindSpecificEquivalence(Decl *D1, Decl *D2); }; } // namespace clang Index: lib/AST/ASTStructuralEquivalence.cpp =================================================================== --- lib/AST/ASTStructuralEquivalence.cpp +++ lib/AST/ASTStructuralEquivalence.cpp @@ -1023,7 +1023,7 @@ return true; // If any of the records has external storage and we do a minimal check (or - // AST import) we assmue they are equivalent. (If we didn't have this + // AST import) we assume they are equivalent. (If we didn't have this // assumption then `RecordDecl::LoadFieldsFromExternalStorage` could trigger // another AST import which in turn would call the structural equivalency // check again and finally we'd have an improper result.) @@ -1497,6 +1497,141 @@ return !Finish(); } +bool StructuralEquivalenceContext::CheckCommonEquivalence(Decl *D1, Decl *D2) { + // Check for equivalent described template. + TemplateDecl *Template1 = D1->getDescribedTemplate(); + TemplateDecl *Template2 = D2->getDescribedTemplate(); + if ((Template1 != nullptr) != (Template2 != nullptr)) + return false; + if (Template1 && !IsStructurallyEquivalent(*this, Template1, Template2)) + return false; + + // FIXME: Move check for identifier names into this function. + + return true; +} + +bool StructuralEquivalenceContext::CheckKindSpecificEquivalence( + Decl *D1, Decl *D2) { + // FIXME: Switch on all declaration kinds. For now, we're just going to + // check the obvious ones. + if (auto *Record1 = dyn_cast(D1)) { + if (auto *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)) + return false; + } else { + // Record/non-record mismatch. + return false; + } + } else if (auto *Enum1 = dyn_cast(D1)) { + if (auto *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)) + return false; + } else { + // Enum/non-enum mismatch + return false; + } + } else if (const auto *Typedef1 = dyn_cast(D1)) { + if (const auto *Typedef2 = dyn_cast(D2)) { + if (!::IsStructurallyEquivalent(Typedef1->getIdentifier(), + Typedef2->getIdentifier()) || + !::IsStructurallyEquivalent(*this, Typedef1->getUnderlyingType(), + Typedef2->getUnderlyingType())) + return false; + } else { + // Typedef/non-typedef mismatch. + return false; + } + } else if (auto *ClassTemplate1 = dyn_cast(D1)) { + if (auto *ClassTemplate2 = dyn_cast(D2)) { + if (!::IsStructurallyEquivalent(*this, ClassTemplate1, + ClassTemplate2)) + return false; + } else { + // Class template/non-class-template mismatch. + return false; + } + } else if (auto *FunctionTemplate1 = dyn_cast(D1)) { + if (auto *FunctionTemplate2 = dyn_cast(D2)) { + if (!::IsStructurallyEquivalent(*this, FunctionTemplate1, + FunctionTemplate2)) + return false; + } else { + // Class template/non-class-template mismatch. + return false; + } + } else if (auto *TTP1 = dyn_cast(D1)) { + if (auto *TTP2 = dyn_cast(D2)) { + if (!::IsStructurallyEquivalent(*this, TTP1, TTP2)) + return false; + } else { + // Kind mismatch. + return false; + } + } else if (auto *NTTP1 = dyn_cast(D1)) { + if (auto *NTTP2 = dyn_cast(D2)) { + if (!::IsStructurallyEquivalent(*this, NTTP1, NTTP2)) + return false; + } else { + // Kind mismatch. + return false; + } + } else if (auto *TTP1 = dyn_cast(D1)) { + if (auto *TTP2 = dyn_cast(D2)) { + if (!::IsStructurallyEquivalent(*this, TTP1, TTP2)) + return false; + } else { + // Kind mismatch. + return false; + } + } else if (auto *MD1 = dyn_cast(D1)) { + if (auto *MD2 = dyn_cast(D2)) { + if (!::IsStructurallyEquivalent(*this, MD1, MD2)) + return false; + } else { + // Kind mismatch. + return false; + } + } else if (FunctionDecl *FD1 = dyn_cast(D1)) { + if (FunctionDecl *FD2 = dyn_cast(D2)) { + if (!::IsStructurallyEquivalent(FD1->getIdentifier(), + FD2->getIdentifier())) + return false; + if (!::IsStructurallyEquivalent(*this, FD1, FD2)) + return false; + } else { + // Kind mismatch. + return false; + } + } else if (FriendDecl *FrD1 = dyn_cast(D1)) { + if (FriendDecl *FrD2 = dyn_cast(D2)) { + if (!::IsStructurallyEquivalent(*this, FrD1, FrD2)) + return false; + } else { + // Kind mismatch. + return false; + } + } + + return true; +} + bool StructuralEquivalenceContext::Finish() { while (!DeclsToCheck.empty()) { // Check the next declaration. @@ -1506,123 +1641,8 @@ 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 (auto *Record1 = dyn_cast(D1)) { - if (auto *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 (auto *Enum1 = dyn_cast(D1)) { - if (auto *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 (const auto *Typedef1 = dyn_cast(D1)) { - if (const auto *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 (auto *ClassTemplate1 = dyn_cast(D1)) { - if (auto *ClassTemplate2 = dyn_cast(D2)) { - if (!::IsStructurallyEquivalent(*this, ClassTemplate1, - ClassTemplate2)) - Equivalent = false; - } else { - // Class template/non-class-template mismatch. - Equivalent = false; - } - } else if (auto *FunctionTemplate1 = dyn_cast(D1)) { - if (auto *FunctionTemplate2 = dyn_cast(D2)) { - if (!::IsStructurallyEquivalent(*this, FunctionTemplate1, - FunctionTemplate2)) - Equivalent = false; - } else { - // Class template/non-class-template mismatch. - Equivalent = false; - } - } else if (auto *TTP1 = dyn_cast(D1)) { - if (auto *TTP2 = dyn_cast(D2)) { - if (!::IsStructurallyEquivalent(*this, TTP1, TTP2)) - Equivalent = false; - } else { - // Kind mismatch. - Equivalent = false; - } - } else if (auto *NTTP1 = dyn_cast(D1)) { - if (auto *NTTP2 = dyn_cast(D2)) { - if (!::IsStructurallyEquivalent(*this, NTTP1, NTTP2)) - Equivalent = false; - } else { - // Kind mismatch. - Equivalent = false; - } - } else if (auto *TTP1 = dyn_cast(D1)) { - if (auto *TTP2 = dyn_cast(D2)) { - if (!::IsStructurallyEquivalent(*this, TTP1, TTP2)) - Equivalent = false; - } else { - // Kind mismatch. - Equivalent = false; - } - } else if (auto *MD1 = dyn_cast(D1)) { - if (auto *MD2 = dyn_cast(D2)) { - if (!::IsStructurallyEquivalent(*this, MD1, MD2)) - Equivalent = false; - } else { - // Kind mismatch. - Equivalent = false; - } - } else if (FunctionDecl *FD1 = dyn_cast(D1)) { - if (FunctionDecl *FD2 = dyn_cast(D2)) { - if (!::IsStructurallyEquivalent(FD1->getIdentifier(), - FD2->getIdentifier())) - Equivalent = false; - if (!::IsStructurallyEquivalent(*this, FD1, FD2)) - Equivalent = false; - } else { - // Kind mismatch. - Equivalent = false; - } - } else if (FriendDecl *FrD1 = dyn_cast(D1)) { - if (FriendDecl *FrD2 = dyn_cast(D2)) { - if (!::IsStructurallyEquivalent(*this, FrD1, FrD2)) - Equivalent = false; - } else { - // Kind mismatch. - Equivalent = false; - } - } + bool Equivalent = + CheckCommonEquivalence(D1, D2) && CheckKindSpecificEquivalence(D1, D2); if (!Equivalent) { // Note that these two declarations are not equivalent (and we already @@ -1631,7 +1651,6 @@ std::make_pair(D1->getCanonicalDecl(), D2->getCanonicalDecl())); return true; } - // FIXME: Check other declaration kinds! } return false; Index: unittests/AST/StructuralEquivalenceTest.cpp =================================================================== --- unittests/AST/StructuralEquivalenceTest.cpp +++ unittests/AST/StructuralEquivalenceTest.cpp @@ -78,11 +78,18 @@ } bool testStructuralMatch(Decl *D0, Decl *D1) { - llvm::DenseSet> NonEquivalentDecls; - StructuralEquivalenceContext Ctx( - D0->getASTContext(), D1->getASTContext(), NonEquivalentDecls, - StructuralEquivalenceKind::Default, false, false); - return Ctx.IsEquivalent(D0, D1); + llvm::DenseSet> NonEquivalentDecls01; + llvm::DenseSet> NonEquivalentDecls10; + StructuralEquivalenceContext Ctx01( + D0->getASTContext(), D1->getASTContext(), + NonEquivalentDecls01, StructuralEquivalenceKind::Default, false, false); + StructuralEquivalenceContext Ctx10( + D1->getASTContext(), D0->getASTContext(), + NonEquivalentDecls10, StructuralEquivalenceKind::Default, false, false); + bool Eq01 = Ctx01.IsEquivalent(D0, D1); + bool Eq10 = Ctx10.IsEquivalent(D1, D0); + EXPECT_EQ(Eq01, Eq10); + return Eq01; } bool testStructuralMatch(std::tuple t) { @@ -215,6 +222,14 @@ struct StructuralEquivalenceFunctionTest : StructuralEquivalenceTest { }; +TEST_F(StructuralEquivalenceFunctionTest, TemplateVsNonTemplate) { + auto t = makeNamedDecls( + "void foo();", + "template void foo();", + Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} + TEST_F(StructuralEquivalenceFunctionTest, ParamConstWithRef) { auto t = makeNamedDecls("void foo(int&);", "void foo(const int&);", Lang_CXX); @@ -618,6 +633,14 @@ EXPECT_FALSE(testStructuralMatch(R0, R1)); } +TEST_F(StructuralEquivalenceRecordTest, TemplateVsNonTemplate) { + auto t = makeDecls( + "struct A { };", + "template struct A { };", + Lang_CXX, + cxxRecordDecl(hasName("A"))); + EXPECT_FALSE(testStructuralMatch(t)); +} TEST_F(StructuralEquivalenceTest, CompareSameDeclWithMultiple) { auto t = makeNamedDecls(