Faisal Vali 2b391ab708 Implement a rudimentary form of generic lambdas.
Specifically, the following features are not included in this commit:
  - any sort of capturing within generic lambdas 
  - generic lambdas within template functions and nested 
    within other generic lambdas
  - conversion operator for captureless lambdas
  - ensuring all visitors are generic lambda aware
  (Although I have gotten some useful feedback on my patches of the above and will be incorporating that as I submit those patches for commit)

As an example of what compiles through this commit:

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.

This patch has been reviewed by Doug and Richard.  Minor changes (non-functionality affecting) have been made since both of them formally looked at it, but the changes involve removal of supernumerary return type deduction changes (since they are now redundant, with richard having committed a recent patch to address return type deduction for C++11 lambdas using C++14 semantics). 



Some implementation notes:

  - Add a new Declarator context => LambdaExprParameterContext to 
    clang::Declarator to allow the use of 'auto' in declaring generic
    lambda parameters
      
  - Add various helpers to CXXRecordDecl to facilitate identifying
    and querying a closure class
  
  - LambdaScopeInfo (which maintains the current lambda's Sema state)
    was augmented to house the current depth of the template being
    parsed (id est the Parser calls Sema::RecordParsingTemplateParameterDepth)
    so that SemaType.cpp::ConvertDeclSpecToType may use it to immediately 
    generate a template-parameter-type when 'auto' is parsed in a generic
    lambda parameter context.  (i.e we do NOT use AutoType deduced to 
    a template parameter type - Richard seemed ok with this approach).  
    We encode that this template type was generated from an auto by simply
    adding $auto to the name which can be used for better diagnostics if needed.

  - SemaLambda.h was added to hold some common lambda utility
    functions (this file is likely to grow ...)
    
  - Teach Sema::ActOnStartOfFunctionDef to check whether it
    is being called to instantiate a generic lambda's call
    operator, and if so, push an appropriately prepared
    LambdaScopeInfo object on the stack.
    
  - various tests were added - but much more will be needed.

There is obviously more work to be done, and both Richard (weakly) and Doug (strongly) 
have requested that LambdaExpr be removed form the CXXRecordDecl LambdaDefinitionaData
in a future patch which is forthcoming.

A greatful thanks to all reviewers including Eli Friedman, James Dennett, 
and especially the two gracious wizards (Richard Smith and Doug Gregor) 
who spent hours providing feedback (in person in Chicago and on the mailing lists).  
And yet I am certain that I have allowed unidentified bugs to creep in; bugs, that I will do my best to slay, once identified!

Thanks!

llvm-svn: 191453
2013-09-26 19:54:12 +00:00

66 lines
1.9 KiB
C++

// RUN: %clang_cc1 -fsyntax-only -std=c++1y %s -verify
int a;
int &b = [] (int &r) -> decltype(auto) { return r; } (a);
int &c = [] (int &r) -> decltype(auto) { return (r); } (a);
int &d = [] (int &r) -> auto & { return r; } (a);
int &e = [] (int &r) -> auto { return r; } (a); // expected-error {{cannot bind to a temporary}}
int &f = [] (int r) -> decltype(auto) { return r; } (a); // expected-error {{cannot bind to a temporary}}
int &g = [] (int r) -> decltype(auto) { return (r); } (a); // expected-warning {{reference to stack}}
int test_explicit_auto_return()
{
struct X {};
auto L = [](auto F, auto a) { return F(a); };
auto M = [](auto a) -> auto { return a; }; // OK
auto MRef = [](auto b) -> auto& { return b; }; //expected-warning{{reference to stack}}
auto MPtr = [](auto c) -> auto* { return &c; }; //expected-warning{{address of stack}}
auto MDeclType = [](auto&& d) -> decltype(auto) { return static_cast<decltype(d)>(d); }; //OK
M(3);
auto &&x = MDeclType(X{});
auto &&x1 = M(X{});
auto &&x2 = MRef(X{});//expected-note{{in instantiation of}}
auto &&x3 = MPtr(X{}); //expected-note{{in instantiation of}}
return 0;
}
int test_implicit_auto_return()
{
{
auto M = [](auto a) { return a; };
struct X {};
X x = M(X{});
}
}
int test_multiple_returns() {
auto M = [](auto a) {
bool k;
if (k)
return a;
else
return 5; //expected-error{{deduced as 'int' here}}
};
M(3); // OK
M('a'); //expected-note{{in instantiation of}}
return 0;
}
int test_no_parameter_list()
{
static int si = 0;
auto M = [] { return 5; }; // OK
auto M2 = [] -> auto&& { return si; }; // expected-error{{lambda requires '()'}}
M();
}
int test_conditional_in_return() {
auto Fac = [](auto f, auto n) {
return n <= 0 ? n : f(f, n - 1) * n;
};
// FIXME: this test causes a recursive limit - need to error more gracefully.
//Fac(Fac, 3);
}