balazske created this revision. Herald added subscribers: cfe-commits, gamesh411, Szelethus, dkrupp. Herald added a reviewer: martong. Herald added a reviewer: a.sidorin. Herald added a reviewer: shafik. Herald added a project: clang.
For functions there is a check to not duplicate the declaration if it is in a record (class). For function templates there was no similar check, if a template (in the same class) was imported multiple times the FunctionTemplateDecl was created multiple times with the same templated FunctionDecl. This can result in problems with the declaration chain. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D65203 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 @@ -2389,6 +2389,47 @@ functionDecl(hasName("f"), hasDescendant(declRefExpr())))))); } +TEST_P(ImportFunctionTemplates, ImportFunctionTemplateInRecordDeclTwice) { + auto Code = + R"( + class X { + template <class T> + void f(T t); + }; + )"; + Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc"); + auto *FromD1 = FirstDeclMatcher<FunctionTemplateDecl>().match( + FromTU1, functionTemplateDecl(hasName("f"))); + auto *ToD1 = Import(FromD1, Lang_CXX); + Decl *FromTU2 = getTuDecl(Code, Lang_CXX, "input2.cc"); + auto *FromD2 = FirstDeclMatcher<FunctionTemplateDecl>().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 <class T> + void f(T t); + }; + template <class T> + void X::f(T t) {}; + )"; + Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc"); + auto *FromD1 = FirstDeclMatcher<FunctionTemplateDecl>().match( + FromTU1, functionTemplateDecl(hasName("f"))); + auto *ToD1 = Import(FromD1, Lang_CXX); + Decl *FromTU2 = getTuDecl(Code, Lang_CXX, "input2.cc"); + auto *FromD2 = FirstDeclMatcher<FunctionTemplateDecl>().match( + FromTU2, functionTemplateDecl(hasName("f"))); + auto *ToD2 = Import(FromD2, Lang_CXX); + EXPECT_EQ(ToD1, ToD2); +} + struct ImportFriendFunctions : ImportFunctions {}; TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainProto) { Index: clang/lib/AST/ASTImporter.cpp =================================================================== --- clang/lib/AST/ASTImporter.cpp +++ clang/lib/AST/ASTImporter.cpp @@ -3066,9 +3066,19 @@ if (FoundByLookup) { if (isa<CXXMethodDecl>(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))
Index: clang/unittests/AST/ASTImporterTest.cpp =================================================================== --- clang/unittests/AST/ASTImporterTest.cpp +++ clang/unittests/AST/ASTImporterTest.cpp @@ -2389,6 +2389,47 @@ functionDecl(hasName("f"), hasDescendant(declRefExpr())))))); } +TEST_P(ImportFunctionTemplates, ImportFunctionTemplateInRecordDeclTwice) { + auto Code = + R"( + class X { + template <class T> + void f(T t); + }; + )"; + Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc"); + auto *FromD1 = FirstDeclMatcher<FunctionTemplateDecl>().match( + FromTU1, functionTemplateDecl(hasName("f"))); + auto *ToD1 = Import(FromD1, Lang_CXX); + Decl *FromTU2 = getTuDecl(Code, Lang_CXX, "input2.cc"); + auto *FromD2 = FirstDeclMatcher<FunctionTemplateDecl>().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 <class T> + void f(T t); + }; + template <class T> + void X::f(T t) {}; + )"; + Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc"); + auto *FromD1 = FirstDeclMatcher<FunctionTemplateDecl>().match( + FromTU1, functionTemplateDecl(hasName("f"))); + auto *ToD1 = Import(FromD1, Lang_CXX); + Decl *FromTU2 = getTuDecl(Code, Lang_CXX, "input2.cc"); + auto *FromD2 = FirstDeclMatcher<FunctionTemplateDecl>().match( + FromTU2, functionTemplateDecl(hasName("f"))); + auto *ToD2 = Import(FromD2, Lang_CXX); + EXPECT_EQ(ToD1, ToD2); +} + struct ImportFriendFunctions : ImportFunctions {}; TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainProto) { Index: clang/lib/AST/ASTImporter.cpp =================================================================== --- clang/lib/AST/ASTImporter.cpp +++ clang/lib/AST/ASTImporter.cpp @@ -3066,9 +3066,19 @@ if (FoundByLookup) { if (isa<CXXMethodDecl>(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))
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits