-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[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
Conversation
✅ With the latest revision this PR passed the Python code formatter. |
✅ With the latest revision this PR passed the C/C++ code formatter. |
What you have make sense i think. Let me know when you remove the draft status so i can start reviewing the details |
I was waiting for the CI, but it seems Linux CI would take hours. However that shouldn't block the review I think, because Windows CI is green now. |
// Temporarily set the lexical declaration context to the current | ||
// context, so that the Scope stack matches the lexical nesting. | ||
Method->setLexicalDeclContext(CurContext); |
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.
I might be stupid but i still do not get why fold expressions are special, they should not be. void f(auto...);
template <class = void> void foo() {
[]<class... Is>() {
f([]() requires (!C<Is>) {}()...);
}.template operator()<char, int, float>();
} other interesting expansions might be f<[]() requires (!C<Is>) {}()...>();
//
f({+[]() requires (!C<Is>) {}()...});
// I am truly sorry.
[ ...fs = +[]() requires (!C<Is>) {}()]() {
(fs(),...);
}
|
Sorry I was inaccurate suggesting fold expressions are special, it's actually constraints involving pack expansions who are special. (I admit inventing another
This is awkward: no compiler currently compiles it. (https://gcc.godbolt.org/z/rce3rh7jK) And spuriously, both gcc and clang think that there are no unexpanded parameter packs for expansion. Regardless of whether this is conforming, the diagnostic here is simple to explain: we didn't preserve the (Though I didn't add the following to the patch yet, it doesn't solve the problem and instead would cause a crash) Expr *TrailingRequires = E->getCallOperator()->getTrailingRequiresClause();
if (TrailingRequires)
LSI->ContainsUnexpandedParameterPack |=
TrailingRequires->containsUnexpandedParameterPack(); We still fail to evaluate the constraint because it ends up being dependent even with proper template arguments. This is because the expression is not expanded anywhere before evaluating the constraint, which is one step when we forming up a CallExpr. The situation could be slightly different (or rather, better?) if a fold expression gets involved. We would still have a chance to expand the fold expression into an expanded but unsubstituted state. So we can make them into the constraint evaluation and substitute arguments there, and get a correct result. So this is why it's convoluted: we probably miss out on some passes for constraint packs: Maybe we need to expand them somewhere before evaluation, but I'm not completely clear.
(I promise I won't forget them but let's consider them when we get around to the constraints in the next patch. :) |
Okay so we seem to agree. Can you go over the pr and make sure "fold expression" says "pack expansion" everywhere that it should (release note, comments, function names) ? Thanks! |
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.
LGTM (modulo nit)
We should probably backport that
Co-authored-by: cor3ntin <[email protected]>
Thanks for the review! I've just merged it.
SGTM but given that this bug persists in so many versions, I think it is also fine to leave the fix just on trunk - especially since this is actually a partial fix. We could issue a backport to 19 after we have addressed remaining FIXMEs i.e. bugs on attributes/constraints/captures, if I can catch up the release schedule. WDYT? |
r3 is in 2 weeks and we have to be more conservatives in our backports, so it really depends on how long it took you and @ilya-biryukov to address these issues and the complexity of the patches. In particular, I would be surprised if the attribute case impacts many people. |
To be clear, I don't have a real-world use-case for attributes. The only reason I have added it in my patch is because there was a test in Clang that started failing with my approach (because it started looking at thing structurally even on the parsing path, as discussed earlier). |
…ints (#109518) (This continues the effort of #86265, fixing another piece of issue in constraint evaluation on variadic lambdas.) We need the depth of the primary template parameters for constraint substitution. To that end, we avoided substituting type constraints by copying the constraint expression when instantiating a template. This, however, has left an issue in that for lambda's parameters, they can reference outer template packs that would be expanded in the process of an instantiation, where these parameters would make their way into the constraint evaluation, wherein we have no other way to expand them later in evaluation. For example, template <class... Ts> void foo() { bar([](C<Ts> auto value) {}...); } The lambda references a pack `Ts` that should be expanded when instantiating `foo()`. The `Ts` along with the constraint expression would not be transformed until constraint evaluation, and at that point, we would have no chance to expand `Ts` anyhow. This patch takes an approach that transforms `Ts` from an unexpanded TemplateTypeParmType into a SubstTemplateTypeParmType with the current pack substitution index, such that we could use that to expand the type during evaluation. Fixes #101754
…ints (llvm#109518) (This continues the effort of llvm#86265, fixing another piece of issue in constraint evaluation on variadic lambdas.) We need the depth of the primary template parameters for constraint substitution. To that end, we avoided substituting type constraints by copying the constraint expression when instantiating a template. This, however, has left an issue in that for lambda's parameters, they can reference outer template packs that would be expanded in the process of an instantiation, where these parameters would make their way into the constraint evaluation, wherein we have no other way to expand them later in evaluation. For example, template <class... Ts> void foo() { bar([](C<Ts> auto value) {}...); } The lambda references a pack `Ts` that should be expanded when instantiating `foo()`. The `Ts` along with the constraint expression would not be transformed until constraint evaluation, and at that point, we would have no chance to expand `Ts` anyhow. This patch takes an approach that transforms `Ts` from an unexpanded TemplateTypeParmType into a SubstTemplateTypeParmType with the current pack substitution index, such that we could use that to expand the type during evaluation. Fixes llvm#101754
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 #56852
Fixes #85667
Mitigates #99877 because the attributes were not handled in this patch.