-
Notifications
You must be signed in to change notification settings - Fork 13.7k
[Sema] Preserve ContainsUnexpandedParameterPack in TransformLambdaExpr #86265
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
6e7b38b
[Sema] Preserve ContainsUnexpandedParameterPack in TransformLambdaExpr
zyn0217 e76fedd
Consider the captured pack variables - gh18873
zyn0217 cc74f6d
Merge branch 'main' into GH85667
zyn0217 faf5d87
comments
zyn0217 c5765ec
format
zyn0217 efb1e72
Merge branch 'main' into GH85667
zyn0217 6c2d311
Retain the 'unexpanded' Decls
zyn0217 1ac5ca7
Update ReleaseNotes
zyn0217 db36bc8
Cleanup & silence the unused-expression warning
zyn0217 66acffb
Fix the CI
zyn0217 5285c10
Fix typos
zyn0217 b336807
Merge branch 'main' into GH85667
zyn0217 b2d2836
Fixup
zyn0217 26872a6
Fix release notes
zyn0217 10b9d8b
clarify a bit more
zyn0217 54af6a1
Typo
zyn0217 81fd4c9
[Sema] Default arguments for template parameters affect ContainsUnexp…
ilya-biryukov 97372b6
Merge tests from ilya's 99882 and handle template parameter cases
zyn0217 d1cc8a8
Handle more cases e.g. constraints, DeclStmt, etc.
zyn0217 45921ef
Merge branch 'main' into GH85667
zyn0217 ce2bf93
Co-author
zyn0217 c2a0bf9
Fix ReleaseNotes
zyn0217 ea94d6d
Use CurContext
zyn0217 412bf5e
Remove the fixes for constraints
zyn0217 de76522
Remove extra blanks
zyn0217 930e8f9
Remove changes for constraints in TransformLambdaExpr
zyn0217 9ce3250
Simplify the RebuildLambdaExpr()
zyn0217 0379733
Merge branch 'main' into GH85667
zyn0217 e74a968
Remove most self-explanatory comments
zyn0217 27bd832
Remove one more gratuitous comment
zyn0217 ac89937
FIXME
zyn0217 301037e
Fix CI
zyn0217 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4028,6 +4028,20 @@ class TreeTransform { | |
NumExpansions); | ||
} | ||
|
||
ExprResult RebuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc, | ||
LambdaScopeInfo *LSI) { | ||
for (ParmVarDecl *PVD : LSI->CallOperator->parameters()) { | ||
if (Expr *Init = PVD->getInit()) | ||
LSI->ContainsUnexpandedParameterPack |= | ||
Init->containsUnexpandedParameterPack(); | ||
else if (PVD->hasUninstantiatedDefaultArg()) | ||
LSI->ContainsUnexpandedParameterPack |= | ||
PVD->getUninstantiatedDefaultArg() | ||
->containsUnexpandedParameterPack(); | ||
} | ||
return getSema().BuildLambdaExpr(StartLoc, EndLoc, LSI); | ||
} | ||
|
||
/// Build an empty C++1z fold-expression with the given operator. | ||
/// | ||
/// By default, produces the fallback value for the fold-expression, or | ||
|
@@ -8284,6 +8298,7 @@ StmtResult | |
TreeTransform<Derived>::TransformDeclStmt(DeclStmt *S) { | ||
bool DeclChanged = false; | ||
SmallVector<Decl *, 4> Decls; | ||
LambdaScopeInfo *LSI = getSema().getCurLambda(); | ||
for (auto *D : S->decls()) { | ||
Decl *Transformed = getDerived().TransformDefinition(D->getLocation(), D); | ||
if (!Transformed) | ||
|
@@ -8292,6 +8307,15 @@ TreeTransform<Derived>::TransformDeclStmt(DeclStmt *S) { | |
if (Transformed != D) | ||
DeclChanged = true; | ||
|
||
if (LSI && isa<TypeDecl>(Transformed)) | ||
LSI->ContainsUnexpandedParameterPack |= | ||
getSema() | ||
.getASTContext() | ||
.getTypeDeclType(cast<TypeDecl>(Transformed)) | ||
.getCanonicalType() | ||
.getTypePtr() | ||
->containsUnexpandedParameterPack(); | ||
|
||
Comment on lines
+8310
to
+8318
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't think of any other case that owns an unexpanded flag, handling TypeDecls + Exprs is probably sufficient at this point. |
||
Decls.push_back(Transformed); | ||
} | ||
|
||
|
@@ -14523,7 +14547,6 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { | |
|
||
CXXMethodDecl *NewCallOperator = | ||
getSema().CreateLambdaCallOperator(E->getIntroducerRange(), Class); | ||
NewCallOperator->setLexicalDeclContext(getSema().CurContext); | ||
|
||
// Enter the scope of the lambda. | ||
getSema().buildLambdaScope(LSI, NewCallOperator, E->getIntroducerRange(), | ||
|
@@ -14591,6 +14614,13 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { | |
} | ||
NewVDs.push_back(NewVD); | ||
getSema().addInitCapture(LSI, NewVD, C->getCaptureKind() == LCK_ByRef); | ||
// Cases we want to tackle: | ||
// ([C(Pack)] {}, ...) | ||
// But rule out cases e.g. | ||
// [...C = Pack()] {} | ||
if (NewC.EllipsisLoc.isInvalid()) | ||
LSI->ContainsUnexpandedParameterPack |= | ||
Init.get()->containsUnexpandedParameterPack(); | ||
} | ||
|
||
if (Invalid) | ||
|
@@ -14658,6 +14688,11 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { | |
continue; | ||
} | ||
|
||
// This is not an init-capture; however it contains an unexpanded pack e.g. | ||
// ([Pack] {}(), ...) | ||
if (auto *VD = dyn_cast<VarDecl>(CapturedVar); VD && !C->isPackExpansion()) | ||
LSI->ContainsUnexpandedParameterPack |= VD->isParameterPack(); | ||
|
||
// Capture the transformed variable. | ||
getSema().tryCaptureVariable(CapturedVar, C->getLocation(), Kind, | ||
EllipsisLoc); | ||
|
@@ -14669,9 +14704,12 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { | |
auto TPL = getDerived().TransformTemplateParameterList( | ||
E->getTemplateParameterList()); | ||
LSI->GLTemplateParameterList = TPL; | ||
if (TPL) | ||
if (TPL) { | ||
getSema().AddTemplateParametersToLambdaCallOperator(NewCallOperator, Class, | ||
TPL); | ||
LSI->ContainsUnexpandedParameterPack |= | ||
TPL->containsUnexpandedParameterPack(); | ||
} | ||
|
||
// Transform the type of the original lambda's call operator. | ||
// The transformation MUST be done in the CurrentInstantiationScope since | ||
|
@@ -14710,6 +14748,8 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { | |
|
||
if (NewCallOpType.isNull()) | ||
return ExprError(); | ||
LSI->ContainsUnexpandedParameterPack |= | ||
NewCallOpType->containsUnexpandedParameterPack(); | ||
NewCallOpTSI = | ||
NewCallOpTLBuilder.getTypeSourceInfo(getSema().Context, NewCallOpType); | ||
} | ||
|
@@ -14824,8 +14864,8 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { | |
Class->setTypeForDecl(nullptr); | ||
getSema().Context.getTypeDeclType(Class); | ||
|
||
return getSema().BuildLambdaExpr(E->getBeginLoc(), Body.get()->getEndLoc(), | ||
&LSICopy); | ||
return getDerived().RebuildLambdaExpr(E->getBeginLoc(), | ||
Body.get()->getEndLoc(), &LSICopy); | ||
} | ||
|
||
template<typename Derived> | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s | ||
|
||
namespace GH85667 { | ||
|
||
template <class T> | ||
struct identity { | ||
using type = T; | ||
}; | ||
|
||
template <class = void> void f() { | ||
|
||
static_assert([]<class... Is>(Is... x) { | ||
return ([I(x)] { | ||
return I; | ||
}() + ...); | ||
}(1, 2) == 3); | ||
|
||
[]<class... Is>(Is... x) { | ||
return ([](auto y = Is()) { return y + 1; }() + ...); // expected-error {{no matching function}} \ | ||
// expected-note {{couldn't infer template argument 'y:auto'}} \ | ||
// expected-note@-1 {{requested here}} | ||
// expected-note@#instantiate-f {{requested here}} | ||
}(1); | ||
|
||
[]<class... Is>() { | ||
([]<class = Is>(Is) | ||
noexcept(bool(Is())) | ||
{}(Is()), | ||
...); | ||
}.template operator()<char, int, float>(); | ||
|
||
static_assert(__is_same(decltype([]<class... Is>() { | ||
return ([]() -> decltype(Is()) { return {}; }(), | ||
...); | ||
}.template operator()<int, char>()), | ||
char)); | ||
|
||
[]<class... Is>() { | ||
return ([]<class... Ts>() -> decltype(Is()) { return Ts(); }() + ...); | ||
// expected-error@-1 {{unexpanded parameter pack 'Ts'}} | ||
}.template operator()<int, int>(); | ||
|
||
// https://github.com/llvm/llvm-project/issues/56852 | ||
[]<class... Is>(Is...) { | ||
([] { | ||
using T = identity<Is>::type; | ||
}(), ...); | ||
}(1, 2); | ||
|
||
[](auto ...y) { | ||
([y] { }(), ...); | ||
}(); | ||
|
||
[](auto ...x) { | ||
([&](auto ...y) { | ||
([x..., y] { }(), ...); | ||
})(1); | ||
}(2, 'b'); | ||
|
||
#if 0 | ||
// FIXME: https://github.com/llvm/llvm-project/issues/18873 | ||
[](auto ...x) { // #1 | ||
([&](auto ...y) { // #2 | ||
([x, y] { }(), ...); // #3 | ||
})(1, 'a'); // #4 | ||
}(2, 'b'); // #5 | ||
|
||
// We run into another crash for the above lambda because of the absence of a | ||
// mechanism that rebuilds an unexpanded pack from an expanded Decls. | ||
// | ||
// Basically, this happens after `x` at #1 being expanded when the template | ||
// arguments at #5, deduced as <int, char>, are ready. When we want to | ||
// instantiate the body of #1, we first instantiate the CallExpr at #4, which | ||
// boils down to the lambda's instantiation at #2. To that end, we have to | ||
// instantiate the body of it, which turns out to be #3. #3 is a CXXFoldExpr, | ||
// and we immediately have to hold off on the expansion because we don't have | ||
// corresponding template arguments (arguments at #4 are not transformed yet) for it. | ||
// Therefore, we want to rebuild a CXXFoldExpr, which requires another pattern | ||
// transformation of the lambda inside #3. Then we need to find an unexpanded form | ||
// of such a Decl of x at the time of transforming the capture, which is impossible | ||
// because the instantiated form has been expanded at #1! | ||
|
||
[](auto ...x) { // #outer | ||
([&](auto ...y) { // #inner | ||
([x, y] { }(), ...); | ||
// expected-error@-1 {{parameter pack 'y' that has a different length (4 vs. 3) from outer parameter packs}} | ||
// expected-note-re@#inner {{function template specialization {{.*}} requested here}} | ||
// expected-note-re@#outer {{function template specialization {{.*}} requested here}} | ||
// expected-note-re@#instantiate-f {{function template specialization {{.*}} requested here}} | ||
})('a', 'b', 'c'); | ||
}(0, 1, 2, 3); | ||
#endif | ||
} | ||
|
||
template void f(); // #instantiate-f | ||
|
||
} // namespace GH85667 | ||
|
||
namespace GH99877 { | ||
|
||
struct tuple { | ||
int x[3]; | ||
}; | ||
|
||
template <class F> int apply(F f, tuple v) { return f(v.x[0], v.x[1], v.x[2]); } | ||
|
||
int Cartesian1(auto x, auto y) { | ||
return apply( | ||
[&](auto... xs) { | ||
return (apply([xs](auto... ys) { return (ys + ...); }, y) + ...); | ||
}, | ||
x); | ||
} | ||
|
||
int Cartesian2(auto x, auto y) { | ||
return apply( | ||
[&](auto... xs) { | ||
return (apply([zs = xs](auto... ys) { return (ys + ...); }, y) + ...); | ||
}, | ||
x); | ||
} | ||
|
||
template <int...> struct Ints {}; | ||
template <int> struct Choose { | ||
template <class> struct Templ; | ||
}; | ||
template <int... x> int Cartesian3(auto y) { | ||
return [&]<int... xs>(Ints<xs...>) { | ||
// check in default template arguments for | ||
// - type template parameters, | ||
(void)(apply([]<class = decltype(xs)>(auto... ys) { return (ys + ...); }, | ||
y) + | ||
...); | ||
// - template template parameters. | ||
(void)(apply([]<template <class> class = Choose<xs>::template Templ>( | ||
auto... ys) { return (ys + ...); }, | ||
y) + | ||
...); | ||
// - non-type template parameters, | ||
return (apply([]<int = xs>(auto... ys) { return (ys + ...); }, y) + ...); | ||
}(Ints<x...>()); | ||
} | ||
|
||
template <int... x> int Cartesian4(auto y) { | ||
return [&]<int... xs>(Ints<xs...>) { | ||
return ( | ||
apply([]<decltype(xs) xx = 1>(auto... ys) { return (ys + ...); }, y) + | ||
...); | ||
}(Ints<x...>()); | ||
} | ||
|
||
// FIXME: Attributes should preserve the ContainsUnexpandedPack flag. | ||
#if 0 | ||
|
||
int Cartesian5(auto x, auto y) { | ||
return apply( | ||
[&](auto... xs) { | ||
return (apply([](auto... ys) __attribute__(( | ||
diagnose_if(!__is_same(decltype(xs), int), "message", | ||
"error"))) { return (ys + ...); }, | ||
y) + | ||
...); | ||
}, | ||
x); | ||
} | ||
|
||
#endif | ||
|
||
void foo() { | ||
auto x = tuple({1, 2, 3}); | ||
auto y = tuple({4, 5, 6}); | ||
Cartesian1(x, y); | ||
Cartesian2(x, y); | ||
Cartesian3<1, 2, 3>(y); | ||
Cartesian4<1, 2, 3>(y); | ||
#if 0 | ||
Cartesian5(x, y); | ||
#endif | ||
} | ||
|
||
} // namespace GH99877 |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recovered this comment from https://reviews.llvm.org/D124351 because at least it tries to explain something - better than nothing that just makes the lambda parsing dimmer.