Skip to content

Commit 869ac40

Browse files
authored
[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 #94614
1 parent d392335 commit 869ac40

File tree

5 files changed

+185
-12
lines changed

5 files changed

+185
-12
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,7 @@ Bug Fixes to C++ Support
10001000
evaluated to an integer. (#GH96670).
10011001
- Fixed a bug where references to lambda capture inside a ``noexcept`` specifier were not correctly
10021002
instantiated. (#GH95735).
1003+
- Fixed a CTAD substitution bug involving type aliases that reference outer template parameters. (#GH94614).
10031004

10041005
Bug Fixes to AST Handling
10051006
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Sema/Template.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,7 @@ enum class TemplateSubstitutionKind : char {
711711
VarTemplateSpecializationDecl *PrevDecl = nullptr);
712712

713713
Decl *InstantiateTypedefNameDecl(TypedefNameDecl *D, bool IsTypeAlias);
714+
Decl *InstantiateTypeAliasTemplateDecl(TypeAliasTemplateDecl *D);
714715
ClassTemplatePartialSpecializationDecl *
715716
InstantiateClassTemplatePartialSpecialization(
716717
ClassTemplateDecl *ClassTemplate,

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 100 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2178,23 +2178,110 @@ namespace {
21782178
class ExtractTypeForDeductionGuide
21792179
: public TreeTransform<ExtractTypeForDeductionGuide> {
21802180
llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs;
2181+
ClassTemplateDecl *NestedPattern;
2182+
const MultiLevelTemplateArgumentList *OuterInstantiationArgs;
2183+
std::optional<TemplateDeclInstantiator> TypedefNameInstantiator;
21812184

21822185
public:
21832186
typedef TreeTransform<ExtractTypeForDeductionGuide> Base;
21842187
ExtractTypeForDeductionGuide(
21852188
Sema &SemaRef,
2186-
llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs)
2187-
: Base(SemaRef), MaterializedTypedefs(MaterializedTypedefs) {}
2189+
llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs,
2190+
ClassTemplateDecl *NestedPattern,
2191+
const MultiLevelTemplateArgumentList *OuterInstantiationArgs)
2192+
: Base(SemaRef), MaterializedTypedefs(MaterializedTypedefs),
2193+
NestedPattern(NestedPattern),
2194+
OuterInstantiationArgs(OuterInstantiationArgs) {
2195+
if (OuterInstantiationArgs)
2196+
TypedefNameInstantiator.emplace(
2197+
SemaRef, SemaRef.getASTContext().getTranslationUnitDecl(),
2198+
*OuterInstantiationArgs);
2199+
}
21882200

21892201
TypeSourceInfo *transform(TypeSourceInfo *TSI) { return TransformType(TSI); }
21902202

2203+
/// Returns true if it's safe to substitute \p Typedef with
2204+
/// \p OuterInstantiationArgs.
2205+
bool mightReferToOuterTemplateParameters(TypedefNameDecl *Typedef) {
2206+
if (!NestedPattern)
2207+
return false;
2208+
2209+
static auto WalkUp = [](DeclContext *DC, DeclContext *TargetDC) {
2210+
if (DC->Equals(TargetDC))
2211+
return true;
2212+
while (DC->isRecord()) {
2213+
if (DC->Equals(TargetDC))
2214+
return true;
2215+
DC = DC->getParent();
2216+
}
2217+
return false;
2218+
};
2219+
2220+
if (WalkUp(Typedef->getDeclContext(), NestedPattern->getTemplatedDecl()))
2221+
return true;
2222+
if (WalkUp(NestedPattern->getTemplatedDecl(), Typedef->getDeclContext()))
2223+
return true;
2224+
return false;
2225+
}
2226+
2227+
QualType
2228+
RebuildTemplateSpecializationType(TemplateName Template,
2229+
SourceLocation TemplateNameLoc,
2230+
TemplateArgumentListInfo &TemplateArgs) {
2231+
if (!OuterInstantiationArgs ||
2232+
!isa_and_present<TypeAliasTemplateDecl>(Template.getAsTemplateDecl()))
2233+
return Base::RebuildTemplateSpecializationType(Template, TemplateNameLoc,
2234+
TemplateArgs);
2235+
2236+
auto *TATD = cast<TypeAliasTemplateDecl>(Template.getAsTemplateDecl());
2237+
auto *Pattern = TATD;
2238+
while (Pattern->getInstantiatedFromMemberTemplate())
2239+
Pattern = Pattern->getInstantiatedFromMemberTemplate();
2240+
if (!mightReferToOuterTemplateParameters(Pattern->getTemplatedDecl()))
2241+
return Base::RebuildTemplateSpecializationType(Template, TemplateNameLoc,
2242+
TemplateArgs);
2243+
2244+
Decl *NewD =
2245+
TypedefNameInstantiator->InstantiateTypeAliasTemplateDecl(TATD);
2246+
if (!NewD)
2247+
return QualType();
2248+
2249+
auto *NewTATD = cast<TypeAliasTemplateDecl>(NewD);
2250+
MaterializedTypedefs.push_back(NewTATD->getTemplatedDecl());
2251+
2252+
return Base::RebuildTemplateSpecializationType(
2253+
TemplateName(NewTATD), TemplateNameLoc, TemplateArgs);
2254+
}
2255+
21912256
QualType TransformTypedefType(TypeLocBuilder &TLB, TypedefTypeLoc TL) {
21922257
ASTContext &Context = SemaRef.getASTContext();
21932258
TypedefNameDecl *OrigDecl = TL.getTypedefNameDecl();
21942259
TypedefNameDecl *Decl = OrigDecl;
21952260
// Transform the underlying type of the typedef and clone the Decl only if
21962261
// the typedef has a dependent context.
2197-
if (OrigDecl->getDeclContext()->isDependentContext()) {
2262+
bool InDependentContext = OrigDecl->getDeclContext()->isDependentContext();
2263+
2264+
// A typedef/alias Decl within the NestedPattern may reference the outer
2265+
// template parameters. They're substituted with corresponding instantiation
2266+
// arguments here and in RebuildTemplateSpecializationType() above.
2267+
// Otherwise, we would have a CTAD guide with "dangling" template
2268+
// parameters.
2269+
// For example,
2270+
// template <class T> struct Outer {
2271+
// using Alias = S<T>;
2272+
// template <class U> struct Inner {
2273+
// Inner(Alias);
2274+
// };
2275+
// };
2276+
if (OuterInstantiationArgs && InDependentContext &&
2277+
TL.getTypePtr()->isInstantiationDependentType()) {
2278+
Decl = cast_if_present<TypedefNameDecl>(
2279+
TypedefNameInstantiator->InstantiateTypedefNameDecl(
2280+
OrigDecl, /*IsTypeAlias=*/isa<TypeAliasDecl>(OrigDecl)));
2281+
if (!Decl)
2282+
return QualType();
2283+
MaterializedTypedefs.push_back(Decl);
2284+
} else if (InDependentContext) {
21982285
TypeLocBuilder InnerTLB;
21992286
QualType Transformed =
22002287
TransformType(InnerTLB, OrigDecl->getTypeSourceInfo()->getTypeLoc());
@@ -2541,16 +2628,18 @@ struct ConvertConstructorToDeductionGuideTransform {
25412628
// defined outside of the surrounding class template. That is T in the
25422629
// above example.
25432630
if (NestedPattern) {
2544-
NewParam = transformFunctionTypeParam(NewParam, OuterInstantiationArgs,
2545-
MaterializedTypedefs);
2631+
NewParam = transformFunctionTypeParam(
2632+
NewParam, OuterInstantiationArgs, MaterializedTypedefs,
2633+
/*TransformingOuterPatterns=*/true);
25462634
if (!NewParam)
25472635
return QualType();
25482636
}
25492637
// Then, transform all the references to template parameters that are
25502638
// defined at the class template and the constructor. In this example,
25512639
// they're U and V, respectively.
25522640
NewParam =
2553-
transformFunctionTypeParam(NewParam, Args, MaterializedTypedefs);
2641+
transformFunctionTypeParam(NewParam, Args, MaterializedTypedefs,
2642+
/*TransformingOuterPatterns=*/false);
25542643
if (!NewParam)
25552644
return QualType();
25562645
ParamTypes.push_back(NewParam->getType());
@@ -2594,7 +2683,8 @@ struct ConvertConstructorToDeductionGuideTransform {
25942683

25952684
ParmVarDecl *transformFunctionTypeParam(
25962685
ParmVarDecl *OldParam, MultiLevelTemplateArgumentList &Args,
2597-
llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs) {
2686+
llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs,
2687+
bool TransformingOuterPatterns) {
25982688
TypeSourceInfo *OldDI = OldParam->getTypeSourceInfo();
25992689
TypeSourceInfo *NewDI;
26002690
if (auto PackTL = OldDI->getTypeLoc().getAs<PackExpansionTypeLoc>()) {
@@ -2617,7 +2707,9 @@ struct ConvertConstructorToDeductionGuideTransform {
26172707
// members of the current instantiations with the definitions of those
26182708
// typedefs, avoiding triggering instantiation of the deduced type during
26192709
// deduction.
2620-
NewDI = ExtractTypeForDeductionGuide(SemaRef, MaterializedTypedefs)
2710+
NewDI = ExtractTypeForDeductionGuide(
2711+
SemaRef, MaterializedTypedefs, NestedPattern,
2712+
TransformingOuterPatterns ? &Args : nullptr)
26212713
.transform(NewDI);
26222714

26232715
// Resolving a wording defect, we also inherit default arguments from the

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,8 +1096,8 @@ Decl *TemplateDeclInstantiator::VisitTypeAliasDecl(TypeAliasDecl *D) {
10961096
return Typedef;
10971097
}
10981098

1099-
Decl *
1100-
TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
1099+
Decl *TemplateDeclInstantiator::InstantiateTypeAliasTemplateDecl(
1100+
TypeAliasTemplateDecl *D) {
11011101
// Create a local instantiation scope for this type alias template, which
11021102
// will contain the instantiations of the template parameters.
11031103
LocalInstantiationScope Scope(SemaRef);
@@ -1143,7 +1143,14 @@ TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
11431143
if (!PrevAliasTemplate)
11441144
Inst->setInstantiatedFromMemberTemplate(D);
11451145

1146-
Owner->addDecl(Inst);
1146+
return Inst;
1147+
}
1148+
1149+
Decl *
1150+
TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
1151+
Decl *Inst = InstantiateTypeAliasTemplateDecl(D);
1152+
if (Inst)
1153+
Owner->addDecl(Inst);
11471154

11481155
return Inst;
11491156
}

clang/test/SemaTemplate/nested-deduction-guides.cpp

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// RUN: %clang_cc1 -std=c++17 -verify %s
2-
// expected-no-diagnostics
32

43
template<typename T> struct A {
54
template<typename U> struct B {
@@ -16,3 +15,76 @@ using T = A<void>::B<int>;
1615

1716
using Copy = decltype(copy);
1817
using Copy = A<void>::B<int>;
18+
19+
namespace GH94614 {
20+
21+
template <class, class> struct S {};
22+
23+
struct trouble_1 {
24+
} constexpr t1;
25+
struct trouble_2 {
26+
} constexpr t2;
27+
struct trouble_3 {
28+
} constexpr t3;
29+
struct trouble_4 {
30+
} constexpr t4;
31+
struct trouble_5 {
32+
} constexpr t5;
33+
struct trouble_6 {
34+
} constexpr t6;
35+
struct trouble_7 {
36+
} constexpr t7;
37+
struct trouble_8 {
38+
} constexpr t8;
39+
struct trouble_9 {
40+
} constexpr t9;
41+
42+
template <class U, class... T> struct Unrelated {
43+
using Trouble = S<U, T...>;
44+
45+
template <class... V> using Trouble2 = S<V..., T...>;
46+
};
47+
48+
template <class T, class U> struct Outer {
49+
using Trouble = S<U, T>;
50+
51+
template <class V> using Trouble2 = S<V, T>;
52+
53+
template <class V> using Trouble3 = S<U, T>;
54+
55+
template <class V> struct Inner {
56+
template <class W> struct Paranoid {
57+
using Trouble4 = S<W, T>;
58+
59+
template <class... X> using Trouble5 = S<X..., T>;
60+
};
61+
62+
Inner(trouble_1, V v, Trouble trouble) {}
63+
Inner(trouble_2, V v, Trouble2<V> trouble) {}
64+
Inner(trouble_3, V v, Trouble3<V> trouble) {}
65+
Inner(trouble_4, V v, typename Unrelated<U, T>::template Trouble2<V> trouble) {}
66+
Inner(trouble_5, V v, typename Unrelated<U, T>::Trouble trouble) {}
67+
Inner(trouble_6, V v, typename Unrelated<V, T>::Trouble trouble) {}
68+
Inner(trouble_7, V v, typename Paranoid<V>::Trouble4 trouble) {}
69+
Inner(trouble_8, V v, typename Paranoid<V>::template Trouble5<V> trouble) {}
70+
template <class W>
71+
Inner(trouble_9, V v, W w, typename Paranoid<V>::template Trouble5<W> trouble) {}
72+
};
73+
};
74+
75+
S<int, char> s;
76+
77+
Outer<char, int>::Inner _1(t1, 42, s);
78+
Outer<char, int>::Inner _2(t2, 42, s);
79+
Outer<char, int>::Inner _3(t3, 42, s);
80+
Outer<char, int>::Inner _4(t4, 42, s);
81+
Outer<char, int>::Inner _5(t5, 42, s);
82+
Outer<char, int>::Inner _6(t6, 42, s);
83+
Outer<char, int>::Inner _7(t7, 42, s);
84+
Outer<char, int>::Inner _8(t8, 42, s);
85+
Outer<char, int>::Inner _9(t9, 42, 24, s);
86+
87+
// Make sure we don't accidentally inject the TypedefNameDecl into the TU.
88+
Trouble should_not_be_in_the_tu_decl; // expected-error {{unknown type name 'Trouble'}}
89+
90+
} // namespace GH94614

0 commit comments

Comments
 (0)