diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -3734,9 +3734,14 @@ // Connect the redecl chain. if (FoundByLookup) { - auto *Recent = const_cast( - FoundByLookup->getMostRecentDecl()); - ToFunction->setPreviousDecl(Recent); + if (auto *Ctor = dyn_cast(D); + Ctor && Ctor->isImplicit()) + DC->removeDecl(FoundByLookup); + else { + auto *Recent = + const_cast(FoundByLookup->getMostRecentDecl()); + ToFunction->setPreviousDecl(Recent); + } // FIXME Probably we should merge exception specifications. E.g. In the // "To" context the existing function may have exception specification with // noexcept-unevaluated, while the newly imported function may have an diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -3175,6 +3175,39 @@ testNoImportOf(cxxMethodDecl(hasName("f")), Code); } +TEST_P(ImportImplicitMethods, MergeImplicitMethodWithDefinition) { + Decl *From, *To; + std::tie(From, To) = getImportedDecl( + R"s( + struct A { + A() : m() {} + int m; + }; + + A foo() { A a; return a; } + A bar() { return {}; } + )s", + Lang_CXX17, + R"s( + struct A { + A() : m() {} + int m; + }; + A baz() { return {}; } + )s", + Lang_CXX17, "A"); + + MatchVerifier Verifier; + auto HasCopyCtor = has(cxxConstructorDecl(isCopyConstructor(), isImplicit())); + auto HasCtorInit = + hasAnyConstructorInitializer(cxxCtorInitializer(isMemberInitializer())); + auto HasMoveCtor = + has(cxxConstructorDecl(isMoveConstructor(), isImplicit(), HasCtorInit)); + auto Pattern = cxxRecordDecl(HasCopyCtor, HasMoveCtor); + ASSERT_TRUE(Verifier.match(From, Pattern)); + EXPECT_TRUE(Verifier.match(To, Pattern)); +} + TEST_P(ASTImporterOptionSpecificTestBase, ImportOfEquivalentRecord) { Decl *ToR1; {