[clang] [clang][ASTImporter] Fix import of template parameter default values. (PR #100100)
Balázs Kéri via cfe-commits
cfe-commits at lists.llvm.org
Tue Jul 23 03:55:45 PDT 2024
https://github.com/balazske created https://github.com/llvm/llvm-project/pull/100100
Default values of template parameters (non-type, type, template) were not correctly handled in the "inherited" case. This occurs if the first declaration contains the default value but a next one not. The default value is "inherited" from the first.
In ASTImporter it was only possible to set the inherited status after the template object was created with the template parameters that were imported without handling the inherited case. The import function of the template parameter contains not enough information (previous declaration) to set the inherited-from status. After the template was created, default value of the parameters that should be inherited is reset to inherited mode.
>From 8a5280d4c7de49128d9ebabc14c41985603df748 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= <balazs.keri at ericsson.com>
Date: Tue, 23 Jul 2024 11:20:22 +0200
Subject: [PATCH] [clang][ASTImporter] Fix import of template parameter default
values.
Default values of template parameters (non-type, type, template)
were not correctly handled in the "inherited" case. This occurs
if the first declaration contains the default value but a next
one not. The default value is "inherited" from the first.
In ASTImporter it was only possible to set the inherited status
after the template object was created with the template parameters
that were imported without handling the inherited case. The
import function of the template parameter contains not enough
information (previous declaration) to set the inherited-from status.
After the template was created, default value of the parameters
that should be inherited is reset to inherited mode.
---
clang/lib/AST/ASTImporter.cpp | 34 ++++
clang/unittests/AST/ASTImporterTest.cpp | 215 ++++++++++++++++--------
2 files changed, 179 insertions(+), 70 deletions(-)
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 08ef09d353afc..1d9ea714780ce 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -359,6 +359,31 @@ namespace clang {
Params, Importer.getToContext().getTranslationUnitDecl());
}
+ template <typename TemplateParmDeclT>
+ void tryUpdateTemplateParmDeclInheritedFrom(NamedDecl *RecentParm,
+ NamedDecl *NewParm) {
+ if (auto *ParmT = dyn_cast<TemplateParmDeclT>(RecentParm)) {
+ if (ParmT->hasDefaultArgument()) {
+ auto *P = cast<TemplateParmDeclT>(NewParm);
+ P->removeDefaultArgument();
+ P->setInheritedDefaultArgument(Importer.ToContext, ParmT);
+ }
+ }
+ }
+
+ void updateTemplateParametersInheritedFrom(
+ const TemplateParameterList &RecentParams,
+ TemplateParameterList &NewParams) {
+ for (auto [Idx, Param] : enumerate(RecentParams)) {
+ tryUpdateTemplateParmDeclInheritedFrom<NonTypeTemplateParmDecl>(
+ Param, NewParams.getParam(Idx));
+ tryUpdateTemplateParmDeclInheritedFrom<TemplateTypeParmDecl>(
+ Param, NewParams.getParam(Idx));
+ tryUpdateTemplateParmDeclInheritedFrom<TemplateTemplateParmDecl>(
+ Param, NewParams.getParam(Idx));
+ }
+ }
+
public:
explicit ASTNodeImporter(ASTImporter &Importer) : Importer(Importer) {}
@@ -6138,6 +6163,9 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
}
D2->setPreviousDecl(Recent);
+
+ updateTemplateParametersInheritedFrom(*(Recent->getTemplateParameters()),
+ **TemplateParamsOrErr);
}
return D2;
@@ -6452,6 +6480,9 @@ ExpectedDecl ASTNodeImporter::VisitVarTemplateDecl(VarTemplateDecl *D) {
ToTemplated->setPreviousDecl(PrevTemplated);
}
ToVarTD->setPreviousDecl(Recent);
+
+ updateTemplateParametersInheritedFrom(*(Recent->getTemplateParameters()),
+ **TemplateParamsOrErr);
}
return ToVarTD;
@@ -6724,6 +6755,9 @@ ASTNodeImporter::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) {
TemplatedFD->setPreviousDecl(PrevTemplated);
}
ToFunc->setPreviousDecl(Recent);
+
+ updateTemplateParametersInheritedFrom(*(Recent->getTemplateParameters()),
+ *Params);
}
return ToFunc;
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index 6d987cc7e9ec6..5f6d9fec7052b 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -9681,59 +9681,40 @@ AST_MATCHER_P(EnumDecl, hasEnumConstName, StringRef, ConstName) {
return false;
}
-TEST_P(ASTImporterOptionSpecificTestBase, ImportAnonymousEnums) {
- const char *Code =
+TEST_P(ASTImporterOptionSpecificTestBase, ImportExistingAnonymousEnum) {
+ const char *ToCode =
R"(
struct A {
- enum { E1, E2 } x;
- enum { E3, E4 } y;
+ enum { E1, E2} x;
+ enum { E3, E4} y;
};
)";
- Decl *FromTU = getTuDecl(Code, Lang_CXX11);
- auto *FromEnumE1 = FirstDeclMatcher<EnumDecl>().match(
- FromTU, enumDecl(hasEnumConstName("E1")));
- auto *ImportedEnumE1 = Import(FromEnumE1, Lang_CXX11);
- EXPECT_TRUE(ImportedEnumE1);
- auto *FromEnumE3 = FirstDeclMatcher<EnumDecl>().match(
- FromTU, enumDecl(hasEnumConstName("E3")));
- auto *ImportedEnumE3 = Import(FromEnumE3, Lang_CXX11);
- EXPECT_TRUE(ImportedEnumE3);
- EXPECT_NE(ImportedEnumE1, ImportedEnumE3);
-}
-
-TEST_P(ASTImporterOptionSpecificTestBase, ImportFreeStandingAnonymousEnums) {
+ Decl *ToTU = getToTuDecl(ToCode, Lang_CXX11);
+ auto *ToE1 = FirstDeclMatcher<EnumDecl>().match(
+ ToTU, enumDecl(hasEnumConstName("E1")));
+ auto *ToE3 = FirstDeclMatcher<EnumDecl>().match(
+ ToTU, enumDecl(hasEnumConstName("E3")));
const char *Code =
R"(
struct A {
- enum { E1, E2 };
- enum { E3, E4 };
+ enum { E1, E2} x;
+ enum { E3, E4} y;
};
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
- auto *FromEnumE1 = FirstDeclMatcher<EnumDecl>().match(
+ auto *FromE1 = FirstDeclMatcher<EnumDecl>().match(
FromTU, enumDecl(hasEnumConstName("E1")));
- auto *ImportedEnumE1 = Import(FromEnumE1, Lang_CXX11);
- EXPECT_TRUE(ImportedEnumE1);
- auto *FromEnumE3 = FirstDeclMatcher<EnumDecl>().match(
+ auto *ImportedE1 = Import(FromE1, Lang_CXX11);
+ ASSERT_TRUE(ImportedE1);
+ EXPECT_EQ(ImportedE1, ToE1);
+ auto *FromE3 = FirstDeclMatcher<EnumDecl>().match(
FromTU, enumDecl(hasEnumConstName("E3")));
- auto *ImportedEnumE3 = Import(FromEnumE3, Lang_CXX11);
- EXPECT_TRUE(ImportedEnumE3);
- EXPECT_NE(ImportedEnumE1, ImportedEnumE3);
+ auto *ImportedE3 = Import(FromE3, Lang_CXX11);
+ ASSERT_TRUE(ImportedE3);
+ EXPECT_EQ(ImportedE3, ToE3);
}
-TEST_P(ASTImporterOptionSpecificTestBase, ImportExistingAnonymousEnums) {
- const char *ToCode =
- R"(
- struct A {
- enum { E1, E2 } x;
- enum { E3, E4 } y;
- };
- )";
- Decl *ToTU = getToTuDecl(ToCode, Lang_CXX11);
- auto *ToEnumE1 = FirstDeclMatcher<EnumDecl>().match(
- ToTU, enumDecl(hasEnumConstName("E1")));
- auto *ToEnumE3 = FirstDeclMatcher<EnumDecl>().match(
- ToTU, enumDecl(hasEnumConstName("E3")));
+TEST_P(ASTImporterOptionSpecificTestBase, ImportAnonymousEnum) {
const char *Code =
R"(
struct A {
@@ -9742,45 +9723,136 @@ TEST_P(ASTImporterOptionSpecificTestBase, ImportExistingAnonymousEnums) {
};
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
- auto *FromEnumE1 = FirstDeclMatcher<EnumDecl>().match(
+ auto *FromE1 = FirstDeclMatcher<EnumDecl>().match(
FromTU, enumDecl(hasEnumConstName("E1")));
- auto *ImportedEnumE1 = Import(FromEnumE1, Lang_CXX11);
- ASSERT_TRUE(ImportedEnumE1);
- EXPECT_EQ(ImportedEnumE1, ToEnumE1);
- auto *FromEnumE3 = FirstDeclMatcher<EnumDecl>().match(
+ auto *ImportedE1 = Import(FromE1, Lang_CXX11);
+ ASSERT_TRUE(ImportedE1);
+ auto *FromE3 = FirstDeclMatcher<EnumDecl>().match(
FromTU, enumDecl(hasEnumConstName("E3")));
- auto *ImportedEnumE3 = Import(FromEnumE3, Lang_CXX11);
- ASSERT_TRUE(ImportedEnumE3);
- EXPECT_EQ(ImportedEnumE3, ToEnumE3);
+ auto *ImportedE3 = Import(FromE3, Lang_CXX11);
+ ASSERT_TRUE(ImportedE3);
}
-TEST_P(ASTImporterOptionSpecificTestBase, ImportExistingEmptyAnonymousEnums) {
- const char *ToCode =
+struct ImportTemplateParmDeclDefaultValue
+ : public ASTImporterOptionSpecificTestBase {
+protected:
+ void checkTemplateParams(RedeclarableTemplateDecl *D) {
+ auto *CanD = cast<RedeclarableTemplateDecl>(D->getCanonicalDecl());
+ auto *CanNonTypeP = cast<NonTypeTemplateParmDecl>(
+ CanD->getTemplateParameters()->getParam(0));
+ auto *CanTypeP =
+ cast<TemplateTypeParmDecl>(CanD->getTemplateParameters()->getParam(1));
+ auto *CanTemplateP = cast<TemplateTemplateParmDecl>(
+ CanD->getTemplateParameters()->getParam(2));
+ EXPECT_FALSE(CanNonTypeP->getDefaultArgStorage().isInherited());
+ EXPECT_FALSE(CanTypeP->getDefaultArgStorage().isInherited());
+ EXPECT_FALSE(CanTemplateP->getDefaultArgStorage().isInherited());
+ for (Decl *Redecl : D->redecls()) {
+ auto *ReD = cast<RedeclarableTemplateDecl>(Redecl);
+ if (ReD != CanD) {
+ auto *NonTypeP = cast<NonTypeTemplateParmDecl>(
+ ReD->getTemplateParameters()->getParam(0));
+ auto *TypeP = cast<TemplateTypeParmDecl>(
+ ReD->getTemplateParameters()->getParam(1));
+ auto *TemplateP = cast<TemplateTemplateParmDecl>(
+ ReD->getTemplateParameters()->getParam(2));
+ EXPECT_TRUE(NonTypeP->getDefaultArgStorage().isInherited());
+ EXPECT_TRUE(TypeP->getDefaultArgStorage().isInherited());
+ EXPECT_TRUE(TemplateP->getDefaultArgStorage().isInherited());
+ EXPECT_EQ(NonTypeP->getDefaultArgStorage().getInheritedFrom(),
+ CanNonTypeP);
+ EXPECT_EQ(TypeP->getDefaultArgStorage().getInheritedFrom(), CanTypeP);
+ EXPECT_EQ(TemplateP->getDefaultArgStorage().getInheritedFrom(),
+ CanTemplateP);
+ }
+ }
+ }
+
+ void testImport(RedeclarableTemplateDecl *FromD) {
+ RedeclarableTemplateDecl *ToD = Import(FromD, Lang_CXX14);
+ checkTemplateParams(ToD);
+ }
+
+ const char *CodeFunction =
R"(
- struct A {
- enum {};
- };
+ template <class> struct X;
+
+ template <int A = 2, typename B = int, template<class> class C = X>
+ void f();
+ template <int A, typename B, template<class> class C>
+ void f();
+ template <int A, typename B, template<class> class C>
+ void f() {}
)";
- Decl *ToTU = getToTuDecl(ToCode, Lang_CXX11);
- auto *ToE1 = FirstDeclMatcher<EnumDecl>().match(ToTU, enumDecl());
- const char *Code =
+
+ const char *CodeClass =
R"(
- struct A {
- enum {};
- enum {};
- };
+ template <class> struct X;
+
+ template <int A = 2, typename B = int, template<class> class C = X>
+ struct S;
+ template <int A, typename B, template<class> class C>
+ struct S;
+ template <int A, typename B, template<class> class C>
+ struct S {};
)";
- Decl *FromTU = getTuDecl(Code, Lang_CXX11);
- auto *FromE1 = FirstDeclMatcher<EnumDecl>().match(FromTU, enumDecl());
- auto *ImportedE1 = Import(FromE1, Lang_CXX11);
- ASSERT_TRUE(ImportedE1);
- EXPECT_EQ(ImportedE1, ToE1);
- auto *FromE2 = LastDeclMatcher<EnumDecl>().match(FromTU, enumDecl());
- ASSERT_NE(FromE1, FromE2);
- auto *ImportedE2 = Import(FromE2, Lang_CXX11);
- ASSERT_TRUE(ImportedE2);
- // FIXME: These should not be equal, or the import should fail.
- EXPECT_EQ(ImportedE2, ToE1);
+
+ const char *CodeVar =
+ R"(
+ template <class> struct X;
+
+ template <int A = 2, typename B = int, template<class> class C = X>
+ extern int V;
+ template <int A, typename B, template<class> class C>
+ extern int V;
+ template <int A, typename B, template<class> class C>
+ int V = A;
+ )";
+};
+
+TEST_P(ImportTemplateParmDeclDefaultValue, ImportFunctionTemplate) {
+ Decl *FromTU = getTuDecl(CodeFunction, Lang_CXX14);
+ auto *FromLastD = LastDeclMatcher<FunctionTemplateDecl>().match(
+ FromTU, functionTemplateDecl(hasName("f")));
+ testImport(FromLastD);
+}
+
+TEST_P(ImportTemplateParmDeclDefaultValue, ImportExistingFunctionTemplate) {
+ getToTuDecl(CodeFunction, Lang_CXX14);
+ Decl *FromTU = getTuDecl(CodeFunction, Lang_CXX14);
+ auto *FromLastD = LastDeclMatcher<FunctionTemplateDecl>().match(
+ FromTU, functionTemplateDecl(hasName("f")));
+ testImport(FromLastD);
+}
+
+TEST_P(ImportTemplateParmDeclDefaultValue, ImportClassTemplate) {
+ Decl *FromTU = getTuDecl(CodeClass, Lang_CXX14);
+ auto *FromLastD = LastDeclMatcher<ClassTemplateDecl>().match(
+ FromTU, classTemplateDecl(hasName("S")));
+ testImport(FromLastD);
+}
+
+TEST_P(ImportTemplateParmDeclDefaultValue, ImportExistingClassTemplate) {
+ getToTuDecl(CodeClass, Lang_CXX14);
+ Decl *FromTU = getTuDecl(CodeClass, Lang_CXX14);
+ auto *FromLastD = LastDeclMatcher<ClassTemplateDecl>().match(
+ FromTU, classTemplateDecl(hasName("S")));
+ testImport(FromLastD);
+}
+
+TEST_P(ImportTemplateParmDeclDefaultValue, ImportVarTemplate) {
+ Decl *FromTU = getTuDecl(CodeVar, Lang_CXX14);
+ auto *FromLastD = LastDeclMatcher<VarTemplateDecl>().match(
+ FromTU, varTemplateDecl(hasName("V")));
+ testImport(FromLastD);
+}
+
+TEST_P(ImportTemplateParmDeclDefaultValue, ImportExistingVarTemplate) {
+ getToTuDecl(CodeVar, Lang_CXX14);
+ Decl *FromTU = getTuDecl(CodeVar, Lang_CXX14);
+ auto *FromLastD = LastDeclMatcher<VarTemplateDecl>().match(
+ FromTU, varTemplateDecl(hasName("V")));
+ testImport(FromLastD);
}
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ASTImporterLookupTableTest,
@@ -9866,6 +9938,9 @@ INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportInjectedClassNameType,
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportMatrixType,
DefaultTestValuesForRunOptions);
+INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportTemplateParmDeclDefaultValue,
+ DefaultTestValuesForRunOptions);
+
// FIXME: Make ImportOpenCLPipe test work.
// INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportOpenCLPipe,
// DefaultTestValuesForRunOptions);
More information about the cfe-commits
mailing list