[llvm-branch-commits] [clang] release/20.x: [clang] Forward TPL of NestedNameSpecifier (PR #137806)
Jonas Hahnfeld via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Apr 29 06:14:22 PDT 2025
https://github.com/hahnjo created https://github.com/llvm/llvm-project/pull/137806
This avoids type suffixes for integer constants when the type can be inferred from the template parameter, such as the `unsigned` parameter of `A<1>` and `A<2>` in the added test.
---
Note: This issue does not exist anymore in `main`, see https://github.com/llvm/llvm-project/pull/137804. However, I don't think dc17429ae6961a6783371dcf6749eea657b5446a should be backported to `release/20.x`, so I'm proposing a targeted fix.
>From d787edd6017104fc2dc15effdbceb02490d7bc19 Mon Sep 17 00:00:00 2001
From: Jonas Hahnfeld <jonas.hahnfeld at cern.ch>
Date: Tue, 29 Apr 2025 14:54:30 +0200
Subject: [PATCH] [clang] Forward TPL of NestedNameSpecifier
This avoids type suffixes for integer constants when the type can be
inferred from the template parameter, such as the unsigned parameter
of A<1> and A<2> in the added test.
---
clang/lib/AST/NestedNameSpecifier.cpp | 17 ++--
clang/unittests/Tooling/QualTypeNamesTest.cpp | 96 +++++++++++++++++++
2 files changed, 106 insertions(+), 7 deletions(-)
diff --git a/clang/lib/AST/NestedNameSpecifier.cpp b/clang/lib/AST/NestedNameSpecifier.cpp
index 76c77569da9fd..c043996f1ada3 100644
--- a/clang/lib/AST/NestedNameSpecifier.cpp
+++ b/clang/lib/AST/NestedNameSpecifier.cpp
@@ -283,13 +283,16 @@ void NestedNameSpecifier::print(raw_ostream &OS, const PrintingPolicy &Policy,
case TypeSpec: {
const auto *Record =
dyn_cast_or_null<ClassTemplateSpecializationDecl>(getAsRecordDecl());
- if (ResolveTemplateArguments && Record) {
+ const TemplateParameterList *TPL = nullptr;
+ if (Record) {
+ TPL = Record->getSpecializedTemplate()->getTemplateParameters();
+ if (ResolveTemplateArguments) {
// Print the type trait with resolved template parameters.
Record->printName(OS, Policy);
- printTemplateArgumentList(
- OS, Record->getTemplateArgs().asArray(), Policy,
- Record->getSpecializedTemplate()->getTemplateParameters());
+ printTemplateArgumentList(OS, Record->getTemplateArgs().asArray(),
+ Policy, TPL);
break;
+ }
}
const Type *T = getAsType();
@@ -313,8 +316,8 @@ void NestedNameSpecifier::print(raw_ostream &OS, const PrintingPolicy &Policy,
TemplateName::Qualified::None);
// Print the template argument list.
- printTemplateArgumentList(OS, SpecType->template_arguments(),
- InnerPolicy);
+ printTemplateArgumentList(OS, SpecType->template_arguments(), InnerPolicy,
+ TPL);
} else if (const auto *DepSpecType =
dyn_cast<DependentTemplateSpecializationType>(T)) {
// Print the template name without its corresponding
@@ -322,7 +325,7 @@ void NestedNameSpecifier::print(raw_ostream &OS, const PrintingPolicy &Policy,
OS << DepSpecType->getIdentifier()->getName();
// Print the template argument list.
printTemplateArgumentList(OS, DepSpecType->template_arguments(),
- InnerPolicy);
+ InnerPolicy, TPL);
} else {
// Print the type normally
QualType(T, 0).print(OS, InnerPolicy);
diff --git a/clang/unittests/Tooling/QualTypeNamesTest.cpp b/clang/unittests/Tooling/QualTypeNamesTest.cpp
index 5ded64d4fcc8c..49c40d633ad4b 100644
--- a/clang/unittests/Tooling/QualTypeNamesTest.cpp
+++ b/clang/unittests/Tooling/QualTypeNamesTest.cpp
@@ -265,6 +265,102 @@ TEST(QualTypeNameTest, InlineNamespace) {
TypeNameVisitor::Lang_CXX11);
}
+TEST(QualTypeNameTest, TemplatedClass) {
+ std::unique_ptr<ASTUnit> AST =
+ tooling::buildASTFromCode("template <unsigned U1> struct A {\n"
+ " template <unsigned U2> struct B {};\n"
+ "};\n"
+ "template struct A<1>;\n"
+ "template struct A<2u>;\n"
+ "template struct A<1>::B<3>;\n"
+ "template struct A<2u>::B<4u>;\n");
+
+ auto &Context = AST->getASTContext();
+ auto &Policy = Context.getPrintingPolicy();
+ auto getFullyQualifiedName = [&](QualType QT) {
+ return TypeName::getFullyQualifiedName(QT, Context, Policy);
+ };
+
+ auto *A = Context.getTranslationUnitDecl()
+ ->lookup(&Context.Idents.get("A"))
+ .find_first<ClassTemplateDecl>();
+ ASSERT_NE(A, nullptr);
+
+ // A has two explicit instantiations: A<1> and A<2u>
+ auto ASpec = A->spec_begin();
+ ASSERT_NE(ASpec, A->spec_end());
+ auto *A1 = *ASpec;
+ ASpec++;
+ ASSERT_NE(ASpec, A->spec_end());
+ auto *A2 = *ASpec;
+
+ // Their type names follow the records.
+ QualType A1RecordTy = Context.getRecordType(A1);
+ EXPECT_EQ(getFullyQualifiedName(A1RecordTy), "A<1>");
+ QualType A2RecordTy = Context.getRecordType(A2);
+ EXPECT_EQ(getFullyQualifiedName(A2RecordTy), "A<2U>");
+
+ // getTemplateSpecializationType() gives types that print the integral
+ // argument directly.
+ TemplateArgument Args1[] = {
+ {Context, llvm::APSInt::getUnsigned(1u), Context.UnsignedIntTy}};
+ QualType A1TemplateSpecTy =
+ Context.getTemplateSpecializationType(TemplateName(A), Args1, A1RecordTy);
+ EXPECT_EQ(A1TemplateSpecTy.getAsString(), "A<1>");
+
+ TemplateArgument Args2[] = {
+ {Context, llvm::APSInt::getUnsigned(2u), Context.UnsignedIntTy}};
+ QualType A2TemplateSpecTy =
+ Context.getTemplateSpecializationType(TemplateName(A), Args2, A2RecordTy);
+ EXPECT_EQ(A2TemplateSpecTy.getAsString(), "A<2>");
+
+ // Find A<1>::B and its specialization B<3>.
+ auto *A1B =
+ A1->lookup(&Context.Idents.get("B")).find_first<ClassTemplateDecl>();
+ ASSERT_NE(A1B, nullptr);
+ auto A1BSpec = A1B->spec_begin();
+ ASSERT_NE(A1BSpec, A1B->spec_end());
+ auto *A1B3 = *A1BSpec;
+ QualType A1B3RecordTy = Context.getRecordType(A1B3);
+ EXPECT_EQ(getFullyQualifiedName(A1B3RecordTy), "A<1>::B<3>");
+
+ // Construct A<1>::B<3> and check name.
+ TemplateArgument Args3[] = {
+ {Context, llvm::APSInt::getUnsigned(3u), Context.UnsignedIntTy}};
+ QualType A1B3TemplateSpecTy = Context.getTemplateSpecializationType(
+ TemplateName(A1B), Args3, A1B3RecordTy);
+ EXPECT_EQ(A1B3TemplateSpecTy.getAsString(), "B<3>");
+
+ NestedNameSpecifier *A1Nested = NestedNameSpecifier::Create(
+ Context, nullptr, false, A1TemplateSpecTy.getTypePtr());
+ QualType A1B3ElaboratedTy = Context.getElaboratedType(
+ ElaboratedTypeKeyword::None, A1Nested, A1B3TemplateSpecTy);
+ EXPECT_EQ(A1B3ElaboratedTy.getAsString(), "A<1>::B<3>");
+
+ // Find A<2u>::B and its specialization B<4u>.
+ auto *A2B =
+ A2->lookup(&Context.Idents.get("B")).find_first<ClassTemplateDecl>();
+ ASSERT_NE(A2B, nullptr);
+ auto A2BSpec = A2B->spec_begin();
+ ASSERT_NE(A2BSpec, A2B->spec_end());
+ auto *A2B4 = *A2BSpec;
+ QualType A2B4RecordTy = Context.getRecordType(A2B4);
+ EXPECT_EQ(getFullyQualifiedName(A2B4RecordTy), "A<2U>::B<4U>");
+
+ // Construct A<2>::B<4> and check name.
+ TemplateArgument Args4[] = {
+ {Context, llvm::APSInt::getUnsigned(4u), Context.UnsignedIntTy}};
+ QualType A2B4TemplateSpecTy = Context.getTemplateSpecializationType(
+ TemplateName(A2B), Args4, A2B4RecordTy);
+ EXPECT_EQ(A2B4TemplateSpecTy.getAsString(), "B<4>");
+
+ NestedNameSpecifier *A2Nested = NestedNameSpecifier::Create(
+ Context, nullptr, false, A2TemplateSpecTy.getTypePtr());
+ QualType A2B4ElaboratedTy = Context.getElaboratedType(
+ ElaboratedTypeKeyword::None, A2Nested, A2B4TemplateSpecTy);
+ EXPECT_EQ(A2B4ElaboratedTy.getAsString(), "A<2>::B<4>");
+}
+
TEST(QualTypeNameTest, AnonStrucs) {
TypeNameVisitor AnonStrucs;
AnonStrucs.ExpectedQualTypeNames["a"] = "short";
More information about the llvm-branch-commits
mailing list