Skip to content

Commit 032ad59

Browse files
authored
[Clang] Fix various bugs in alias CTAD transform (llvm#132061)
1 parent 2186199 commit 032ad59

File tree

4 files changed

+230
-51
lines changed

4 files changed

+230
-51
lines changed

clang/lib/Sema/SemaTemplateDeductionGuide.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,12 +1072,27 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
10721072
AliasRhsTemplateArgs, TDeduceInfo, DeduceResults,
10731073
/*NumberOfArgumentsMustMatch=*/false);
10741074

1075+
static std::function<bool(const TemplateArgument &TA)> IsNonDeducedArgument =
1076+
[](const TemplateArgument &TA) {
1077+
// The following cases indicate the template argument is non-deducible:
1078+
// 1. The result is null. E.g. When it comes from a default template
1079+
// argument that doesn't appear in the alias declaration.
1080+
// 2. The template parameter is a pack and that cannot be deduced from
1081+
// the arguments within the alias declaration.
1082+
// Non-deducible template parameters will persist in the transformed
1083+
// deduction guide.
1084+
return TA.isNull() ||
1085+
(TA.getKind() == TemplateArgument::Pack &&
1086+
llvm::any_of(TA.pack_elements(), IsNonDeducedArgument));
1087+
};
1088+
10751089
SmallVector<TemplateArgument> DeducedArgs;
10761090
SmallVector<unsigned> NonDeducedTemplateParamsInFIndex;
10771091
// !!NOTE: DeduceResults respects the sequence of template parameters of
10781092
// the deduction guide f.
10791093
for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) {
1080-
if (const auto &D = DeduceResults[Index]; !D.isNull()) // Deduced
1094+
const auto &D = DeduceResults[Index];
1095+
if (!IsNonDeducedArgument(D))
10811096
DeducedArgs.push_back(D);
10821097
else
10831098
NonDeducedTemplateParamsInFIndex.push_back(Index);
@@ -1141,7 +1156,7 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
11411156
Args.addOuterTemplateArguments(TransformedDeducedAliasArgs);
11421157
for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) {
11431158
const auto &D = DeduceResults[Index];
1144-
if (D.isNull()) {
1159+
if (IsNonDeducedArgument(D)) {
11451160
// 2): Non-deduced template parameters would be substituted later.
11461161
continue;
11471162
}

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,6 +1348,16 @@ std::optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
13481348
return std::nullopt;
13491349
}
13501350

1351+
static TemplateArgument
1352+
getPackSubstitutedTemplateArgument(Sema &S, TemplateArgument Arg) {
1353+
assert(S.ArgumentPackSubstitutionIndex >= 0);
1354+
assert(S.ArgumentPackSubstitutionIndex < (int)Arg.pack_size());
1355+
Arg = Arg.pack_begin()[S.ArgumentPackSubstitutionIndex];
1356+
if (Arg.isPackExpansion())
1357+
Arg = Arg.getPackExpansionPattern();
1358+
return Arg;
1359+
}
1360+
13511361
//===----------------------------------------------------------------------===/
13521362
// Template Instantiation for Types
13531363
//===----------------------------------------------------------------------===/
@@ -1467,11 +1477,13 @@ namespace {
14671477
}
14681478
}
14691479

