danix800 created this revision. danix800 added reviewers: balazske, aaron.ballman. danix800 added a project: clang. Herald added subscribers: pengfei, martong, kristof.beyls. Herald added a reviewer: a.sidorin. Herald added a reviewer: shafik. Herald added a project: All. danix800 requested review of this revision. Herald added a subscriber: cfe-commits.
1. Repeated friends of template class/function in template class are partially imported: template <class T> class Container { template <class U> friend void m(); template <class U> friend void m(); }; Only one `m` can be imported, `FromTu`: ClassTemplateDecl 0x5590cff47ae0 <input.cc:2:9, line:6:9> line:3:15 Container |-TemplateTypeParmDecl 0x5590cff47990 <line:2:19, col:25> col:25 class depth 0 index 0 T `-CXXRecordDecl 0x5590cff47a50 <line:3:9, line:6:9> line:3:15 class Container definition |-DefinitionData empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param | |-MoveConstructor exists simple trivial needs_implicit | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param | |-MoveAssignment exists simple trivial needs_implicit | `-Destructor simple irrelevant trivial needs_implicit |-CXXRecordDecl 0x5590cff47d30 <col:9, col:15> col:15 implicit class Container |-FriendDecl 0x5590cff48048 <line:4:11, col:44> col:42 | `-FunctionTemplateDecl 0x5590cff47f80 parent 0x5590cfefeda8 <col:11, col:44> col:42 m | |-TemplateTypeParmDecl 0x5590cff47dc0 <col:21, col:27> col:27 class depth 1 index 0 U | `-FunctionDecl 0x5590cff47ec8 parent 0x5590cfefeda8 <col:30, col:44> col:42 m 'void ()' `-FriendDecl 0x5590cff48260 <line:5:11, col:44> col:42 `-FunctionTemplateDecl 0x5590cff481f8 parent 0x5590cfefeda8 <col:11, col:44> col:42 m |-TemplateTypeParmDecl 0x5590cff48088 <col:21, col:27> col:27 class depth 1 index 0 U `-FunctionDecl 0x5590cff48140 parent 0x5590cfefeda8 <col:30, col:44> col:42 m 'void ()' `ToTu`: ClassTemplateDecl 0x5590cffe3ad8 <input.cc:2:9, line:6:9> line:3:15 Container |-TemplateTypeParmDecl 0x5590cffe3950 <line:2:19, col:25> col:25 class depth 0 index 0 T `-CXXRecordDecl 0x5590cffe3a10 <line:3:9, line:6:9> line:3:15 class Container definition |-DefinitionData empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param | |-MoveConstructor exists simple trivial needs_implicit | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param | |-MoveAssignment exists simple trivial needs_implicit | `-Destructor simple irrelevant trivial needs_implicit |-FunctionTemplateDecl 0x5590cffe3ef8 parent 0x5590cff9b128 <line:4:11, col:44> col:42 m // extranous node | |-TemplateTypeParmDecl 0x5590cffe3d20 <col:21, col:27> col:27 class depth 1 index 0 U | `-FunctionDecl 0x5590cffe3e28 parent 0x5590cff9b128 <col:30, col:44> col:42 m 'void ()' |-FriendDecl 0x5590cffe3f60 <col:11, col:44> col:42 | `-FunctionTemplateDecl 0x5590cffe3ef8 parent 0x5590cff9b128 <col:11, col:44> col:42 m | |-TemplateTypeParmDecl 0x5590cffe3d20 <col:21, col:27> col:27 class depth 1 index 0 U | `-FunctionDecl 0x5590cffe3e28 parent 0x5590cff9b128 <col:30, col:44> col:42 m 'void ()' `-CXXRecordDecl 0x5590cffe3fa0 <line:3:9, col:15> col:15 implicit class Container Also note that an extranous `FunctionTemplateDecl 0x5590cffe3ef8` is added into this `ClassTemplateDecl 0x5590cffe3ad8` lexical context, which renders it NOT identical to the original DeclContext, even after the second friend is successfully imported, this extra node would crash `clang-import-test`. Plan to fix it in another revision. 2. Import single friend node `m` into existing context would crash with an assertion failure: ./build/tools/clang/unittests/AST/ASTTests --gtest_filter="*ImportFriendClasses.ImportOfRepeatedFriendFunctionTemplateDecl*" |& sed 's/danis/xxxxx/' Note: Google Test filter = *ImportFriendClasses.ImportOfRepeatedFriendFunctionTemplateDecl* [==========] Running 4 tests from 1 test suite. [----------] Global test environment set-up. [----------] 4 tests from ParameterizedTests/ImportFriendClasses [ RUN ] ParameterizedTests/ImportFriendClasses.ImportOfRepeatedFriendFunctionTemplateDecl/0 ASTTests: /home/xxxxx/Sources/llvm-project-main/clang/lib/AST/ASTImporter.cpp:4139: clang::ExpectedDecl clang::ASTNodeImporter::VisitFriendDecl(clang::FriendDecl *): Assertion `ImportedEquivalentFriends.size() <= CountAndPosition.TotalCount && "Class with non-matching friends is imported, ODR check wrong?"' failed. #0 0x00007f1f53bf812a llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /home/xxxxx/Sources/llvm-project-main/llvm/lib/Support/Unix/Signals.inc:602:11 #1 0x00007f1f53bf82db PrintStackTraceSignalHandler(void*) /home/xxxxx/Sources/llvm-project-main/llvm/lib/Support/Unix/Signals.inc:675:1 #2 0x00007f1f53bf6846 llvm::sys::RunSignalHandlers() /home/xxxxx/Sources/llvm-project-main/llvm/lib/Support/Signals.cpp:104:5 #3 0x00007f1f53bf8af5 SignalHandler(int) /home/xxxxx/Sources/llvm-project-main/llvm/lib/Support/Unix/Signals.inc:413:1 #4 0x00007f1f5365afd0 (/lib/x86_64-linux-gnu/libc.so.6+0x3bfd0) #5 0x00007f1f536a9d3c __pthread_kill_implementation ./nptl/./nptl/pthread_kill.c:44:76 #6 0x00007f1f5365af32 raise ./signal/../sysdeps/posix/raise.c:27:6 #7 0x00007f1f53645472 abort ./stdlib/./stdlib/abort.c:81:7 #8 0x00007f1f53645395 _nl_load_domain ./intl/./intl/loadmsgcat.c:1177:9 #9 0x00007f1f53653e32 (/lib/x86_64-linux-gnu/libc.so.6+0x34e32) #10 0x00007f1f555c7179 clang::ASTNodeImporter::VisitFriendDecl(clang::FriendDecl*) /home/xxxxx/Sources/llvm-project-main/clang/lib/AST/ASTImporter.cpp:4140:7 #11 0x00007f1f5561f564 clang::declvisitor::Base<std::add_pointer, clang::ASTNodeImporter, llvm::Expected<clang::Decl*> >::Visit(clang::Decl*) /home/xxxxx/Sources/llvm-project-main/build/tools/clang/include/clang/AST/DeclNodes.inc:71:1 #12 0x00007f1f555eaf3d clang::ASTImporter::ImportImpl(clang::Decl*) /home/xxxxx/Sources/llvm-project-main/clang/lib/AST/ASTImporter.cpp:8768:19 #13 0x00007f1f555ce605 clang::ASTImporter::Import(clang::Decl*) /home/xxxxx/Sources/llvm-project-main/clang/lib/AST/ASTImporter.cpp:9167:8 #14 0x0000562aa520bd4a clang::ast_matchers::ASTImporterTestBase::TU::import(std::shared_ptr<clang::ASTImporterSharedState> const&, clang::ASTUnit*, clang::Decl*) /home/xxxxx/Sources/llvm-project-main/clang/unittests/AST/ASTImporterFixtures.cpp:83:12 #15 0x0000562aa520c9c9 clang::ast_matchers::ASTImporterTestBase::Import(clang::Decl*, clang::TestLanguage) /home/xxxxx/Sources/llvm-project-main/clang/unittests/AST/ASTImporterFixtures.cpp:199:9 #16 0x0000562aa531f023 clang::FriendDecl* clang::ast_matchers::ASTImporterTestBase::Import<clang::FriendDecl>(clang::FriendDecl*, clang::TestLanguage) /home/xxxxx/Sources/llvm-project-main/clang/unittests/AST/ASTImporterFixtures.h:185:32 #17 0x0000562aa52af6a2 clang::ast_matchers::ImportFriendClasses::testRepeatedFriendImport(char const*) /home/xxxxx/Sources/llvm-project-main/clang/unittests/AST/ASTImporterTest.cpp:4068:37 #18 0x0000562aa5255524 clang::ast_matchers::ImportFriendClasses_ImportOfRepeatedFriendFunctionTemplateDecl_Test::TestBody() /home/xxxxx/Sources/llvm-project-main/clang/unittests/AST/ASTImporterTest.cpp:4388:1 #19 0x00007f1f56ddb76b void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) /home/xxxxx/Sources/llvm-project-main/third-party/unittest/googletest/src/gtest.cc:2433:3 #20 0x00007f1f56dc0e07 void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) /home/xxxxx/Sources/llvm-project-main/third-party/unittest/googletest/src/gtest.cc:2488:5 #21 0x00007f1f56da9963 testing::Test::Run() /home/xxxxx/Sources/llvm-project-main/third-party/unittest/googletest/src/gtest.cc:2515:3 #22 0x00007f1f56daa1ba testing::TestInfo::Run() /home/xxxxx/Sources/llvm-project-main/third-party/unittest/googletest/src/gtest.cc:2687:12 #23 0x00007f1f56daa71b testing::TestSuite::Run() /home/xxxxx/Sources/llvm-project-main/third-party/unittest/googletest/src/gtest.cc:2815:44 #24 0x00007f1f56db2ef9 testing::internal::UnitTestImpl::RunAllTests() /home/xxxxx/Sources/llvm-project-main/third-party/unittest/googletest/src/gtest.cc:5337:24 #25 0x00007f1f56ddea3b bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /home/xxxxx/Sources/llvm-project-main/third-party/unittest/googletest/src/gtest.cc:2433:3 #26 0x00007f1f56dc3247 bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /home/xxxxx/Sources/llvm-project-main/third-party/unittest/googletest/src/gtest.cc:2488:5 #27 0x00007f1f56db2adf testing::UnitTest::Run() /home/xxxxx/Sources/llvm-project-main/third-party/unittest/googletest/src/gtest.cc:4925:10 #28 0x00007f1f578add71 RUN_ALL_TESTS() /home/xxxxx/Sources/llvm-project-main/third-party/unittest/googletest/include/gtest/gtest.h:2472:3 #29 0x00007f1f578adcb4 main /home/xxxxx/Sources/llvm-project-main/third-party/unittest/UnitTestMain/TestMain.cpp:55:3 #30 0x00007f1f536461ca __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:74:3 #31 0x00007f1f53646285 call_init ./csu/../csu/libc-start.c:128:20 #32 0x00007f1f53646285 __libc_start_main ./csu/../csu/libc-start.c:347:5 #33 0x0000562aa51aed31 _start (./build/tools/clang/unittests/AST/ASTTests+0x490d31) `FriendCountAndPosition` computation for `FromContext` by pointer comparison is not reliable in this case. As `ToContext` counts what's already imported, we could use `ASTStructuralEquivalence` which is more reliable. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D157684 Files: clang/lib/AST/ASTImporter.cpp clang/unittests/AST/ASTImporterTest.cpp
Index: clang/unittests/AST/ASTImporterTest.cpp =================================================================== --- clang/unittests/AST/ASTImporterTest.cpp +++ clang/unittests/AST/ASTImporterTest.cpp @@ -4054,6 +4054,24 @@ ->lookup(ToRecordOfFriend->getDeclName()) .empty()); } + + void testRepeatedFriendImport(const char *Code) { + Decl *ToTu = getToTuDecl(Code, Lang_CXX03); + Decl *FromTu = getTuDecl(Code, Lang_CXX03, "from.cc"); + + auto *ToFriend1 = FirstDeclMatcher<FriendDecl>().match(ToTu, friendDecl()); + auto *ToFriend2 = LastDeclMatcher<FriendDecl>().match(ToTu, friendDecl()); + auto *FromFriend1 = + FirstDeclMatcher<FriendDecl>().match(FromTu, friendDecl()); + auto *FromFriend2 = LastDeclMatcher<FriendDecl>().match(FromTu, friendDecl()); + + FriendDecl *ToImportedFriend1 = Import(FromFriend1, Lang_CXX03); + FriendDecl *ToImportedFriend2 = Import(FromFriend2, Lang_CXX03); + + EXPECT_NE(ToImportedFriend1, ToImportedFriend2); + EXPECT_EQ(ToFriend1, ToImportedFriend1); + EXPECT_EQ(ToFriend2, ToImportedFriend2); + } }; TEST_P(ImportFriendClasses, ImportOfFriendRecordDoesNotMergeDefinition) { @@ -4343,21 +4361,7 @@ friend class X; }; )"; - Decl *ToTu = getToTuDecl(Code, Lang_CXX03); - Decl *FromTu = getTuDecl(Code, Lang_CXX03, "from.cc"); - - auto *ToFriend1 = FirstDeclMatcher<FriendDecl>().match(ToTu, friendDecl()); - auto *ToFriend2 = LastDeclMatcher<FriendDecl>().match(ToTu, friendDecl()); - auto *FromFriend1 = - FirstDeclMatcher<FriendDecl>().match(FromTu, friendDecl()); - auto *FromFriend2 = LastDeclMatcher<FriendDecl>().match(FromTu, friendDecl()); - - FriendDecl *ToImportedFriend1 = Import(FromFriend1, Lang_CXX03); - FriendDecl *ToImportedFriend2 = Import(FromFriend2, Lang_CXX03); - - EXPECT_NE(ToImportedFriend1, ToImportedFriend2); - EXPECT_EQ(ToFriend1, ToImportedFriend1); - EXPECT_EQ(ToFriend2, ToImportedFriend2); + testRepeatedFriendImport(Code); } TEST_P(ImportFriendClasses, ImportOfRepeatedFriendDecl) { @@ -4368,21 +4372,31 @@ friend void f(); }; )"; - Decl *ToTu = getToTuDecl(Code, Lang_CXX03); - Decl *FromTu = getTuDecl(Code, Lang_CXX03, "from.cc"); - - auto *ToFriend1 = FirstDeclMatcher<FriendDecl>().match(ToTu, friendDecl()); - auto *ToFriend2 = LastDeclMatcher<FriendDecl>().match(ToTu, friendDecl()); - auto *FromFriend1 = - FirstDeclMatcher<FriendDecl>().match(FromTu, friendDecl()); - auto *FromFriend2 = LastDeclMatcher<FriendDecl>().match(FromTu, friendDecl()); + testRepeatedFriendImport(Code); +} - FriendDecl *ToImportedFriend1 = Import(FromFriend1, Lang_CXX03); - FriendDecl *ToImportedFriend2 = Import(FromFriend2, Lang_CXX03); +TEST_P(ImportFriendClasses, ImportOfRepeatedFriendFunctionTemplateDecl) { + const char *Code = + R"( + template <class T> + class Container { + template <class U> friend void m(); + template <class U> friend void m(); + }; + )"; + testRepeatedFriendImport(Code); +} - EXPECT_NE(ToImportedFriend1, ToImportedFriend2); - EXPECT_EQ(ToFriend1, ToImportedFriend1); - EXPECT_EQ(ToFriend2, ToImportedFriend2); +TEST_P(ImportFriendClasses, ImportOfRepeatedFriendClassTemplateDecl) { + const char *Code = + R"( + template <class T> + class Container { + template <class U> friend class X; + template <class U> friend class X; + }; + )"; + testRepeatedFriendImport(Code); } TEST_P(ASTImporterOptionSpecificTestBase, FriendFunInClassTemplate) { Index: clang/lib/AST/ASTImporter.cpp =================================================================== --- clang/lib/AST/ASTImporter.cpp +++ clang/lib/AST/ASTImporter.cpp @@ -4064,22 +4064,32 @@ unsigned int IndexOfDecl; }; -template <class T> -static FriendCountAndPosition getFriendCountAndPosition( - const FriendDecl *FD, - llvm::function_ref<T(const FriendDecl *)> GetCanTypeOrDecl) { +static bool IsEquivalentFriend(ASTImporter &Importer, FriendDecl *FD1, + FriendDecl *FD2) { + if ((!FD1->getFriendType()) != (!FD2->getFriendType())) + return false; + + if (auto *TSI = FD1->getFriendType()) + return Importer.IsStructurallyEquivalent( + TSI->getType(), FD2->getFriendType()->getType(), /*Complain=*/false); + + StructuralEquivalenceContext Ctx( + Importer.getFromContext(), Importer.getToContext(), + Importer.getNonEquivalentDecls(), getStructuralEquivalenceKind(Importer)); + return Ctx.IsEquivalent(FD1, FD2); +} + +static FriendCountAndPosition getFriendCountAndPosition(ASTImporter &Importer, + FriendDecl *FD) { unsigned int FriendCount = 0; std::optional<unsigned int> FriendPosition; const auto *RD = cast<CXXRecordDecl>(FD->getLexicalDeclContext()); - T TypeOrDecl = GetCanTypeOrDecl(FD); - - for (const FriendDecl *FoundFriend : RD->friends()) { + for (auto *FoundFriend : RD->friends()) { if (FoundFriend == FD) { FriendPosition = FriendCount; ++FriendCount; - } else if (!FoundFriend->getFriendDecl() == !FD->getFriendDecl() && - GetCanTypeOrDecl(FoundFriend) == TypeOrDecl) { + } else if (IsEquivalentFriend(Importer, FD, FoundFriend)) { ++FriendCount; } } @@ -4089,21 +4099,6 @@ return {FriendCount, *FriendPosition}; } -static FriendCountAndPosition getFriendCountAndPosition(const FriendDecl *FD) { - if (FD->getFriendType()) - return getFriendCountAndPosition<QualType>(FD, [](const FriendDecl *F) { - if (TypeSourceInfo *TSI = F->getFriendType()) - return TSI->getType().getCanonicalType(); - llvm_unreachable("Wrong friend object type."); - }); - else - return getFriendCountAndPosition<Decl *>(FD, [](const FriendDecl *F) { - if (Decl *D = F->getFriendDecl()) - return D->getCanonicalDecl(); - llvm_unreachable("Wrong friend object type."); - }); -} - ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) { // Import the major distinguishing characteristics of a declaration. DeclContext *DC, *LexicalDC; @@ -4114,26 +4109,13 @@ // FriendDecl is not a NamedDecl so we cannot use lookup. // We try to maintain order and count of redundant friend declarations. const auto *RD = cast<CXXRecordDecl>(DC); - FriendDecl *ImportedFriend = RD->getFirstFriend(); SmallVector<FriendDecl *, 2> ImportedEquivalentFriends; - - while (ImportedFriend) { - bool Match = false; - if (D->getFriendDecl() && ImportedFriend->getFriendDecl()) { - Match = - IsStructuralMatch(D->getFriendDecl(), ImportedFriend->getFriendDecl(), - /*Complain=*/false); - } else if (D->getFriendType() && ImportedFriend->getFriendType()) { - Match = Importer.IsStructurallyEquivalent( - D->getFriendType()->getType(), - ImportedFriend->getFriendType()->getType(), /*Complain=*/false); - } - if (Match) + for (auto *ImportedFriend : RD->friends()) + if (IsEquivalentFriend(Importer, D, ImportedFriend)) ImportedEquivalentFriends.push_back(ImportedFriend); - ImportedFriend = ImportedFriend->getNextFriend(); - } - FriendCountAndPosition CountAndPosition = getFriendCountAndPosition(D); + FriendCountAndPosition CountAndPosition = + getFriendCountAndPosition(Importer, D); assert(ImportedEquivalentFriends.size() <= CountAndPosition.TotalCount && "Class with non-matching friends is imported, ODR check wrong?");
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits