-
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
Conversation
@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 |
@shafik do the explanations make sense? |
In principle this also should fix the regression in the type correction but I failed to trigger that logic there. We really need this for enabling downstream clients that use the api for consistent type printing. |
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.
IIUC correctly this will just be backporting what the new behavior will be to help downstream. I think it makes sense to me. Thank you for explaining.
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.
d787edd
to
4370072
Compare
@hahnjo (or anyone else). If you would like to add a note about this fix in the release notes (completely optional). Please reply to this comment with a one or two sentence description of the fix. When you are done, please add the release:note label to this PR. |
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.