From 05e95d2dd74973dd5163b7d44828fac61e416452 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= <1.int32@gmail.com> Date: Thu, 17 Jun 2021 09:12:36 +0200 Subject: [PATCH] [clang][AST] Set correct DeclContext in ASTImporter lookup table for template params. Template parameters are created in ASTImporter with the translation unit as DeclContext. The DeclContext is later updated (by the create function of template classes). ASTImporterLookupTable was not updated after these changes of the DC. The patch adds update of the DeclContext in ASTImporterLookupTable. Reviewed By: martong Differential Revision: https://reviews.llvm.org/D103792 --- clang/lib/AST/ASTImporter.cpp | 43 ++++- clang/unittests/AST/ASTImporterTest.cpp | 239 ++++++++++++++++++++++++ 2 files changed, 281 insertions(+), 1 deletion(-) diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 0fd1401e9e9f..0d0cabc96556 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -322,6 +322,21 @@ namespace clang { } } + void updateLookupTableForTemplateParameters(TemplateParameterList &Params, + DeclContext *OldDC) { + ASTImporterLookupTable *LT = Importer.SharedState->getLookupTable(); + if (!LT) + return; + + for (NamedDecl *TP : Params) + LT->update(TP, OldDC); + } + + void updateLookupTableForTemplateParameters(TemplateParameterList &Params) { + updateLookupTableForTemplateParameters( + Params, Importer.getToContext().getTranslationUnitDecl()); + } + public: explicit ASTNodeImporter(ASTImporter &Importer) : Importer(Importer) {} @@ -2609,6 +2624,8 @@ ASTNodeImporter::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) { ToAlias->setAccess(D->getAccess()); ToAlias->setLexicalDeclContext(LexicalDC); LexicalDC->addDeclInternal(ToAlias); + if (DC != Importer.getToContext().getTranslationUnitDecl()) + updateLookupTableForTemplateParameters(*ToTemplateParameters); return ToAlias; } @@ -5511,6 +5528,7 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) { D2->setLexicalDeclContext(LexicalDC); addDeclToContexts(D, D2); + updateLookupTableForTemplateParameters(**TemplateParamsOrErr); if (FoundByLookup) { auto *Recent = @@ -5654,6 +5672,7 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateSpecializationDecl( // Add this partial specialization to the class template. ClassTemplate->AddPartialSpecialization(PartSpec2, InsertPos); + updateLookupTableForTemplateParameters(*ToTPList); } else { // Not a partial specialization. if (GetImportedOrCreateDecl( D2, D, Importer.getToContext(), D->getTagKind(), DC, @@ -5803,6 +5822,8 @@ ExpectedDecl ASTNodeImporter::VisitVarTemplateDecl(VarTemplateDecl *D) { ToVarTD->setAccess(D->getAccess()); ToVarTD->setLexicalDeclContext(LexicalDC); LexicalDC->addDeclInternal(ToVarTD); + if (DC != Importer.getToContext().getTranslationUnitDecl()) + updateLookupTableForTemplateParameters(**TemplateParamsOrErr); if (FoundByLookup) { auto *Recent = @@ -5928,6 +5949,9 @@ ExpectedDecl ASTNodeImporter::VisitVarTemplateSpecializationDecl( D2 = ToPartial; + // FIXME: Use this update if VarTemplatePartialSpecializationDecl is fixed + // to adopt template parameters. + // updateLookupTableForTemplateParameters(**ToTPListOrErr); } else { // Full specialization if (GetImportedOrCreateDecl(D2, D, Importer.getToContext(), DC, *BeginLocOrErr, *IdLocOrErr, VarTemplate, @@ -6016,14 +6040,30 @@ ASTNodeImporter::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) { auto ParamsOrErr = import(D->getTemplateParameters()); if (!ParamsOrErr) return ParamsOrErr.takeError(); + TemplateParameterList *Params = *ParamsOrErr; FunctionDecl *TemplatedFD; if (Error Err = importInto(TemplatedFD, D->getTemplatedDecl())) return std::move(Err); + // Template parameters of the ClassTemplateDecl and FunctionTemplateDecl are + // shared, if the FunctionTemplateDecl is a deduction guide for the class. + // At import the ClassTemplateDecl object is always created first (FIXME: is + // this really true?) because the dependency, then the FunctionTemplateDecl. + // The DeclContext of the template parameters is changed when the + // FunctionTemplateDecl is created, but was set already when the class + // template was created. So here it is not the TU (default value) any more. + // FIXME: The DeclContext of the parameters is now set finally to the + // CXXDeductionGuideDecl object that was imported later. This may not be the + // same that is in the original AST, specially if there are multiple deduction + // guides. + DeclContext *OldParamDC = nullptr; + if (Params->size() > 0) + OldParamDC = Params->getParam(0)->getDeclContext(); + FunctionTemplateDecl *ToFunc; if (GetImportedOrCreateDecl(ToFunc, D, Importer.getToContext(), DC, Loc, Name, - *ParamsOrErr, TemplatedFD)) + Params, TemplatedFD)) return ToFunc; TemplatedFD->setDescribedFunctionTemplate(ToFunc); @@ -6031,6 +6071,7 @@ ASTNodeImporter::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) { ToFunc->setAccess(D->getAccess()); ToFunc->setLexicalDeclContext(LexicalDC); LexicalDC->addDeclInternal(ToFunc); + updateLookupTableForTemplateParameters(*Params, OldParamDC); if (FoundByLookup) { auto *Recent = diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp index 040adec3d5d7..da4bce16d23b 100644 --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -4382,6 +4382,245 @@ TEST_P(ASTImporterOptionSpecificTestBase, ImportTemplateParameterLists) { EXPECT_EQ(ToD->getNumTemplateParameterLists(), 1u); } +const internal::VariadicDynCastAllOfMatcher + varTemplateDecl; + +const internal::VariadicDynCastAllOfMatcher< + Decl, VarTemplatePartialSpecializationDecl> + varTemplatePartialSpecializationDecl; + +TEST_P(ASTImporterOptionSpecificTestBase, + FunctionTemplateParameterDeclContext) { + constexpr auto Code = + R"( + template + void f() {}; + )"; + + Decl *FromTU = getTuDecl(Code, Lang_CXX11); + + auto *FromD = FirstDeclMatcher().match( + FromTU, functionTemplateDecl(hasName("f"))); + + ASSERT_EQ(FromD->getTemplateParameters()->getParam(0)->getDeclContext(), + FromD->getTemplatedDecl()); + + auto *ToD = Import(FromD, Lang_CXX11); + EXPECT_EQ(ToD->getTemplateParameters()->getParam(0)->getDeclContext(), + ToD->getTemplatedDecl()); + EXPECT_TRUE(SharedStatePtr->getLookupTable()->contains( + ToD->getTemplatedDecl(), ToD->getTemplateParameters()->getParam(0))); +} + +TEST_P(ASTImporterOptionSpecificTestBase, ClassTemplateParameterDeclContext) { + constexpr auto Code = + R"( + template + struct S {}; + template + struct S {}; + )"; + + Decl *FromTU = getTuDecl(Code, Lang_CXX11); + + auto *FromD = FirstDeclMatcher().match( + FromTU, classTemplateDecl(hasName("S"))); + auto *FromDPart = + FirstDeclMatcher().match( + FromTU, classTemplatePartialSpecializationDecl(hasName("S"))); + + ASSERT_EQ(FromD->getTemplateParameters()->getParam(0)->getDeclContext(), + FromD->getTemplatedDecl()); + ASSERT_EQ(FromDPart->getTemplateParameters()->getParam(0)->getDeclContext(), + FromDPart); + + auto *ToD = Import(FromD, Lang_CXX11); + auto *ToDPart = Import(FromDPart, Lang_CXX11); + + EXPECT_EQ(ToD->getTemplateParameters()->getParam(0)->getDeclContext(), + ToD->getTemplatedDecl()); + EXPECT_TRUE(SharedStatePtr->getLookupTable()->contains( + ToD->getTemplatedDecl(), ToD->getTemplateParameters()->getParam(0))); + + EXPECT_EQ(ToDPart->getTemplateParameters()->getParam(0)->getDeclContext(), + ToDPart); + EXPECT_TRUE(SharedStatePtr->getLookupTable()->contains( + ToDPart, ToDPart->getTemplateParameters()->getParam(0))); +} + +TEST_P(ASTImporterOptionSpecificTestBase, + CXXDeductionGuideTemplateParameterDeclContext) { + Decl *FromTU = getTuDecl( + R"( + template struct A { + A(T); + }; + A a{(int)0}; + )", + Lang_CXX17, "input.cc"); +// clang-format off +/* +|-ClassTemplateDecl 0x1fe5000 line:2:36 A +| |-TemplateTypeParmDecl 0x1fe4eb0 col:26 referenced typename depth 0 index 0 T +| |-CXXRecordDecl 0x1fe4f70 line:2:36 struct A definition + +|-FunctionTemplateDecl 0x1fe5860 col:9 implicit +| |-TemplateTypeParmDecl 0x1fe4eb0 col:26 referenced typename depth 0 index 0 T +| |-CXXDeductionGuideDecl 0x1fe57a8 col:9 implicit 'auto (T) -> A' +| | `-ParmVarDecl 0x1fe56b0 col:12 'T' +| `-CXXDeductionGuideDecl 0x20515d8 col:9 implicit used 'auto (int) -> A' +| |-TemplateArgument type 'int' +| | `-BuiltinType 0x20587e0 'int' +| `-ParmVarDecl 0x2051388 col:12 'int':'int' +`-FunctionTemplateDecl 0x1fe5a78 col:36 implicit + |-TemplateTypeParmDecl 0x1fe4eb0 col:26 referenced typename depth 0 index 0 T + `-CXXDeductionGuideDecl 0x1fe59c0 col:36 implicit 'auto (A) -> A' + `-ParmVarDecl 0x1fe5958 col:36 'A' +*/ +// clang-format on + auto *FromD1 = FirstDeclMatcher().match( + FromTU, cxxDeductionGuideDecl()); + auto *FromD2 = LastDeclMatcher().match( + FromTU, cxxDeductionGuideDecl()); + + NamedDecl *P1 = + FromD1->getDescribedFunctionTemplate()->getTemplateParameters()->getParam( + 0); + NamedDecl *P2 = + FromD2->getDescribedFunctionTemplate()->getTemplateParameters()->getParam( + 0); + DeclContext *DC = P1->getDeclContext(); + + ASSERT_EQ(P1, P2); + ASSERT_TRUE(DC == FromD1 || DC == FromD2); + + auto *ToD1 = Import(FromD1, Lang_CXX17); + auto *ToD2 = Import(FromD2, Lang_CXX17); + ASSERT_TRUE(ToD1 && ToD2); + + P1 = ToD1->getDescribedFunctionTemplate()->getTemplateParameters()->getParam( + 0); + P2 = ToD2->getDescribedFunctionTemplate()->getTemplateParameters()->getParam( + 0); + DC = P1->getDeclContext(); + + EXPECT_EQ(P1, P2); + EXPECT_TRUE(DC == ToD1 || DC == ToD2); + + ASTImporterLookupTable *Tbl = SharedStatePtr->getLookupTable(); + if (Tbl->contains(ToD1, P1)) { + EXPECT_FALSE(Tbl->contains(ToD2, P1)); + } else { + EXPECT_TRUE(Tbl->contains(ToD2, P1)); + } +} + +TEST_P(ASTImporterOptionSpecificTestBase, VarTemplateParameterDeclContext) { + constexpr auto Code = + R"( + template + int X1; + template + int X1; + + namespace Ns { + template + int X2; + template + int X2; + } + )"; + + Decl *FromTU = getTuDecl(Code, Lang_CXX14); + + auto *FromD1 = FirstDeclMatcher().match( + FromTU, varTemplateDecl(hasName("X1"))); + auto *FromD1Part = + FirstDeclMatcher().match( + FromTU, varTemplatePartialSpecializationDecl(hasName("X1"))); + auto *FromD2 = FirstDeclMatcher().match( + FromTU, varTemplateDecl(hasName("X2"))); + auto *FromD2Part = + FirstDeclMatcher().match( + FromTU, varTemplatePartialSpecializationDecl(hasName("X2"))); + + ASSERT_EQ(FromD1->getTemplateParameters()->getParam(0)->getDeclContext(), + FromD1->getDeclContext()); + ASSERT_EQ(FromD2->getTemplateParameters()->getParam(0)->getDeclContext(), + FromD2->getDeclContext()); + + ASSERT_EQ(FromD1Part->getTemplateParameters()->getParam(0)->getDeclContext(), + FromD1Part->getDeclContext()); + // FIXME: VarTemplatePartialSpecializationDecl does not update ("adopt") + // template parameter decl context + // ASSERT_EQ(FromD2Part->getTemplateParameters()->getParam(0)->getDeclContext(), + // FromD2Part->getDeclContext()); + + auto *ToD1 = Import(FromD1, Lang_CXX14); + auto *ToD2 = Import(FromD2, Lang_CXX14); + + auto *ToD1Part = Import(FromD1Part, Lang_CXX14); + auto *ToD2Part = Import(FromD2Part, Lang_CXX14); + + EXPECT_EQ(ToD1->getTemplateParameters()->getParam(0)->getDeclContext(), + ToD1->getDeclContext()); + EXPECT_TRUE(SharedStatePtr->getLookupTable()->contains( + ToD1->getDeclContext(), ToD1->getTemplateParameters()->getParam(0))); + EXPECT_EQ(ToD2->getTemplateParameters()->getParam(0)->getDeclContext(), + ToD2->getDeclContext()); + EXPECT_TRUE(SharedStatePtr->getLookupTable()->contains( + ToD2->getDeclContext(), ToD2->getTemplateParameters()->getParam(0))); + + EXPECT_EQ(ToD1Part->getTemplateParameters()->getParam(0)->getDeclContext(), + ToD1Part->getDeclContext()); + EXPECT_TRUE(SharedStatePtr->getLookupTable()->contains( + ToD1Part->getDeclContext(), + ToD1Part->getTemplateParameters()->getParam(0))); + // EXPECT_EQ(ToD2Part->getTemplateParameters()->getParam(0)->getDeclContext(), + // ToD2Part->getDeclContext()); + // EXPECT_TRUE(SharedStatePtr->getLookupTable()->contains( + // ToD2Part->getDeclContext(), + // ToD2Part->getTemplateParameters()->getParam(0))); + (void)ToD2Part; +} + +TEST_P(ASTImporterOptionSpecificTestBase, + TypeAliasTemplateParameterDeclContext) { + constexpr auto Code = + R"( + template + struct S {}; + template using S1 = S; + namespace Ns { + template using S2 = S; + } + )"; + + Decl *FromTU = getTuDecl(Code, Lang_CXX11); + + auto *FromD1 = FirstDeclMatcher().match( + FromTU, typeAliasTemplateDecl(hasName("S1"))); + auto *FromD2 = FirstDeclMatcher().match( + FromTU, typeAliasTemplateDecl(hasName("S2"))); + + ASSERT_EQ(FromD1->getTemplateParameters()->getParam(0)->getDeclContext(), + FromD1->getDeclContext()); + ASSERT_EQ(FromD2->getTemplateParameters()->getParam(0)->getDeclContext(), + FromD2->getDeclContext()); + + auto *ToD1 = Import(FromD1, Lang_CXX11); + auto *ToD2 = Import(FromD2, Lang_CXX11); + + EXPECT_EQ(ToD1->getTemplateParameters()->getParam(0)->getDeclContext(), + ToD1->getDeclContext()); + EXPECT_TRUE(SharedStatePtr->getLookupTable()->contains( + ToD1->getDeclContext(), ToD1->getTemplateParameters()->getParam(0))); + EXPECT_EQ(ToD2->getTemplateParameters()->getParam(0)->getDeclContext(), + ToD2->getDeclContext()); + EXPECT_TRUE(SharedStatePtr->getLookupTable()->contains( + ToD2->getDeclContext(), ToD2->getTemplateParameters()->getParam(0))); +} + struct ASTImporterLookupTableTest : ASTImporterOptionSpecificTestBase {}; TEST_P(ASTImporterLookupTableTest, OneDecl) {