1470-
static TemplateArgument
1480+
TemplateArgument
14711481
getTemplateArgumentPackPatternForRewrite(const TemplateArgument &TA) {
14721482
if (TA.getKind() != TemplateArgument::Pack)
14731483
return TA;
1474-
assert(TA.pack_size() == 1 &&
1484+
if (SemaRef.ArgumentPackSubstitutionIndex != -1)
1485+
return getPackSubstitutedTemplateArgument(SemaRef, TA);
1486+
assert(TA.pack_size() == 1 && TA.pack_begin()->isPackExpansion() &&
14751487
"unexpected pack arguments in template rewrite");
14761488
TemplateArgument Arg = *TA.pack_begin();
14771489
if (Arg.isPackExpansion())
@@ -1632,6 +1644,9 @@ namespace {
16321644
std::vector<TemplateArgument> TArgs;
16331645
switch (Arg.getKind()) {
16341646
case TemplateArgument::Pack:
1647+
assert(SemaRef.CodeSynthesisContexts.empty() ||
1648+
SemaRef.CodeSynthesisContexts.back().Kind ==
1649+
Sema::CodeSynthesisContext::BuildingDeductionGuides);
16351650
// Literally rewrite the template argument pack, instead of unpacking
16361651
// it.
16371652
for (auto &pack : Arg.getPackAsArray()) {
@@ -1652,6 +1667,23 @@ namespace {
16521667
return inherited::TransformTemplateArgument(Input, Output, Uneval);
16531668
}
16541669

1670+
std::optional<unsigned> ComputeSizeOfPackExprWithoutSubstitution(
1671+
ArrayRef<TemplateArgument> PackArgs) {
1672+
// Don't do this when rewriting template parameters for CTAD:
1673+
// 1) The heuristic needs the unpacked Subst* nodes to figure out the
1674+
// expanded size, but this never applies since Subst* nodes are not
1675+
// created in rewrite scenarios.
1676+
//
1677+
// 2) The heuristic substitutes into the pattern with pack expansion
1678+
// suppressed, which does not meet the requirements for argument
1679+
// rewriting when template arguments include a non-pack matching against
1680+
// a pack, particularly when rewriting an alias CTAD.
1681+
if (TemplateArgs.isRewrite())
1682+
return std::nullopt;
1683+
1684+
return inherited::ComputeSizeOfPackExprWithoutSubstitution(PackArgs);
1685+
}
1686+
16551687
template<typename Fn>
16561688
QualType TransformFunctionProtoType(TypeLocBuilder &TLB,
16571689
FunctionProtoTypeLoc TL,
@@ -1871,16 +1903,6 @@ bool TemplateInstantiator::AlreadyTransformed(QualType T) {
18711903
return true;
18721904
}
18731905

1874-
static TemplateArgument
1875-
getPackSubstitutedTemplateArgument(Sema &S, TemplateArgument Arg) {
1876-
assert(S.ArgumentPackSubstitutionIndex >= 0);
1877-
assert(S.ArgumentPackSubstitutionIndex < (int)Arg.pack_size());
1878-
Arg = Arg.pack_begin()[S.ArgumentPackSubstitutionIndex];
1879-
if (Arg.isPackExpansion())
1880-
Arg = Arg.getPackExpansionPattern();
1881-
return Arg;
1882-
}
1883-
18841906
Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
18851907
if (!D)
18861908
return nullptr;

clang/lib/Sema/TreeTransform.h

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3663,6 +3663,9 @@ class TreeTransform {
36633663
return SemaRef.BuildCXXNoexceptExpr(Range.getBegin(), Arg, Range.getEnd());
36643664
}
36653665

3666+
std::optional<unsigned>
3667+
ComputeSizeOfPackExprWithoutSubstitution(ArrayRef<TemplateArgument> PackArgs);
3668+
36663669
/// Build a new expression to compute the length of a parameter pack.
36673670
ExprResult RebuildSizeOfPackExpr(SourceLocation OperatorLoc, NamedDecl *Pack,
36683671
SourceLocation PackLoc,
@@ -16031,6 +16034,49 @@ TreeTransform<Derived>::TransformPackExpansionExpr(PackExpansionExpr *E) {
1603116034
E->getNumExpansions());
1603216035
}
1603316036

16037+
template <typename Derived>
16038+
std::optional<unsigned>
16039+
TreeTransform<Derived>::ComputeSizeOfPackExprWithoutSubstitution(
16040+
ArrayRef<TemplateArgument> PackArgs) {
16041+
std::optional<unsigned> Result = 0;
16042+
for (const TemplateArgument &Arg : PackArgs) {
16043+
if (!Arg.isPackExpansion()) {
16044+
Result = *Result + 1;
16045+
continue;
16046+
}
16047+
16048+
TemplateArgumentLoc ArgLoc;
16049+
InventTemplateArgumentLoc(Arg, ArgLoc);
16050+
16051+
// Find the pattern of the pack expansion.
16052+
SourceLocation Ellipsis;
16053+
std::optional<unsigned> OrigNumExpansions;
16054+
TemplateArgumentLoc Pattern =
16055+
getSema().getTemplateArgumentPackExpansionPattern(ArgLoc, Ellipsis,
16056+
OrigNumExpansions);
16057+
16058+
// Substitute under the pack expansion. Do not expand the pack (yet).
16059+
TemplateArgumentLoc OutPattern;
16060+
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), -1);
16061+
if (getDerived().TransformTemplateArgument(Pattern, OutPattern,
16062+
/*Uneval*/ true))
16063+
return true;
16064+
16065+
// See if we can determine the number of arguments from the result.
16066+
std::optional<unsigned> NumExpansions =
16067+
getSema().getFullyPackExpandedSize(OutPattern.getArgument());
16068+
if (!NumExpansions) {
16069+
// No: we must be in an alias template expansion, and we're going to
16070+
// need to actually expand the packs.
16071+
Result = std::nullopt;
16072+
break;
16073+
}
16074+
16075+
Result = *Result + *NumExpansions;
16076+
}
16077+
return Result;
16078+
}
16079+
1603416080
template<typename Derived>
1603516081
ExprResult
1603616082
TreeTransform<Derived>::TransformSizeOfPackExpr(SizeOfPackExpr *E) {
@@ -16096,42 +16142,8 @@ TreeTransform<Derived>::TransformSizeOfPackExpr(SizeOfPackExpr *E) {
1609616142
}
1609716143

1609816144
// Try to compute the result without performing a partial substitution.
16099-
std::optional<unsigned> Result = 0;
16100-
for (const TemplateArgument &Arg : PackArgs) {
16101-
if (!Arg.isPackExpansion()) {
16102-
Result = *Result + 1;
16103-
continue;
16104-
}
16105-
16106-
TemplateArgumentLoc ArgLoc;
16107-
InventTemplateArgumentLoc(Arg, ArgLoc);
16108-
16109-
// Find the pattern of the pack expansion.
16110-
SourceLocation Ellipsis;
16111-
std::optional<unsigned> OrigNumExpansions;
16112-
TemplateArgumentLoc Pattern =
16113-
getSema().getTemplateArgumentPackExpansionPattern(ArgLoc, Ellipsis,
16114-
OrigNumExpansions);
16115-
16116-
// Substitute under the pack expansion. Do not expand the pack (yet).
16117-
TemplateArgumentLoc OutPattern;
16118-
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), -1);
16119-
if (getDerived().TransformTemplateArgument(Pattern, OutPattern,
16120-
/*Uneval*/ true))
16121-
return true;
16122-
16123-
// See if we can determine the number of arguments from the result.
16124-
std::optional<unsigned> NumExpansions =
16125-
getSema().getFullyPackExpandedSize(OutPattern.getArgument());
16126-
if (!NumExpansions) {
16127-
// No: we must be in an alias template expansion, and we're going to need
16128-
// to actually expand the packs.
16129-
Result = std::nullopt;
16130-
break;
16131-
}
16132-
16133-
Result = *Result + *NumExpansions;
16134-
}
16145+
std::optional<unsigned> Result =
16146+
getDerived().ComputeSizeOfPackExprWithoutSubstitution(PackArgs);
1613516147

1613616148
// Common case: we could determine the number of expansions without
1613716149
// substituting.

clang/test/SemaCXX/ctad.cpp

Lines changed: 131 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// RUN: %clang_cc1 -fsyntax-only -verify -Wno-unused-value -std=c++20 %s
2-
// expected-no-diagnostics
32

43
namespace GH64347 {
54

@@ -17,3 +16,134 @@ void k() {
1716
}
1817

1918
} // namespace GH64347
19+
20+
namespace GH123591 {
21+
22+
23+
template < typename... _Types >
24+
struct variant {
25+
template <int N = sizeof...(_Types)>
26+
variant(_Types...);
27+
};
28+
29+
template <class T>
30+
using AstNode = variant<T, T, T>;
31+
32+
AstNode tree(42, 43, 44);
33+
34+
}
35+
36+
namespace GH123591_2 {
37+
38+
template <int>
39+
using enable_if_t = char;
40+
41+
template < typename... Types >
42+
struct variant {
43+
template < enable_if_t<sizeof...(Types)>>
44+
variant();
45+
};
46+
47+
template <int>
48+
using AstNode = variant<>;
49+
// expected-note@-1 {{couldn't infer template argument ''}} \
50+
// expected-note@-1 2{{implicit deduction guide declared as}} \
51+
// expected-note@-1 {{candidate function template not viable}}
52+
53+
54+
AstNode tree; // expected-error {{no viable constructor or deduction guide}}
55+
56+
}
57+
58+
namespace GH127539 {
59+
60+
template <class...>
61+
struct A {
62+
template <class... ArgTs>
63+
A(ArgTs...) {}
64+
};
65+
66+
template <class... ArgTs>
67+
A(ArgTs...) -> A<typename ArgTs::value_type...>;
68+
69+
template <class... Ts>
70+
using AA = A<Ts..., Ts...>;
71+
72+
AA a{};
73+
74+
}
75+
76+
namespace GH129077 {
77+
78+
using size_t = decltype(sizeof(0));
79+
80+
struct index_type
81+
{
82+
size_t value{~0ull};
83+
index_type() = default;
84+
constexpr index_type(size_t i) noexcept : value(i) {}
85+
};
86+
87+
template <index_type... Extents>
88+
struct extents
89+
{
90+
constexpr extents(decltype(Extents)...) noexcept {}
91+
};
92+
93+
template <class... Extents>
94+
extents(Extents...) -> extents<(requires { Extents::value; } ? Extents{} : ~0ull)...>;
95+
96+
template <index_type... Index>
97+
using index = extents<Index...>;
98+
99+
int main()
100+
{
101+
extents i{0,0};
102+
auto j = extents<64,{}>({}, 42);
103+
104+
index k{0,0};
105+
auto l = index<64,{}>({}, 42);
106+
107+
return 0;
108+
}
109+
110+
}
111+
112+
namespace GH129620 {
113+
114+
template <class... Ts>
115+
struct A {
116+
constexpr A(Ts...) {}
117+
};
118+
119+
template <class... Ts>
120+
using Foo = A<Ts...>;
121+
122+
template <class T>
123+
using Bar = Foo<T, T>;
124+
125+
Bar a{0, 0};
126+
127+
}
128+
129+
namespace GH129998 {
130+
131+
struct converible_to_one {
132+
constexpr operator int() const noexcept { return 1; }
133+
};
134+
135+
template <int... Extents>
136+
struct class_template {
137+
class_template() = default;
138+
constexpr class_template(auto&&...) noexcept {}
139+
};
140+
141+
template <class... Extents>
142+
class_template(Extents...) -> class_template<(true ? 0 : +Extents{})...>;
143+
144+
template <int... Extents>
145+
using alias_template = class_template<Extents...>;
146+
147+
alias_template var2{converible_to_one{}, 2};
148+
149+
}

0 commit comments

Comments
 (0)