[PATCH] Step 1: Simple Generic lambda (no captures or return type deduction)

Faisal Vali faisalv at gmail.com
Sat Jun 15 07:34:28 PDT 2013


This updated patch implements simple non-capturing, non-nested generic lambdas:

As an example of what compiles:

template <class F1, class F2>
struct overload : F1, F2 {
    using F1::operator();
    using F2::operator();
    overload(F1 f1, F2 f2) : F1(f1), F2(f2) { }
  };

  auto Recursive = [](auto Self, auto h, auto ... rest) {
    return 1 + Self(Self, rest...);
  };
  auto Base = [](auto Self, auto h) {
      return 1;
  };
  overload<decltype(Base), decltype(Recursive)> O(Base, Recursive);
  int num_params =  O(O, 5, 3, "abc", 3.14, 'a');

Please see attached tests for more examples.


Changes from the initial version of this patch:
  - added return type deduction (since the changes were minimal)
    -- both C++11 and C++1y deduction is supported for lambdas
    -- in c++1y mode we replace the implicit trailing return type with 'auto'
        instead of 'dependent type' (c++11)
    -- in c++1y mode forward to Richard's deduction machinery, else
        to Doug's original deduction machinery (there are some minor
        differences in deduction, hopefully subtly captured in the tests)
  - Per feedback from Eli:
    -- the ActOnAutoParameter has been buried within ActOnParamDeclarator
    -- ConversionOperator property has been removed
    -- "__invoke" is being used in the one place it is needed in the
static invoker

Issues:
  - the pch test file cxx11-lambda.mm causes a crash, and I do not know
    enough about PCH to try and tackle it without some guidance.
 - I'm still hoping Doug or Richard or another Clang template instantiation
   wizard can comment on the recursive instantiation issue.

Next TODO:
   - once i get this patch approved and committed, i will start
merging/working on
     capturing capability and nested lambdas.

Look forward to your timely :) review and feedback!

Thanks!


Faisal Vali



On Fri, Jun 14, 2013 at 12:26 PM, Eli Friedman <eli.friedman at gmail.com> wrote:
> On Fri, Jun 14, 2013 at 10:01 AM, Faisal Vali <faisalv at gmail.com> wrote:
>>
>> On Thu, Jun 13, 2013 at 12:58 PM, Eli Friedman <eli.friedman at gmail.com>
>> wrote:
>> > On Tue, Jun 11, 2013 at 10:05 PM, Faisal Vali <faisalv at gmail.com> wrote:
>> > @@ -547,6 +550,18 @@
>> >
>> >      /// \brief The type of the call method.
>> >      TypeSourceInfo *MethodTyInfo;
>> > +
>> > +    /// \brief The Lambda call method
>> > +    CXXMethodDecl *CallOperator;
>> > +
>> > +    /// \brief The Lambda conversion operator, for non-capturing
>> > lambdas
>> > +    CXXConversionDecl *ConversionOperator;
>> > +
>> > +    /// \brief The Lambda static method invoker, for non-capturing
>> > lambdas
>> > +    CXXMethodDecl *StaticInvoker;
>> > +
>> > +    LambdaExpr *ParentLambdaExpr;
>> >
>> > It's not obvious why you're making this change... we try to keep the AST
>> > as
>> > small as possible.
>> >
>>
>> I see, but how do you suggest I access this data when I need it?
>
>
> If you really do need it all, it's okay, I guess... it was just a bit
> surprising at first glance.  Taking another look, it doesn't look like
> you're actually using getLambdaConversionOperator() (and in clang's
> implementation, lambdas can have multiple conversion operators).
>
>>
>>
>> >
>> > +      ParmVarDecl *PVD = cast<ParmVarDecl>(Param);
>> > +      // Handle 'auto' within a generic lambda.
>> > +      // FVQUESTION: Should I use getOriginalType here - how do i
>> > +      // know when to use which?
>> > +      QualType ParamType = PVD->getType();
>> >
>> > If you're unsure, getType() is probably the right choice... I don't
>> > think it
>> > matters here, though.
>> >
>>
>> Thanks!  What is the difference between the two?
>
>
> getOriginalType() returns the undecayed type; getType() returns the semantic
> type of the declaration.
>
>>
>>
>> > +      if (getLangOpts().CPlusPlus1y &&
>> > ParamType->getContainedAutoType()) {
>> > +        TemplateTypeParmDecl *TemplateParam =
>> > +            Actions.ActOnLambdaAutoParameter(getCurScope(), PVD,
>> > +                          CurTemplateDepth, CurAutoParameterIndex);
>> > +        TemplateParams.push_back(TemplateParam);
>> > +      }
>> >
>> > We generally prefer to avoid having code in Parser/ inspect AST types.
>> > Can
>> > you move building the TemplateParameterList into
>> > ActOnStartOfLambdaDefinition?
>> >
>>
>>
>> In my initial implementation of generic lambdas, I had encased this
>> transformation
>> entirely within ActOnStartOfLambdaDefinition (which is called once the
>> parameter
>> declaration clause has been constructed).   In that version, I
>> reconstructed
>> the typesourceinfo and function type of the call operator after it was
>> initially
>> parsed and semanalyzed, by replacing each auto with a template type
>> paramater,
>> creating the template prameter list, and using a visitor to fix all
>> subsequent references (ugh!)
>>
>> Based on sage advice from Doug, I moved the transformation upstream
>> and the code changes
>> have proved to be much leaner.
>
>
> Okay.
>
>> In regards to avoiding having the Parser inspect AST types What I
>> could do is nest
>> ActOnAutoParameter within ActOnParamDeclarator.  Is it ok that I pass
>> in a vector
>> of TemplateTypeParamDecl* as an out parameter to
>> ParseParameterDeclarationClause
>> which I will then need to pass in to ActOnParamDeclarator? Or is it
>> important that I find a way to do it without passing that vector around?
>
>
> That should be fine.
>
>>
>> > +// The name of the static invoker function that will forward
>> > +// to the corresponding lambda call operator.
>> > +static inline const char* getSecretLambdaStaticInvokerStringID() {
>> > +  return "__invoker";
>> > +}
>> >
>> > I'm not sure how this abstraction is actually helpful, given the
>> > function
>> > only has one use.
>> >
>>
>> I figured it might be less brittle if more uses cropped up in the future.
>> But
>> I can just insert the name: "__invoker" in the one place i need it, and
>> drop
>> this function?
>
>
> I think so.  We can always change it when we add more uses.
>
>>
>> > +  // FVQUESTION? When a generic lamdba call operator is being
>> > instantiated,
>> > if
>> > +  // for some reason instantiation fails, the SemaDiagnosticEmitter
>> > ends up
>> > +  // emitting the entire instantiation stack, and since local pending
>> > +  // instantiations are called recursively,
>> >
>> > We should not be instantiating the lambda in your example more than once
>> > at
>> > the same time.  If we are, it's probably a bug.
>> >
>>
>>
>> Does my workaround seem reasonable?
>>
>
> Sorry, I'm not really an expert on template instantiation.  Hopefully Doug
> will chime in.
>
> -Eli
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: simple-generic-lambda+return-deduction-1.patch
Type: application/octet-stream
Size: 72304 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20130615/6e2c7a8b/attachment.obj>


More information about the cfe-commits mailing list