[clang] [Clang][Sema] Use correct TemplateName when transforming TemplateSpecializationType (PR #93411)
via cfe-commits
cfe-commits at lists.llvm.org
Sun May 26 05:27:01 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Qizhi Hu (jcsxky)
<details>
<summary>Changes</summary>
Consider the following testcase:
```cpp
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 `clang(assertions trunk)`. The reason is that we lookup `X`(`B::X`) in `##<!-- -->1` when instantiating `typedef B::X<typename B::arg> x; ` during instantiating `A<int>::C::x`. This is incorrect because we should lookup `X` in `##<!-- -->2` when we see the declaration `A<int>::C::x a;`. Since clang parse `A<T>::B<T>` to an `ElaboratedType`(`typename` is not required while compiling with `-std=c++20`) while `typename A<T>::B<T>` turns to be a `DependentTemplateSpecializationType`, we should rebuild the `TemplateName` with transformed `Qualifier`(whose type is `NestedNameSpecifier`) to make sure the lookup context is correct.
This patch also attempts to fix #<!-- -->91677 which crashes with the same reason.
---
Full diff: https://github.com/llvm/llvm-project/pull/93411.diff
3 Files Affected:
- (modified) clang/lib/Sema/TreeTransform.h (+24-3)
- (added) clang/test/SemaCXX/PR91677.cpp (+31)
- (modified) clang/test/SemaTemplate/typename-specifier-3.cpp (+4-3)
``````````diff
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 {
``````````
</details>
https://github.com/llvm/llvm-project/pull/93411
More information about the cfe-commits
mailing list