Skip to content

Commit 874067a

Browse files
zyn0217ilya-biryukovcor3ntin
authored
[Sema] Preserve ContainsUnexpandedParameterPack in TransformLambdaExpr (llvm#86265)
The lambda `ContainsUnexpandedParameterPack` flag is used for the expressions' dependency computing and is therefore essential for pack expansion. We previously lost the flag's preservation during the lambda's transform, which caused some issues, e.g. a fold expression couldn't properly expand inside a template. This patch alleviates the issue by retaining the flag in more scenarios. Note that we still have problems with constraints involving packs regarding lambdas, and dealing with that would take more effort, and we'd like to fix them in the future. Fixes llvm#56852 Fixes llvm#85667 Mitigates llvm#99877 because the attributes were not handled in this patch. --------- Co-authored-by: Ilya Biryukov <[email protected]> Co-authored-by: cor3ntin <[email protected]>
1 parent 33fc322 commit 874067a

File tree

5 files changed

+240
-15
lines changed

5 files changed

+240
-15
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ Bug Fixes to C++ Support
186186
substitutions in concepts, so it doesn't incorrectly complain of missing
187187
module imports in those situations. (#GH60336)
188188
- Fix init-capture packs having a size of one before being instantiated. (#GH63677)
189+
- Clang now preserves the unexpanded flag in a lambda transform used for pack expansion. (#GH56852), (#GH85667),
190+
(#GH99877).
189191

190192
Bug Fixes to AST Handling
191193
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/lib/Sema/SemaLambda.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,8 @@ void Sema::CompleteLambdaCallOperator(
10211021
getGenericLambdaTemplateParameterList(LSI, *this);
10221022

10231023
DeclContext *DC = Method->getLexicalDeclContext();
1024+
// DeclContext::addDecl() assumes that the DeclContext we're adding to is the
1025+
// lexical context of the Method. Do so.
10241026
Method->setLexicalDeclContext(LSI->Lambda);
10251027
if (TemplateParams) {
10261028
FunctionTemplateDecl *TemplateMethod =
@@ -1105,6 +1107,8 @@ void Sema::ActOnLambdaExpressionAfterIntroducer(LambdaIntroducer &Intro,
11051107

11061108
CXXMethodDecl *Method = CreateLambdaCallOperator(Intro.Range, Class);
11071109
LSI->CallOperator = Method;
1110+
// Temporarily set the lexical declaration context to the current
1111+
// context, so that the Scope stack matches the lexical nesting.
11081112
Method->setLexicalDeclContext(CurContext);
11091113

11101114
PushDeclContext(CurScope, Method);

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "llvm/ADT/STLForwardCompat.h"
4040
#include "llvm/ADT/StringExtras.h"
4141
#include "llvm/Support/ErrorHandling.h"
42+
#include "llvm/Support/SaveAndRestore.h"
4243
#include "llvm/Support/TimeProfiler.h"
4344
#include <optional>
4445

@@ -1657,11 +1658,12 @@ namespace {
16571658
LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
16581659
Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this);
16591660

1660-
ExprResult Result = inherited::TransformLambdaExpr(E);
1661-
if (Result.isInvalid())
1662-
return Result;
1661+
return inherited::TransformLambdaExpr(E);
1662+
}
16631663

1664-
CXXMethodDecl *MD = Result.getAs<LambdaExpr>()->getCallOperator();
1664+
ExprResult RebuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
1665+
LambdaScopeInfo *LSI) {
1666+
CXXMethodDecl *MD = LSI->CallOperator;
16651667
for (ParmVarDecl *PVD : MD->parameters()) {
16661668
assert(PVD && "null in a parameter list");
16671669
if (!PVD->hasDefaultArg())
@@ -1680,8 +1682,7 @@ namespace {
16801682
PVD->setDefaultArg(ErrorResult.get());
16811683
}
16821684
}
1683-
1684-
return Result;
1685+
return inherited::RebuildLambdaExpr(StartLoc, EndLoc, LSI);
16851686
}
16861687

16871688
StmtResult TransformLambdaBody(LambdaExpr *E, Stmt *Body) {
@@ -1694,11 +1695,8 @@ namespace {
16941695
// `true` to temporarily fix this issue.
16951696
// FIXME: This temporary fix can be removed after fully implementing
16961697
// p0588r1.
1697-
bool Prev = EvaluateConstraints;
1698-
EvaluateConstraints = true;
1699-
StmtResult Stmt = inherited::TransformLambdaBody(E, Body);
1700-
EvaluateConstraints = Prev;
1701-
return Stmt;
1698+
llvm::SaveAndRestore _(EvaluateConstraints, true);
1699+
return inherited::TransformLambdaBody(E, Body);
17021700
}
17031701

17041702
ExprResult TransformRequiresExpr(RequiresExpr *E) {

clang/lib/Sema/TreeTransform.h

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4028,6 +4028,20 @@ class TreeTransform {
40284028
NumExpansions);
40294029
}
40304030

4031+
ExprResult RebuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
4032+
LambdaScopeInfo *LSI) {
4033+
for (ParmVarDecl *PVD : LSI->CallOperator->parameters()) {
4034+
if (Expr *Init = PVD->getInit())
4035+
LSI->ContainsUnexpandedParameterPack |=
4036+
Init->containsUnexpandedParameterPack();
4037+
else if (PVD->hasUninstantiatedDefaultArg())
4038+
LSI->ContainsUnexpandedParameterPack |=
4039+
PVD->getUninstantiatedDefaultArg()
4040+
->containsUnexpandedParameterPack();
4041+
}
4042+
return getSema().BuildLambdaExpr(StartLoc, EndLoc, LSI);
4043+
}
4044+
40314045
/// Build an empty C++1z fold-expression with the given operator.
40324046
///
40334047
/// By default, produces the fallback value for the fold-expression, or
@@ -8284,6 +8298,7 @@ StmtResult
82848298
TreeTransform<Derived>::TransformDeclStmt(DeclStmt *S) {
82858299
bool DeclChanged = false;
82868300
SmallVector<Decl *, 4> Decls;
8301+
LambdaScopeInfo *LSI = getSema().getCurLambda();
82878302
for (auto *D : S->decls()) {
82888303
Decl *Transformed = getDerived().TransformDefinition(D->getLocation(), D);
82898304
if (!Transformed)
@@ -8292,6 +8307,15 @@ TreeTransform<Derived>::TransformDeclStmt(DeclStmt *S) {
82928307
if (Transformed != D)
82938308
DeclChanged = true;
82948309

8310+
if (LSI && isa<TypeDecl>(Transformed))
8311+
LSI->ContainsUnexpandedParameterPack |=
8312+
getSema()
8313+
.getASTContext()
8314+
.getTypeDeclType(cast<TypeDecl>(Transformed))
8315+
.getCanonicalType()
8316+
.getTypePtr()
8317+
->containsUnexpandedParameterPack();
8318+
82958319
Decls.push_back(Transformed);
82968320
}
82978321

@@ -14523,7 +14547,6 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
1452314547

1452414548
CXXMethodDecl *NewCallOperator =
1452514549
getSema().CreateLambdaCallOperator(E->getIntroducerRange(), Class);
14526-
NewCallOperator->setLexicalDeclContext(getSema().CurContext);
1452714550

1452814551
// Enter the scope of the lambda.
1452914552
getSema().buildLambdaScope(LSI, NewCallOperator, E->getIntroducerRange(),
@@ -14591,6 +14614,13 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
1459114614
}
1459214615
NewVDs.push_back(NewVD);
1459314616
getSema().addInitCapture(LSI, NewVD, C->getCaptureKind() == LCK_ByRef);
14617+
// Cases we want to tackle:
14618+
// ([C(Pack)] {}, ...)
14619+
// But rule out cases e.g.
14620+
// [...C = Pack()] {}
14621+
if (NewC.EllipsisLoc.isInvalid())
14622+
LSI->ContainsUnexpandedParameterPack |=
14623+
Init.get()->containsUnexpandedParameterPack();
1459414624
}
1459514625

1459614626
if (Invalid)
@@ -14658,6 +14688,11 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
1465814688
continue;
1465914689
}
1466014690

14691+
// This is not an init-capture; however it contains an unexpanded pack e.g.
14692+
// ([Pack] {}(), ...)
14693+
if (auto *VD = dyn_cast<VarDecl>(CapturedVar); VD && !C->isPackExpansion())
14694+
LSI->ContainsUnexpandedParameterPack |= VD->isParameterPack();
14695+
1466114696
// Capture the transformed variable.
1466214697
getSema().tryCaptureVariable(CapturedVar, C->getLocation(), Kind,
1466314698
EllipsisLoc);
@@ -14669,9 +14704,12 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
1466914704
auto TPL = getDerived().TransformTemplateParameterList(
1467014705
E->getTemplateParameterList());
1467114706
LSI->GLTemplateParameterList = TPL;
14672-
if (TPL)
14707+
if (TPL) {
1467314708
getSema().AddTemplateParametersToLambdaCallOperator(NewCallOperator, Class,
1467414709
TPL);
14710+
LSI->ContainsUnexpandedParameterPack |=
14711+
TPL->containsUnexpandedParameterPack();
14712+
}
1467514713

1467614714
// Transform the type of the original lambda's call operator.
1467714715
// The transformation MUST be done in the CurrentInstantiationScope since
@@ -14710,6 +14748,8 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
1471014748

1471114749
if (NewCallOpType.isNull())
1471214750
return ExprError();
14751+
LSI->ContainsUnexpandedParameterPack |=
14752+
NewCallOpType->containsUnexpandedParameterPack();
1471314753
NewCallOpTSI =
1471414754
NewCallOpTLBuilder.getTypeSourceInfo(getSema().Context, NewCallOpType);
1471514755
}
@@ -14824,8 +14864,8 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
1482414864
Class->setTypeForDecl(nullptr);
1482514865
getSema().Context.getTypeDeclType(Class);
1482614866

14827-
return getSema().BuildLambdaExpr(E->getBeginLoc(), Body.get()->getEndLoc(),
14828-
&LSICopy);
14867+
return getDerived().RebuildLambdaExpr(E->getBeginLoc(),
14868+
Body.get()->getEndLoc(), &LSICopy);
1482914869
}
1483014870

1483114871
template<typename Derived>
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
2+
3+
namespace GH85667 {
4+
5+
template <class T>
6+
struct identity {
7+
using type = T;
8+
};
9+
10+
template <class = void> void f() {
11+
12+
static_assert([]<class... Is>(Is... x) {
13+
return ([I(x)] {
14+
return I;
15+
}() + ...);
16+
}(1, 2) == 3);
17+
18+
[]<class... Is>(Is... x) {
19+
return ([](auto y = Is()) { return y + 1; }() + ...); // expected-error {{no matching function}} \
20+
// expected-note {{couldn't infer template argument 'y:auto'}} \
21+
// expected-note@-1 {{requested here}}
22+
// expected-note@#instantiate-f {{requested here}}
23+
}(1);
24+
25+
[]<class... Is>() {
26+
([]<class = Is>(Is)
27+
noexcept(bool(Is()))
28+
{}(Is()),
29+
...);
30+
}.template operator()<char, int, float>();
31+
32+
static_assert(__is_same(decltype([]<class... Is>() {
33+
return ([]() -> decltype(Is()) { return {}; }(),
34+
...);
35+
}.template operator()<int, char>()),
36+
char));
37+
38+
[]<class... Is>() {
39+
return ([]<class... Ts>() -> decltype(Is()) { return Ts(); }() + ...);
40+
// expected-error@-1 {{unexpanded parameter pack 'Ts'}}
41+
}.template operator()<int, int>();
42+
43+
// https://github.com/llvm/llvm-project/issues/56852
44+
[]<class... Is>(Is...) {
45+
([] {
46+
using T = identity<Is>::type;
47+
}(), ...);
48+
}(1, 2);
49+
50+
[](auto ...y) {
51+
([y] { }(), ...);
52+
}();
53+
54+
[](auto ...x) {
55+
([&](auto ...y) {
56+
([x..., y] { }(), ...);
57+
})(1);
58+
}(2, 'b');
59+
60+
#if 0
61+
// FIXME: https://github.com/llvm/llvm-project/issues/18873
62+
[](auto ...x) { // #1
63+
([&](auto ...y) { // #2
64+
([x, y] { }(), ...); // #3
65+
})(1, 'a'); // #4
66+
}(2, 'b'); // #5
67+
68+
// We run into another crash for the above lambda because of the absence of a
69+
// mechanism that rebuilds an unexpanded pack from an expanded Decls.
70+
//
71+
// Basically, this happens after `x` at #1 being expanded when the template
72+
// arguments at #5, deduced as <int, char>, are ready. When we want to
73+
// instantiate the body of #1, we first instantiate the CallExpr at #4, which
74+
// boils down to the lambda's instantiation at #2. To that end, we have to
75+
// instantiate the body of it, which turns out to be #3. #3 is a CXXFoldExpr,
76+
// and we immediately have to hold off on the expansion because we don't have
77+
// corresponding template arguments (arguments at #4 are not transformed yet) for it.
78+
// Therefore, we want to rebuild a CXXFoldExpr, which requires another pattern
79+
// transformation of the lambda inside #3. Then we need to find an unexpanded form
80+
// of such a Decl of x at the time of transforming the capture, which is impossible
81+
// because the instantiated form has been expanded at #1!
82+
83+
[](auto ...x) { // #outer
84+
([&](auto ...y) { // #inner
85+
([x, y] { }(), ...);
86+
// expected-error@-1 {{parameter pack 'y' that has a different length (4 vs. 3) from outer parameter packs}}
87+
// expected-note-re@#inner {{function template specialization {{.*}} requested here}}
88+
// expected-note-re@#outer {{function template specialization {{.*}} requested here}}
89+
// expected-note-re@#instantiate-f {{function template specialization {{.*}} requested here}}
90+
})('a', 'b', 'c');
91+
}(0, 1, 2, 3);
92+
#endif
93+
}
94+
95+
template void f(); // #instantiate-f
96+
97+
} // namespace GH85667
98+
99+
namespace GH99877 {
100+
101+
struct tuple {
102+
int x[3];
103+
};
104+
105+
template <class F> int apply(F f, tuple v) { return f(v.x[0], v.x[1], v.x[2]); }
106+
107+
int Cartesian1(auto x, auto y) {
108+
return apply(
109+
[&](auto... xs) {
110+
return (apply([xs](auto... ys) { return (ys + ...); }, y) + ...);
111+
},
112+
x);
113+
}
114+
115+
int Cartesian2(auto x, auto y) {
116+
return apply(
117+
[&](auto... xs) {
118+
return (apply([zs = xs](auto... ys) { return (ys + ...); }, y) + ...);
119+
},
120+
x);
121+
}
122+
123+
template <int...> struct Ints {};
124+
template <int> struct Choose {
125+
template <class> struct Templ;
126+
};
127+
template <int... x> int Cartesian3(auto y) {
128+
return [&]<int... xs>(Ints<xs...>) {
129+
// check in default template arguments for
130+
// - type template parameters,
131+
(void)(apply([]<class = decltype(xs)>(auto... ys) { return (ys + ...); },
132+
y) +
133+
...);
134+
// - template template parameters.
135+
(void)(apply([]<template <class> class = Choose<xs>::template Templ>(
136+
auto... ys) { return (ys + ...); },
137+
y) +
138+
...);
139+
// - non-type template parameters,
140+
return (apply([]<int = xs>(auto... ys) { return (ys + ...); }, y) + ...);
141+
}(Ints<x...>());
142+
}
143+
144+
template <int... x> int Cartesian4(auto y) {
145+
return [&]<int... xs>(Ints<xs...>) {
146+
return (
147+
apply([]<decltype(xs) xx = 1>(auto... ys) { return (ys + ...); }, y) +
148+
...);
149+
}(Ints<x...>());
150+
}
151+
152+
// FIXME: Attributes should preserve the ContainsUnexpandedPack flag.
153+
#if 0
154+
155+
int Cartesian5(auto x, auto y) {
156+
return apply(
157+
[&](auto... xs) {
158+
return (apply([](auto... ys) __attribute__((
159+
diagnose_if(!__is_same(decltype(xs), int), "message",
160+
"error"))) { return (ys + ...); },
161+
y) +
162+
...);
163+
},
164+
x);
165+
}
166+
167+
#endif
168+
169+
void foo() {
170+
auto x = tuple({1, 2, 3});
171+
auto y = tuple({4, 5, 6});
172+
Cartesian1(x, y);
173+
Cartesian2(x, y);
174+
Cartesian3<1, 2, 3>(y);
175+
Cartesian4<1, 2, 3>(y);
176+
#if 0
177+
Cartesian5(x, y);
178+
#endif
179+
}
180+
181+
} // namespace GH99877

0 commit comments

Comments
 (0)