-
Notifications
You must be signed in to change notification settings - Fork 13.4k
release/20.x: [clang] Forward TPL of NestedNameSpecifier #137806
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: release/20.x
Are you sure you want to change the base?
Conversation
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.
@llvm/pr-subscribers-clang Author: Jonas Hahnfeld (hahnjo) ChangesThis avoids type suffixes for integer constants when the type can be inferred from the template parameter, such as the Note: This issue does not exist anymore in Full diff: https://github.com/llvm/llvm-project/pull/137806.diff 2 Files Affected:
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";
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great, @hahnjo. We need this to unbreak llvm20 for downstream clients. LGTM!
Is there a bug report for this, I would like to understand how this is breaking someone? IF there is not one, can we please file one. Without understanding the problem better, I don't see why we would want to backport this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The summary should also explain the problem being fixed and how exactly this fixes the problem. The motivation for backporting is not clear to me from what is provided.
Hi @shafik, the issue is an inconsistency in the treatment of template specialization types depending on whether they are the prefix or the "final" type. To stay with the example of the test, the result without this patch is There is no issue for this because the problem is not present anymore in |
This avoids type suffixes for integer constants when the type can be inferred from the template parameter, such as the
unsigned
parameter ofA<1>
andA<2>
in the added test.Note: This issue does not exist anymore in
main
, see #137804. However, I don't think dc17429 should be backported torelease/20.x
, so I'm proposing a targeted fix.