Open cjdb opened 1 year ago
@llvm/issue-subscribers-c-20
@llvm/issue-subscribers-clang-frontend
@erichkeane maybe related to https://github.com/llvm/llvm-project/issues/61776
@erichkeane maybe related to #61776
Looks similar, but not the same cause apparently. Still crashes after the fix for #61776.
This one is likely https://github.com/llvm/llvm-project/issues/58872, the problem is we're instantiating the requires constraint before having a call to it. I had a WIP implementation here: https://reviews.llvm.org/D138148 but it wasnt taken over by @cschreib as anticipated.
@cschreib mentions in the other bug that he's unable to help out with that patch, so I/someone else need to grab it.
I am trying to implement non-eager lambda instantiation, but I am not that familiar with clang yet, so progress will be relatively slow ;)
I am trying to implement non-eager lambda instantiation, but I am not that familiar with clang yet, so progress will be relatively slow ;)
That is great news! I did a quick start above, but never got to spend more than a few minutes on it. Let me know if you get stuck, and I'll help how I can.
Progress update: I finally make the code in this issue and https://github.com/llvm/llvm-project/issues/58872 and https://reviews.llvm.org/D138148 (the test case provided by cschreib) compile ...
Next, I'll test nested generic lambda and more cases mixed with other language features (nontype template params, concepts, etc.)
Handling the implicit capture of this
in lambdas presents a challenge. Omitting the TransformLambdaBody
process entirely would prevent us from accurately determining if the lambda requires an implicit capture of this
.
#include <type_traits>
struct X {
void f(int) { }
static void f(double) { }
int g() {
auto L = [=](auto a) {
return [](int i) { // expected-note {{explicitly capture 'this'}} expected-note {{while substituting into a lambda}}
return [=](auto b) { // expected-note {{while substituting into a lambda}}
f(decltype(a){}); //expected-error{{this}}
int x = i;
};
};
};
L(3);
return 0;
}
};
int run = X{}.g();
My current implementation does not raise any compile error with this code (because the body is not instantiated). It will only flag an error when the body is instantiated (L(3)(0)(1)
). Is this behavior compliant with the standard? Would this code be considered IFNDR?
If not, could we ONLY skip the instantiation of dependent if constexpr
statements during TransformLambdaBody
? Alternatively, are there other viable method? Do you have any thoughts on this @erichkeane ? Your input would be greatly appreciated.
Handling the implicit capture of
this
in lambdas presents a challenge. Omitting theTransformLambdaBody
process entirely would prevent us from accurately determining if the lambda requires an implicit capture ofthis
.#include <type_traits> struct X { void f(int) { } static void f(double) { } int g() { auto L = [=](auto a) { return [](int i) { // expected-note {{explicitly capture 'this'}} expected-note {{while substituting into a lambda}} return [=](auto b) { // expected-note {{while substituting into a lambda}} f(decltype(a){}); //expected-error{{this}} int x = i; }; }; }; L(3); return 0; } }; int run = X{}.g();
My current implementation does not raise any compile error with this code (because the body is not instantiated). It will only flag an error when the body is instantiated (
L(3)(0)(1)
). Is this behavior compliant with the standard? Would this code be considered IFNDR?If not, could we ONLY skip the instantiation of dependent
if constexpr
statements duringTransformLambdaBody
? Alternatively, are there other viable method? Do you have any thoughts on this @erichkeane ? Your input would be greatly appreciated.
I don't think we could do that. I THINK we still would like to analyze that to see if we can calculate the capture based on everything else.
In the example above, we SHOULD be able to diagnose that right away, 'a' clearly cannot be anything else (that is, its type is dependent, but not its lookup).
I've encountered another example where the body needs to be instantiated to compute the capture set:
void bar(auto... v) {}
void foo(auto... v) {
bar([&] { v; } ...);
}
foo(1, 1.0, 'c');
Currently, I plan to execute TransformLambdaBody
as before, but the instantiation is only used to calculate the capture set, and I'll discard the instantiated body. During TransformLambdaBody
, I will block all diagnostic triggered in TransformLambdaBody
(similar to the implementation of SFINAE).
I'm not sure if this is feasible? Is there a better solution?
I've read through P0588R0 several times, and it mentions:
With this change in place, the set of captures of a lambda can be determined by a walk of the syntactic structure of the lambda-expression , which means that we do not need to instantiate the body of the lambda-expression within f(unique_ptr
) in order to determine its capture set.
However, I can't figure out how to implement the computation of implicit this
capture and the parameter pack expansion case added by this comment without instantiating the body under the existing architecture of Clang...
I'm still working on the implmentation. Currently I bypass the instantiation of if constexpr while still processing the remainder of the body. This approach works for both the parameter pack scenario and the implicit this capture case (see comments above). However, this method struggles with parameter packs within constexpr if statements, as demonstrated in this example (gcc just iced.). Additionally, the current method leads to redundant diagnostics.
Quoting my previous comment:
I've read through P0588R0 several times, and it mentions:
With this change in place, the set of captures of a lambda can be determined by a walk of the syntactic structure of the lambda-expression , which means that we do not need to instantiate the body of the lambda-expression within f(unique_ptr) in order to determine its capture set.
However, I can't figure out how to implement the computation of implicit this capture and the parameter pack expansion case added by this comment without instantiating the body under the existing architecture of Clang...
I REALLY need some help here @erichkeane @zygoloid
I experimented with another approach:
During TransformLambdaBody
, I bypassed dependent if constexpr
statements while still processing the remainder of the body.
Later, while instantiating the definition, I instantiated the previously skipped if constexpr
statements separately (with different template args, scope, etc.).
This method effectively resolved the issue of redundant instantiations (redundant diagnostics). (I’m not sure if this approach will encounter other problems.)
However, I think I still require a visitor to address the parameter pack within the if constexpr
case.
Sorry for the delay, I was out for 3 months on bonding leave, so jsut catching up now. Sorry if my comment misses past context here...
I don't think just skipping the if constexpr
is sufficient here, I think we end up needing to skip transforming the entire body unfortunately. Else we'll end up having spurious diagnostics on things that we shouldn't really be diagnosing 'yet'. Sure, they are things that would eventually fail and are probably covered under the "can never be instantiated" rule, but I wonder if we should avoid that.
The snippet below is derived from GCC's test
concepts-lambda14.C
causes an assertion failure. Unsure if it's a minimal repro, but it's as minimal as I could get it for now.