On Mon, May 13, 2013 at 10:25 PM, Faisal Vali <span dir="ltr"><<a href="mailto:faisalv@gmail.com" target="_blank">faisalv@gmail.com</a>></span> wrote:<br><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<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></div></blockquote><div><br></div><div>Just a couple of thoughts below, I've not had time to study the patch itself yet.</div>
<div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">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></div></blockquote><div><br></div><div>Perhaps an AutoType whose deduced type is the template parameter would work? However, note this comment from SubstituteAutoTransform:</div>
<div><br></div><div><div>      // If we're building the type pattern to deduce against, don't wrap the</div><div>      // substituted type in an AutoType. Certain template deduction rules</div><div>      // apply only when a template type parameter appears directly (and not if</div>
<div>      // the parameter is found through desugaring). For instance:</div><div>      //   auto &&lref = lvalue;</div><div>      // must transform into "rvalue reference to T" not "rvalue reference to</div>
<div>      // auto type deduced as T" in order for [temp.deduct.call]p3 to apply.</div></div><div><br></div><div>If we wanted that to work, we'd need to teach some parts of template deduction to walk over the AutoType node.</div>
<div><br></div><div>How significant is the diagnostic impact of performing the substitution? Where, and how often, does it show up?</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div dir="ltr">  - 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></div>
</blockquote><div><br></div><div>Per the proposal, you need captures in other situations:</div><div><br></div><div>struct S {</div><div>  constexpr S() {}</div><div>  S(const S&) { puts("captured"); }</div><div>
};</div><div>void f() {</div><div>  constexpr S s {};</div><div>  [=] (auto x) {</div><div>    // captures 's' (even though no instantiation can need it),</div><div>    // because the full-expression depends on 'x'. Therefore</div>
<div>    // we must print "captured".</div><div>    (void)x, s;</div><div>  };</div><div>}</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div dir="ltr">         - 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?</div></blockquote><div><br></div><div>Here's how I was imagining this working when we were in CWG:</div><div><br></div><div>The captures for a lambda are not finalized until the outermost context of the reaching scope is non-dependent. At that point, for each call to ActOnFinishFullExpr within the lambda, we check whether the full-expression is instantiation-dependent. If it is, we scan it for entities which it might need to capture, and capture all of them. There may be ways of optimizing this (maybe keep a list of the potentially-captured variables for each full-expression as we build it).</div>
<div style="text-align:left"><u><br></u></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div style="text-align:left">         </div>        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>
</blockquote></div><br>