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