[clang] 869ac40 - [Clang][Sema] Substitute for the type aliases inside of a CTAD guide (#94740)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jul 9 21:51:39 PDT 2024
Author: Younan Zhang
Date: 2024-07-10T12:51:27+08:00
New Revision: 869ac4064861b7e644ae52a72fb80efacfd86462
URL: https://github.com/llvm/llvm-project/commit/869ac4064861b7e644ae52a72fb80efacfd86462
DIFF: https://github.com/llvm/llvm-project/commit/869ac4064861b7e644ae52a72fb80efacfd86462.diff
LOG: [Clang][Sema] Substitute for the type aliases inside of a CTAD guide (#94740)
Similar to the approach of handling nested class templates when building
a CTAD guide, we substitute the template parameters of a type alias
declaration with the instantiating template arguments in order to ensure
the guide eventually doesn't reference any outer template parameters.
For example,
```cpp
template <class T> struct Outer {
using Alias = S<T>;
template <class U> struct Inner {
Inner(Alias);
};
};
```
we used to retain the reference to T accidentally because the
TreeTransform does nothing on type alias Decls by default.
Fixes https://github.com/llvm/llvm-project/issues/94614
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/Sema/Template.h
clang/lib/Sema/SemaTemplate.cpp
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
clang/test/SemaTemplate/nested-deduction-guides.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 2a2bff082ea7f..cd3a4c2b1be1a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -1000,6 +1000,7 @@ Bug Fixes to C++ Support
evaluated to an integer. (#GH96670).
- Fixed a bug where references to lambda capture inside a ``noexcept`` specifier were not correctly
instantiated. (#GH95735).
+- Fixed a CTAD substitution bug involving type aliases that reference outer template parameters. (#GH94614).
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index ce44aca797b0f..0340c23fd170d 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -711,6 +711,7 @@ enum class TemplateSubstitutionKind : char {
VarTemplateSpecializationDecl *PrevDecl = nullptr);
Decl *InstantiateTypedefNameDecl(TypedefNameDecl *D, bool IsTypeAlias);
+ Decl *InstantiateTypeAliasTemplateDecl(TypeAliasTemplateDecl *D);
ClassTemplatePartialSpecializationDecl *
InstantiateClassTemplatePartialSpecialization(
ClassTemplateDecl *ClassTemplate,
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 07b3f793b3a29..726de9172e1cf 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -2178,23 +2178,110 @@ namespace {
class ExtractTypeForDeductionGuide
: public TreeTransform<ExtractTypeForDeductionGuide> {
llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs;
+ ClassTemplateDecl *NestedPattern;
+ const MultiLevelTemplateArgumentList *OuterInstantiationArgs;
+ std::optional<TemplateDeclInstantiator> TypedefNameInstantiator;
public:
typedef TreeTransform<ExtractTypeForDeductionGuide> Base;
ExtractTypeForDeductionGuide(
Sema &SemaRef,
- llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs)
- : Base(SemaRef), MaterializedTypedefs(MaterializedTypedefs) {}
+ llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs,
+ ClassTemplateDecl *NestedPattern,
+ const MultiLevelTemplateArgumentList *OuterInstantiationArgs)
+ : Base(SemaRef), MaterializedTypedefs(MaterializedTypedefs),
+ NestedPattern(NestedPattern),
+ OuterInstantiationArgs(OuterInstantiationArgs) {
+ if (OuterInstantiationArgs)
+ TypedefNameInstantiator.emplace(
+ SemaRef, SemaRef.getASTContext().getTranslationUnitDecl(),
+ *OuterInstantiationArgs);
+ }
TypeSourceInfo *transform(TypeSourceInfo *TSI) { return TransformType(TSI); }
+ /// Returns true if it's safe to substitute \p Typedef with
+ /// \p OuterInstantiationArgs.
+ bool mightReferToOuterTemplateParameters(TypedefNameDecl *Typedef) {
+ if (!NestedPattern)
+ return false;
+
+ static auto WalkUp = [](DeclContext *DC, DeclContext *TargetDC) {
+ if (DC->Equals(TargetDC))
+ return true;
+ while (DC->isRecord()) {
+ if (DC->Equals(TargetDC))
+ return true;
+ DC = DC->getParent();
+ }
+ return false;
+ };
+
+ if (WalkUp(Typedef->getDeclContext(), NestedPattern->getTemplatedDecl()))
+ return true;
+ if (WalkUp(NestedPattern->getTemplatedDecl(), Typedef->getDeclContext()))
+ return true;
+ return false;
+ }
+
+ QualType
+ RebuildTemplateSpecializationType(TemplateName Template,
+ SourceLocation TemplateNameLoc,
+ TemplateArgumentListInfo &TemplateArgs) {
+ if (!OuterInstantiationArgs ||
+ !isa_and_present<TypeAliasTemplateDecl>(Template.getAsTemplateDecl()))
+ return Base::RebuildTemplateSpecializationType(Template, TemplateNameLoc,
+ TemplateArgs);
+
+ auto *TATD = cast<TypeAliasTemplateDecl>(Template.getAsTemplateDecl());
+ auto *Pattern = TATD;
+ while (Pattern->getInstantiatedFromMemberTemplate())
+ Pattern = Pattern->getInstantiatedFromMemberTemplate();
+ if (!mightReferToOuterTemplateParameters(Pattern->getTemplatedDecl()))
+ return Base::RebuildTemplateSpecializationType(Template, TemplateNameLoc,
+ TemplateArgs);
+
+ Decl *NewD =
+ TypedefNameInstantiator->InstantiateTypeAliasTemplateDecl(TATD);
+ if (!NewD)
+ return QualType();
+
+ auto *NewTATD = cast<TypeAliasTemplateDecl>(NewD);
+ MaterializedTypedefs.push_back(NewTATD->getTemplatedDecl());
+
+ return Base::RebuildTemplateSpecializationType(
+ TemplateName(NewTATD), TemplateNameLoc, TemplateArgs);
+ }
+
QualType TransformTypedefType(TypeLocBuilder &TLB, TypedefTypeLoc TL) {
ASTContext &Context = SemaRef.getASTContext();
TypedefNameDecl *OrigDecl = TL.getTypedefNameDecl();
TypedefNameDecl *Decl = OrigDecl;
// Transform the underlying type of the typedef and clone the Decl only if
// the typedef has a dependent context.
- if (OrigDecl->getDeclContext()->isDependentContext()) {
+ bool InDependentContext = OrigDecl->getDeclContext()->isDependentContext();
+
+ // A typedef/alias Decl within the NestedPattern may reference the outer
+ // template parameters. They're substituted with corresponding instantiation
+ // arguments here and in RebuildTemplateSpecializationType() above.
+ // Otherwise, we would have a CTAD guide with "dangling" template
+ // parameters.
+ // For example,
+ // template <class T> struct Outer {
+ // using Alias = S<T>;
+ // template <class U> struct Inner {
+ // Inner(Alias);
+ // };
+ // };
+ if (OuterInstantiationArgs && InDependentContext &&
+ TL.getTypePtr()->isInstantiationDependentType()) {
+ Decl = cast_if_present<TypedefNameDecl>(
+ TypedefNameInstantiator->InstantiateTypedefNameDecl(
+ OrigDecl, /*IsTypeAlias=*/isa<TypeAliasDecl>(OrigDecl)));
+ if (!Decl)
+ return QualType();
+ MaterializedTypedefs.push_back(Decl);
+ } else if (InDependentContext) {
TypeLocBuilder InnerTLB;
QualType Transformed =
TransformType(InnerTLB, OrigDecl->getTypeSourceInfo()->getTypeLoc());
@@ -2541,8 +2628,9 @@ struct ConvertConstructorToDeductionGuideTransform {
// defined outside of the surrounding class template. That is T in the
// above example.
if (NestedPattern) {
- NewParam = transformFunctionTypeParam(NewParam, OuterInstantiationArgs,
- MaterializedTypedefs);
+ NewParam = transformFunctionTypeParam(
+ NewParam, OuterInstantiationArgs, MaterializedTypedefs,
+ /*TransformingOuterPatterns=*/true);
if (!NewParam)
return QualType();
}
@@ -2550,7 +2638,8 @@ struct ConvertConstructorToDeductionGuideTransform {
// defined at the class template and the constructor. In this example,
// they're U and V, respectively.
NewParam =
- transformFunctionTypeParam(NewParam, Args, MaterializedTypedefs);
+ transformFunctionTypeParam(NewParam, Args, MaterializedTypedefs,
+ /*TransformingOuterPatterns=*/false);
if (!NewParam)
return QualType();
ParamTypes.push_back(NewParam->getType());
@@ -2594,7 +2683,8 @@ struct ConvertConstructorToDeductionGuideTransform {
ParmVarDecl *transformFunctionTypeParam(
ParmVarDecl *OldParam, MultiLevelTemplateArgumentList &Args,
- llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs) {
+ llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs,
+ bool TransformingOuterPatterns) {
TypeSourceInfo *OldDI = OldParam->getTypeSourceInfo();
TypeSourceInfo *NewDI;
if (auto PackTL = OldDI->getTypeLoc().getAs<PackExpansionTypeLoc>()) {
@@ -2617,7 +2707,9 @@ struct ConvertConstructorToDeductionGuideTransform {
// members of the current instantiations with the definitions of those
// typedefs, avoiding triggering instantiation of the deduced type during
// deduction.
- NewDI = ExtractTypeForDeductionGuide(SemaRef, MaterializedTypedefs)
+ NewDI = ExtractTypeForDeductionGuide(
+ SemaRef, MaterializedTypedefs, NestedPattern,
+ TransformingOuterPatterns ? &Args : nullptr)
.transform(NewDI);
// Resolving a wording defect, we also inherit default arguments from the
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 2e90f0c215b8d..88f6af80cbc55 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1096,8 +1096,8 @@ Decl *TemplateDeclInstantiator::VisitTypeAliasDecl(TypeAliasDecl *D) {
return Typedef;
}
-Decl *
-TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
+Decl *TemplateDeclInstantiator::InstantiateTypeAliasTemplateDecl(
+ TypeAliasTemplateDecl *D) {
// Create a local instantiation scope for this type alias template, which
// will contain the instantiations of the template parameters.
LocalInstantiationScope Scope(SemaRef);
@@ -1143,7 +1143,14 @@ TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
if (!PrevAliasTemplate)
Inst->setInstantiatedFromMemberTemplate(D);
- Owner->addDecl(Inst);
+ return Inst;
+}
+
+Decl *
+TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
+ Decl *Inst = InstantiateTypeAliasTemplateDecl(D);
+ if (Inst)
+ Owner->addDecl(Inst);
return Inst;
}
diff --git a/clang/test/SemaTemplate/nested-deduction-guides.cpp b/clang/test/SemaTemplate/nested-deduction-guides.cpp
index 38410b93ead3b..30ede69e96e3f 100644
--- a/clang/test/SemaTemplate/nested-deduction-guides.cpp
+++ b/clang/test/SemaTemplate/nested-deduction-guides.cpp
@@ -1,5 +1,4 @@
// RUN: %clang_cc1 -std=c++17 -verify %s
-// expected-no-diagnostics
template<typename T> struct A {
template<typename U> struct B {
@@ -16,3 +15,76 @@ using T = A<void>::B<int>;
using Copy = decltype(copy);
using Copy = A<void>::B<int>;
+
+namespace GH94614 {
+
+template <class, class> struct S {};
+
+struct trouble_1 {
+} constexpr t1;
+struct trouble_2 {
+} constexpr t2;
+struct trouble_3 {
+} constexpr t3;
+struct trouble_4 {
+} constexpr t4;
+struct trouble_5 {
+} constexpr t5;
+struct trouble_6 {
+} constexpr t6;
+struct trouble_7 {
+} constexpr t7;
+struct trouble_8 {
+} constexpr t8;
+struct trouble_9 {
+} constexpr t9;
+
+template <class U, class... T> struct Unrelated {
+ using Trouble = S<U, T...>;
+
+ template <class... V> using Trouble2 = S<V..., T...>;
+};
+
+template <class T, class U> struct Outer {
+ using Trouble = S<U, T>;
+
+ template <class V> using Trouble2 = S<V, T>;
+
+ template <class V> using Trouble3 = S<U, T>;
+
+ template <class V> struct Inner {
+ template <class W> struct Paranoid {
+ using Trouble4 = S<W, T>;
+
+ template <class... X> using Trouble5 = S<X..., T>;
+ };
+
+ Inner(trouble_1, V v, Trouble trouble) {}
+ Inner(trouble_2, V v, Trouble2<V> trouble) {}
+ Inner(trouble_3, V v, Trouble3<V> trouble) {}
+ Inner(trouble_4, V v, typename Unrelated<U, T>::template Trouble2<V> trouble) {}
+ Inner(trouble_5, V v, typename Unrelated<U, T>::Trouble trouble) {}
+ Inner(trouble_6, V v, typename Unrelated<V, T>::Trouble trouble) {}
+ Inner(trouble_7, V v, typename Paranoid<V>::Trouble4 trouble) {}
+ Inner(trouble_8, V v, typename Paranoid<V>::template Trouble5<V> trouble) {}
+ template <class W>
+ Inner(trouble_9, V v, W w, typename Paranoid<V>::template Trouble5<W> trouble) {}
+ };
+};
+
+S<int, char> s;
+
+Outer<char, int>::Inner _1(t1, 42, s);
+Outer<char, int>::Inner _2(t2, 42, s);
+Outer<char, int>::Inner _3(t3, 42, s);
+Outer<char, int>::Inner _4(t4, 42, s);
+Outer<char, int>::Inner _5(t5, 42, s);
+Outer<char, int>::Inner _6(t6, 42, s);
+Outer<char, int>::Inner _7(t7, 42, s);
+Outer<char, int>::Inner _8(t8, 42, s);
+Outer<char, int>::Inner _9(t9, 42, 24, s);
+
+// Make sure we don't accidentally inject the TypedefNameDecl into the TU.
+Trouble should_not_be_in_the_tu_decl; // expected-error {{unknown type name 'Trouble'}}
+
+} // namespace GH94614
More information about the cfe-commits
mailing list