-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[Clang][Sema] Use correct TemplateName when transforming TemplateSpecializationType #93411
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
[Clang][Sema] Use correct TemplateName when transforming TemplateSpecializationType #93411
Conversation
@llvm/pr-subscribers-clang Author: Qizhi Hu (jcsxky) ChangesConsider the following testcase: namespace PR12884_original {
template <typename T> struct A {
struct B { ##<!-- -->1
template <typename U> struct X {};
typedef int arg;
};
struct C {
typedef B::X<typename B::arg> x;
};
};
template <> struct A<int>::B { ##<!-- -->2
template <int N> struct X {};
static const int arg = 0;
};
A<int>::C::x a;
} It will crash when compiling with Full diff: https://github.com/llvm/llvm-project/pull/93411.diff 3 Files Affected:
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index dee335b526991..6ef2eec09ec02 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -7216,9 +7216,30 @@ TreeTransform<Derived>::TransformElaboratedType(TypeLocBuilder &TLB,
return QualType();
}
- QualType NamedT = getDerived().TransformType(TLB, TL.getNamedTypeLoc());
- if (NamedT.isNull())
- return QualType();
+ QualType NamedT;
+ if (SemaRef.getLangOpts().CPlusPlus20 && QualifierLoc &&
+ isa<TemplateSpecializationType>(TL.getNamedTypeLoc().getType())) {
+ TemplateSpecializationTypeLoc SpecTL =
+ TL.getNamedTypeLoc().castAs<TemplateSpecializationTypeLoc>();
+ const TemplateSpecializationType *TST =
+ SpecTL.getType()->castAs<TemplateSpecializationType>();
+ CXXScopeSpec SS;
+ SS.Adopt(QualifierLoc);
+ if (TemplateDecl *TD = TST->getTemplateName().getAsTemplateDecl()) {
+ TemplateName InstName = getDerived().RebuildTemplateName(
+ SS, TL.getTemplateKeywordLoc(), *TD->getIdentifier(),
+ TL.getNamedTypeLoc().getBeginLoc(), /*ObjectType=*/QualType(),
+ /*FirstQualifierInScope=*/nullptr, /*AllowInjectedClassName=*/false);
+ if (InstName.isNull())
+ return QualType();
+ NamedT = TransformTemplateSpecializationType(TLB, SpecTL, InstName);
+ }
+ }
+ if (NamedT.isNull()) {
+ NamedT = getDerived().TransformType(TLB, TL.getNamedTypeLoc());
+ if (NamedT.isNull())
+ return QualType();
+ }
// C++0x [dcl.type.elab]p2:
// If the identifier resolves to a typedef-name or the simple-template-id
diff --git a/clang/test/SemaCXX/PR91677.cpp b/clang/test/SemaCXX/PR91677.cpp
new file mode 100644
index 0000000000000..ef2999f959506
--- /dev/null
+++ b/clang/test/SemaCXX/PR91677.cpp
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s
+// expected-no-diagnostics
+
+template <typename> struct t1 {
+ template <typename>
+ struct t2 {};
+};
+
+template <typename T>
+t1<T>::template t2<T> f1();
+
+void f2() {
+ f1<bool>();
+}
+
+namespace N {
+ template <typename T> struct A {
+ struct B {
+ template <typename U> struct X {};
+ typedef int arg;
+ };
+ struct C {
+ typedef B::template X<B::arg> x;
+ };
+ };
+
+ template <> struct A<int>::B {
+ template <int N> struct X {};
+ static const int arg = 0;
+ };
+}
diff --git a/clang/test/SemaTemplate/typename-specifier-3.cpp b/clang/test/SemaTemplate/typename-specifier-3.cpp
index 714830f0032d2..a62a1fc5ab39c 100644
--- a/clang/test/SemaTemplate/typename-specifier-3.cpp
+++ b/clang/test/SemaTemplate/typename-specifier-3.cpp
@@ -28,16 +28,17 @@ namespace PR12884_original {
typedef int arg;
};
struct C {
- typedef B::X<typename B::arg> x; // precxx17-warning{{missing 'typename' prior to dependent type name B::X; implicit 'typename' is a C++20 extension}}
+ typedef B::X<typename B::arg> x; // precxx17-warning{{missing 'typename' prior to dependent type name B::X; implicit 'typename' is a C++20 extension}} \
+ cxx17-error{{typename specifier refers to non-type member 'arg' in 'PR12884_original::A<int>::B'}}
};
};
template <> struct A<int>::B {
template <int N> struct X {};
- static const int arg = 0;
+ static const int arg = 0; // cxx17-note{{referenced member 'arg' is declared here}}
};
- A<int>::C::x a;
+ A<int>::C::x a; // cxx17-note{{in instantiation of member class 'PR12884_original::A<int>::C' requested here}}
}
namespace PR12884_half_fixed {
template <typename T> struct A {
|
cb29d45
to
e60cdac
Compare
e60cdac
to
3ebed0f
Compare
@@ -28,16 +28,17 @@ namespace PR12884_original { | |||
typedef int arg; | |||
}; | |||
struct C { | |||
typedef B::X<typename B::arg> x; // precxx17-warning{{missing 'typename' prior to dependent type name B::X; implicit 'typename' is a C++20 extension}} |
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.
A drive-by comment: The test PR12884_original
is still crashing on trunk but it slips through our testing probably because we somehow stop after errors above - if we move the whole namespace to the top, then we'd immediately run into an assert saying Unable to find instantiation of declaration!
.
/*FirstQualifierInScope=*/nullptr, /*AllowInjectedClassName=*/false); | ||
if (InstName.isNull()) | ||
return QualType(); | ||
NamedT = TransformTemplateSpecializationType(TLB, SpecTL, InstName); |
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.
Did you mean getDerived().TransformTemplateSpecializationType()
?
if (SemaRef.getLangOpts().CPlusPlus20 && QualifierLoc && | ||
isa<TemplateSpecializationType>(TL.getNamedTypeLoc().getType())) { | ||
TemplateSpecializationTypeLoc SpecTL = | ||
TL.getNamedTypeLoc().castAs<TemplateSpecializationTypeLoc>(); | ||
const TemplateSpecializationType *TST = | ||
SpecTL.getType()->castAs<TemplateSpecializationType>(); |
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.
if (QualType QT = TL.getNamedTypeLoc().getType(); ... && isa<...>(QT)) {
auto SpecTL = ...;
auto *TST = QT.getTypePtr();
}
Probably looks better.
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 problem here is the loss of the qualification on the TemplateNames
This patch fixes the problem, without taking any workarounds: #93433
It also doesn't cause any change in diagnostics in clang/test/SemaTemplate/typename-specifier-3.cpp
.
I think we should report this message since |
Yeah, I am not saying that is wrong, but this needs more investigation. I am not looking at this bug specifically: I just noticed my patch would have an effect here, and after testing it sure did, but I haven't looked closely. Maybe I used the term 'fixes' incorrectly, as in my patch makes it not crash, but it's strange that type sugar would have a semantic effect here, that is not supposed to happen. Again, thinking in first principles, without looking at this example too specifically, my intuition says that what might be happening here is that we transformed a |
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.
So After we have formed a TemplateSpecializationType, we are done with the NNS. We keep it around in an ElaboratedType for type sugar only, it should not be needed to compile the program correctly anymore.
So I think this solution violates one important aspect of our AST design.
If we still need the NNS, we have to hold-off on building the TST until we don't.
Ah, I see. Thanks for your explanation on the motivation of AST design! |
Consider the following testcase:
It will crash when compiling with
clang(assertions trunk)
. The reason is that we lookupX
(B::X
) in##1
when instantiatingtypedef B::X<typename B::arg> x;
during instantiatingA<int>::C::x
. This is incorrect because we should lookupX
in##2
when we see the declarationA<int>::C::x a;
. Since clang parseA<T>::B<T>
to anElaboratedType
(typename
is not required while compiling with-std=c++20
) whiletypename A<T>::B<T>
turns to be aDependentTemplateSpecializationType
, we should rebuild theTemplateName
with transformedQualifier
(whose type isNestedNameSpecifier
) to make sure the lookup context is correct.This patch also attempts to fix #91677 which crashes with the same reason.