Faisal Vali a17d19fb41 This patch implements capturing of variables within generic lambdas.
Both Richard and I felt that the current wording in the working paper needed some tweaking - Please see http://llvm-reviews.chandlerc.com/D2035 for additional context and references to core-reflector messages that discuss wording tweaks.

What is implemented is what we had intended to specify in Bristol; but, recently felt that the specification might benefit from some tweaking and fleshing.  

As a rough attempt to explain the semantics: If a nested lambda with a default-capture names a variable within its body, and if the enclosing full expression that contains the name of that variable is instantiation-dependent - then an enclosing lambda that is capture-ready (i.e. within a non-dependent context) must capture that variable, if all intervening nested lambdas can potentially capture that variable if they need to, and all intervening parent lambdas of the capture-ready lambda can and do capture the variable.      

Of note, 'this' capturing is also currently underspecified in the working paper for generic lambdas.  What is implemented here is if the set of candidate functions in a nested generic lambda includes both static and non-static member functions (regardless of viability checking - i.e. num and type of parameters/arguments) - and if all intervening nested-inner lambdas between the capture-ready lambda and the function-call containing nested lambda can capture 'this' and if all enclosing lambdas of the capture-ready lambda can capture 'this', then 'this' is speculatively captured by that capture-ready lambda.

Hopefully a paper for the C++ committee (that Richard and I had started some preliminary work on) is forthcoming. 

This essentially makes generic lambdas feature complete, except for known bugs. The more prominent ones (and the ones I am currently aware of) being:
  - generic lambdas and init-captures are broken - but a patch that fixes this is already in the works ...
  - nested variadic expansions such as:
    auto K = [](auto ... OuterArgs) {
      vp([=](auto ... Is) {
          decltype(OuterArgs) OA = OuterArgs;
          return 0;
        }(5)...);
      return 0;
    };
    auto M = K('a', ' ', 1, " -- ", 3.14); 
   currently cause crashes.  I think I know how to fix this (since I had done so in my initial implementation) - but it will probably take some work and back & forth with Doug and Richard.

A warm thanks to all who provided feedback - and especially to Doug Gregor and Richard Smith for their pivotal guidance: their insight and prestidigitation in such matters is boundless!

Now let's hope this commit doesn't upset the buildbot gods ;)

Thanks!

llvm-svn: 194188
2013-11-07 05:17:06 +00:00

132 lines
3.2 KiB
C++

// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++1y -DCXX1Y
namespace test_factorial {
auto Fact = [](auto Self, unsigned n) -> unsigned {
return !n ? 1 : Self(Self, n - 1) * n;
};
auto six = Fact(Fact, 3);
}
namespace overload_generic_lambda {
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 NumParams = [](auto Self, auto h, auto ... rest) -> unsigned {
return 1 + Self(Self, rest...);
};
auto Base = [](auto Self, auto h) -> unsigned {
return 1;
};
overload<decltype(Base), decltype(NumParams)> O(Base, NumParams);
int num_params = O(O, 5, 3, "abc", 3.14, 'a');
}
namespace overload_generic_lambda_return_type_deduction {
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 NumParams = [](auto Self, auto h, auto ... rest) {
return 1 + Self(Self, rest...);
};
auto Base = [](auto Self, auto h) {
return 1;
};
overload<decltype(Base), decltype(NumParams)> O(Base, NumParams);
int num_params = O(O, 5, 3, "abc", 3.14, 'a');
}
namespace test_standard_p5 {
// FIXME: This test should eventually compile without an explicit trailing return type
auto glambda = [](auto a, auto&& b) ->bool { return a < b; };
bool b = glambda(3, 3.14); // OK
}
namespace test_deduction_failure {
int test() {
auto g = [](auto *a) { //expected-note{{candidate template ignored}}
return a;
};
struct X { };
X *x;
g(x);
g(3); //expected-error{{no matching function}}
return 0;
}
}
namespace test_instantiation_or_sfinae_failure {
int test2() {
{
auto L = [](auto *a) {
return (*a)(a); }; //expected-error{{called object type 'double' is not a function}}
double d;
L(&d); //expected-note{{in instantiation of}}
auto M = [](auto b) { return b; };
L(&M); // ok
}
{
auto L = [](auto *a) ->decltype (a->foo()) { //expected-note2{{candidate template ignored:}}
return (*a)(a); };
double d;
L(&d); //expected-error{{no matching function for call}}
auto M = [](auto b) { return b; };
L(&M); //expected-error{{no matching function for call}}
}
return 0;
}
}
namespace test_misc {
auto GL = [](auto a, decltype(a) b) //expected-note{{candidate function}}
-> int { return a + b; };
void test() {
struct X { };
GL(3, X{}); //expected-error{{no matching function}}
}
void test2() {
auto l = [](auto *a) -> int {
(*a)(a); return 0; }; //expected-error{{called object type 'double' is not a function}}
l(&l);
double d;
l(&d); //expected-note{{in instantiation of}}
}
}
namespace nested_lambdas {
int test() {
auto L = [](auto a) {
return [=](auto b) {
return a + b;
};
};
}
auto get_lambda() {
return [](auto a) {
return a;
};
};
int test2() {
auto L = get_lambda();
L(3);
}
}