[cfe-dev] Need Feedback on this very preliminary code that implements a portion of generic lambdas.
Faisal Vali
faisalv at gmail.com
Mon May 13 22:25:07 PDT 2013
First off, this patch is far from ready for committing - it does not include
enough tests - includes commented out code - and even some dead
code. I shall try to address all those issues in the future - but for now
I just wanted to put this out there for some early feedback (before I go
any deeper down a rabbit hole I need not have crawled into)
The patch implements the following (an incomplete list):
- converting auto parameters to template parameters and creating a
function call operator template
- It does this by replacing the typesourceinfo of the parameter -
Should I avoid doing this? Is there a way to transform the auto
into a template type parameter, so that it subsequently behaves
as a template type parameter, but remembers that it was
once an auto (for error messages only, right?)
- it makes TransformLambdaExpr and InstantiateFunctionDefinition
generic lambda aware (to allow for transformation of nested lambdas)
so that the
- Regarding capturing for generic lambdas:
1) A generic lambda (nested or not) will only have its captures
computed/finalized when its enclosing context is non-dependent
(and if an empty capture list, then no captures).
2) When a generic lambda is invoked (i.e a specialization is
instantiated)
any variable from an outer scope that is odr-used, but not already
captured by the lambda expression, results in an arror.
3) If an outer lambda that is enclosed in a non-dependent context,
can't tell whether an inner lambda (with an implicit capture)
or a tree of inner lambdas (all with implicit captures)
might need to capture the variable for some
instantiation of that inner lambda, the outer lambda will
capture that variable (if it has a capture default).
- this is implemented by hooking into ActOnCallExpr
(is it enough to assume that the only cases where an inner
lambda might potentially need to capture a variable is
if it is used in a dependent function call (ctor, operator)
of the inner lambda, in a non-unevaluated context?)
- Within ActOnCallExpr, I dig into each argument, and as
long as they are not in an unevaluated context (currently
I only check for sizeof), and are from a scope outside
the outer lambda, I capture the variable within the outer
lambda (if both the inner and outer lambda have a cap default,
and the outer lambda is enclosed by a non-dependent context).
- The reason I do this in ActOnCallExpr is because until then
I do not know if the function call is still dependent, perhaps
there is a better place to do this?
I will include some code to illustrate various scenarios:
#define L_CAP
#define M_CAP
#define N_CAP
#define L_CALL
#define M_CALL
#define N_CALL
#define M_IS_GENERIC_IF_AUTO auto
#define TEST_CALL test('a')
#define F_CALL f(x, selector)
void f(int, const int (&)[1]) { }
void f(const int&, const int (&)[2]) { }
void g(int, const int (&)[1]) { }
void g(int, const int (&)[2]) { }
template<class T>
void test(T t) {
const int x = 0;
const int y = 10;
auto L = [L_CAP](auto a) -> void {
int selector[sizeof(t) == 1 ? 1 : 2]{};
F_CALL;
auto M = [M_CAP](M_IS_GENERIC_IF_AUTO b) -> void {
int selector[sizeof(a) == 1 ? 1 : 2]{};
F_CALL;
auto N = [N_CAP](auto c) -> void {
int selector[sizeof(c) == 1 ?
(sizeof(b) == 1 ? 1 : 2) : 2]{};
F_CALL;
};
N_CALL;
};
M_CALL;
};
L_CALL;
}
int main() {
TEST_CALL;
}
Now lets consider the following scenarios (if the #define is not
mentioned in that scenario, it is as above):
A) #define TEST_CALL test('h'); test(5);
- no errors
B) #define TEST_CALL test(5)
- no errors
- error if L is instantiated: e.g. #define L_CALL L('5')
- ok again if: #define L_CAP =
C) #define TEST_CALL test(5)
#define L_CAP =
#define L_CALL L('j')
- no errors since M does not need to capture x
- if #define N_CAP =, still no error.
- error if M is instantiated: e.g. #define M_CALL M('a')
- since when N is examined, it contains an expression that
depends on a dependent generic parameter, so therefore
M contains an expression that depends on a dependent
generic
parameter, and so x needs to be captured by M
but M has no default capture or explicit x capure.
- this can be fixed by #define M_CAP = OR #define M_CAP x
- if #define N_CAP =
#define M_CALL M('a')
#define F_CALL sizeof(f(x, selector))
Then no error, since x can not be odr-used
- if #define N_CAP =
#define M_CALL M('a')
#define F_CALL g(y, selector)
Even though all overloads of g don't require N to
capture y, y will need to be captured preemptively by M
(which will error, unless M has a default capture or
explicitly
captures y) but never by N, because when M has to finalize
its
captures, it can only see that N uses x in a
dependent expression, and so it can't know that all
instantiations of N will never odr-use y.
(although i guess it can by looking at each overload
and knowing that ADL is disabled for primitives and
recognizing that every call requires the lvalue-to-rvalue
conversion, but this could get very hairy i imagine)
D) #define TEST_CALL test('h')
#define L_CALL L('j')
#define M_IS_GENERIC_IF_AUTO char
- no errors
- if #define N_CALL N('k') - still no errors
- if #define N_CALL N(5) - error, N can not capture
- if #define M_IS_GENERIC_IF_AUTO auto
and #define M_CALL M('k')
and #define N_CALL N('t') - still no errors
but if instead above #define M_CALL M(5) - error N cant
capture x
4) Does ActOnCallExpr, cover dependent Constructor and Operator
invocations? (sorry i'm being lazy here)
Additionally, It does not implement the following (but i will get to it):
- return type deduction for generic lambdas
- conversion operator for generic lambdas with no captures
- comprehensive capturing of variadic packs within nested lambdas
Recognizing that this is a very rough draft, and that the code does need
significant refactoring, curating, testing and potential re-architecting
before it is ready for commit;
I'm hoping to solicit the following feedback:
- general thoughts & discussion regarding the implementation strategy
(and whether there are better ways to do what I am doing).
- on the correctness of the capturing semantics as described above and
their concordance with our standard wording.
- should i try and break up the patch into smaller patches when
submitting for actual commit?
- should i continue working on this ...
Any constructive feedback will be welcome!
Thanks.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20130514/57e7bbc0/attachment.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: initial-generic-lambda-no-conversion-operator-or-pack-expansions-4.patch
Type: application/octet-stream
Size: 133768 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20130514/57e7bbc0/attachment.obj>
More information about the cfe-dev
mailing list