This is an archive of the discontinued LLVM Phabricator instance.

[clang][ASTImporter] Repeated friend templates are partially imported
ClosedPublic

Authored by danix800 on Aug 11 2023, 1:08 AM.

Details

Summary
  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 (D157691).

  1. Import single friend node m into existing context would crash with an assertion failure:
./build/tools/clang/unittests/AST/ASTTests --gtest_filter="*ImportFriendClasses.ImportOfRepeatedFriendFunctionTemplateDecl*"
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.

Diff Detail

Event Timeline

danix800 created this revision.Aug 11 2023, 1:08 AM
Herald added a reviewer: shafik. · View Herald Transcript
Herald added a project: Restricted Project. · View Herald Transcript
danix800 requested review of this revision.Aug 11 2023, 1:08 AM
danix800 edited the summary of this revision. (Show Details)Aug 11 2023, 1:10 AM

By FromContext dump it's easy to observe that there're two different FunctionTemplateDecl (0x5590cff48048 and 0x5590cff481f8):

|-FriendDecl 0x5590cff48048 <line:4:11, col:44> col:42
| `-FunctionTemplateDecl 0x5590cff47f80 parent 0x5590cfefeda8 <col:11, col:44> col:42 m
|   |-...
`-FriendDecl 0x5590cff48260 <line:5:11, col:44> col:42
  `-FunctionTemplateDecl 0x5590cff481f8 parent 0x5590cfefeda8 <col:11, col:44> col:42 m
    |-...
danix800 edited the summary of this revision. (Show Details)Aug 11 2023, 2:15 AM
danix800 updated this revision to Diff 549376.Aug 11 2023, 7:02 AM

Apply git-clang-format.

danix800 updated this revision to Diff 549436.Aug 11 2023, 9:21 AM

Turn off Complain mode on IsEquivalentFriend checking.

balazske added inline comments.Aug 22 2023, 6:31 AM
clang/lib/AST/ASTImporter.cpp
4072

According to the coding rules auto should not be used here.

4079

This is a comparison in the same AST context ("From" side). Importer.getNonEquivalentDecls() returns a cache that is used at compares between "From" and "To" context. This is not valid for this use, you can simply pass an empty map instead, or add a new member that is used (only) here. getStructuralEquivalenceKind(Importer) is not needed for this compare, it can be always "Default".

4089

auto is not good here too.

4114

auto should not be used here, this loop could be replaced by some generic "algorithm" function call (llvm::copy_if).

danix800 added inline comments.Aug 22 2023, 8:04 AM
clang/lib/AST/ASTImporter.cpp
4114

There's no trivial predicate so the result code might be:

llvm::copy_if(RD->friends(), std::back_inserter(ImportedEquivalentFriends),
              [&](FriendDecl *ImportedFriend) {
                return IsEquivalentFriend(Importer, D, ImportedFriend);
              });

which is not that 'intuitive' as the for-range version.

I'll pertain to the original one with auto replaced with explicit type. Thanks!

danix800 updated this revision to Diff 552372.Aug 22 2023, 8:11 AM
  1. Rebase;
  2. Replace auto with explicit type;
  3. Use local var without interrupting Importer state.
danix800 marked 4 inline comments as done.Aug 22 2023, 8:12 AM
balazske added inline comments.Aug 23 2023, 2:11 AM
clang/lib/AST/ASTImporter.cpp
4087

This can be const?

4105

It is better if this is const.

4130–4131

auto should be replaced here

danix800 updated this revision to Diff 552678.Aug 23 2023, 5:37 AM

Add const qualifier & replace auto with explicit type.

danix800 added inline comments.Aug 23 2023, 5:39 AM
clang/lib/AST/ASTImporter.cpp
4105

As API requires this decl cannot be const.

balazske accepted this revision.Aug 23 2023, 5:59 AM
This revision is now accepted and ready to land.Aug 23 2023, 5:59 AM
This revision was landed with ongoing or failed builds.Aug 23 2023, 6:13 PM
This revision was automatically updated to reflect the committed changes.