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 @@ -3066,9 +3066,19 @@ if (FoundByLookup) { if (isa(FoundByLookup)) { if (D->getLexicalDeclContext() == D->getDeclContext()) { - if (!D->doesThisDeclarationHaveABody()) + if (!D->doesThisDeclarationHaveABody()) { + if (FunctionTemplateDecl *DescribedD = + D->getDescribedFunctionTemplate()) { + // Handle a "templated" function together with its described + // template. This avoids need for a similar check at import of the + // described template. + assert(FoundByLookup->getDescribedFunctionTemplate() && + "Templated function mapped to non-templated?"); + Importer.MapImported(DescribedD, + FoundByLookup->getDescribedFunctionTemplate()); + } return Importer.MapImported(D, FoundByLookup); - else { + } else { // Let's continue and build up the redecl chain in this case. // FIXME Merge the functions into one decl. } @@ -5550,14 +5560,14 @@ } } - auto ParamsOrErr = import(D->getTemplateParameters()); - if (!ParamsOrErr) - return ParamsOrErr.takeError(); - FunctionDecl *TemplatedFD; if (Error Err = importInto(TemplatedFD, D->getTemplatedDecl())) return std::move(Err); + auto ParamsOrErr = import(D->getTemplateParameters()); + if (!ParamsOrErr) + return ParamsOrErr.takeError(); + FunctionTemplateDecl *ToFunc; if (GetImportedOrCreateDecl(ToFunc, D, Importer.getToContext(), DC, Loc, Name, *ParamsOrErr, TemplatedFD)) 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 @@ -2389,6 +2389,47 @@ functionDecl(hasName("f"), hasDescendant(declRefExpr())))))); } +TEST_P(ImportFunctionTemplates, ImportFunctionTemplateInRecordDeclTwice) { + auto Code = + R"( + class X { + template + void f(T t); + }; + )"; + Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc"); + auto *FromD1 = FirstDeclMatcher().match( + FromTU1, functionTemplateDecl(hasName("f"))); + auto *ToD1 = Import(FromD1, Lang_CXX); + Decl *FromTU2 = getTuDecl(Code, Lang_CXX, "input2.cc"); + auto *FromD2 = FirstDeclMatcher().match( + FromTU2, functionTemplateDecl(hasName("f"))); + auto *ToD2 = Import(FromD2, Lang_CXX); + EXPECT_EQ(ToD1, ToD2); +} + +TEST_P(ImportFunctionTemplates, + ImportFunctionTemplateWithDefInRecordDeclTwice) { + auto Code = + R"( + class X { + template + void f(T t); + }; + template + void X::f(T t) {}; + )"; + Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc"); + auto *FromD1 = FirstDeclMatcher().match( + FromTU1, functionTemplateDecl(hasName("f"))); + auto *ToD1 = Import(FromD1, Lang_CXX); + Decl *FromTU2 = getTuDecl(Code, Lang_CXX, "input2.cc"); + auto *FromD2 = FirstDeclMatcher().match( + FromTU2, functionTemplateDecl(hasName("f"))); + auto *ToD2 = Import(FromD2, Lang_CXX); + EXPECT_EQ(ToD1, ToD2); +} + struct ImportFriendFunctions : ImportFunctions {}; TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainProto) {