// RUN: %clang_cc1 -std=c++1y %s -verify -emit-llvm-only
// RUN: %clang_cc1 -std=c++1z %s -verify -emit-llvm-only

namespace variadic_expansion {
  int f(int &, char &) { return 0; }
  template<class ... Ts> char fv(Ts ... ts) { return 0; }
  // FIXME: why do we get 2 error messages
  template <typename ... T> void g(T &... t) { //expected-note3{{declared here}}
    f([&a(t)]()->decltype(auto) {
      return a;
    }() ...);
    
    auto L = [x = f([&a(t)]()->decltype(auto) { return a; }()...)]() { return x; };
    const int y = 10;
    auto M = [x = y, 
                &z = y](T& ... t) { }; 
    auto N = [x = y, 
                &z = y, n = f(t...), 
                o = f([&a(t)](T& ... t)->decltype(auto) { return a; }(t...)...), t...](T& ... s) { 
                  fv([&a(t)]()->decltype(auto) { 
                    return a;
                  }() ...);
                };                 
    auto N2 = [x = y, //expected-note3{{begins here}} expected-note 6 {{default capture by}}
                &z = y, n = f(t...), 
                o = f([&a(t)](T& ... t)->decltype(auto) { return a; }(t...)...)](T& ... s) { // expected-note 6 {{capture 't' by}}
                fv([&a(t)]()->decltype(auto) { //expected-error 3{{captured}}
                    return a;
                  }() ...);
                };                 

  }

  void h(int i, char c) { g(i, c); } //expected-note{{in instantiation}}
}

namespace odr_use_within_init_capture {

int test() {
  
  { // no captures
    const int x = 10;
    auto L = [z = x + 2](int a) {
      auto M = [y = x - 2](char b) {
        return y;
      };
      return M;
    };
        
  }
  { // should not capture
    const int x = 10;
    auto L = [&z = x](int a) {
      return a;;
    };
        
  }
  {
    const int x = 10;
    auto L = [k = x](char a) {  //expected-note {{declared}}
      return [](int b) {        //expected-note {{begins}} expected-note 2 {{capture 'k' by}} expected-note 2 {{default capture by}}
        return [j = k](int c) { //expected-error {{cannot be implicitly captured}}
          return c;
        };
      };
    };
  }
  {
    const int x = 10;
    auto L = [k = x](char a) { 
      return [=](int b) { 
        return [j = k](int c) { 
          return c;
        };
      };
    };
  }
  {
    const int x = 10;
    auto L = [k = x](char a) { 
      return [k](int b) { 
        return [j = k](int c) { 
          return c;
        };
      };
    };
  }

  return 0;
}

int run = test();

}

namespace odr_use_within_init_capture_template {

template<class T = int>
int test(T t = T{}) {

  { // no captures
    const T x = 10;
    auto L = [z = x](char a) {
      auto M = [y = x](T b) {
        return y;
      };
      return M;
    };
        
  }
  { // should not capture
    const T x = 10;
    auto L = [&z = x](T a) {
      return a;;
    };
        
  }
  { // will need to capture x in outer lambda
    const T x = 10; //expected-note {{declared}}
    auto L = [z = x](char a) { //expected-note {{begins}} expected-note 2 {{capture 'x' by}} expected-note 2 {{default capture by}}
      auto M = [&y = x](T b) { //expected-error {{cannot be implicitly captured}}
        return y;
      };
      return M;
    };
  }
  { // will need to capture x in outer lambda
    const T x = 10; 
    auto L = [=,z = x](char a) { 
      auto M = [&y = x](T b) { 
        return y;
      };
      return M;
    };
        
  }
  { // will need to capture x in outer lambda
    const T x = 10; 
    auto L = [x, z = x](char a) { 
      auto M = [&y = x](T b) { 
        return y;
      };
      return M;
    };
  }
  { // will need to capture x in outer lambda
    const int x = 10; //expected-note {{declared}}
    auto L = [z = x](char a) { //expected-note {{begins}} expected-note 2 {{capture 'x' by}} expected-note 2 {{default capture by}}
      auto M = [&y = x](T b) { //expected-error {{cannot be implicitly captured}}
        return y;
      };
      return M;
    };
  }
  {
    // no captures
    const T x = 10;
    auto L = [z = 
                  [z = x, &y = x](char a) { return z + y; }('a')](char a) 
      { return z; };
  
  }
  
  return 0;
}

int run = test(); //expected-note {{instantiation}}

}

namespace classification_of_captures_of_init_captures {

template <typename T>
void f() {
  [a = 24] () mutable {
    [&a] { a = 3; }();
  }();
}

template <typename T>
void h() {
  [a = 24] (auto param) mutable {
    [&a] { a = 3; }();
  }(42);
}

int run() {
  f<int>();
  h<int>();
}

}

namespace N3922 {
  struct X { X(); explicit X(const X&); int n; };
  auto a = [x{X()}] { return x.n; }; // ok
  auto b = [x = {X()}] {}; // expected-error{{<initializer_list>}}
}

namespace init_capture_non_mutable {
void test(double weight) {
  double init;
  auto find = [max = init](auto current) {
    max = current; // expected-error{{cannot assign to a variable captured by copy in a non-mutable lambda}}
  };
  find(weight); // expected-note {{in instantiation of function template specialization}}
}
}

namespace init_capture_undeclared_identifier {
  auto a = [x = y]{}; // expected-error{{use of undeclared identifier 'y'}}

  int typo_foo; // expected-note 2 {{'typo_foo' declared here}}
  auto b = [x = typo_boo]{}; // expected-error{{use of undeclared identifier 'typo_boo'; did you mean 'typo_foo'}}
  auto c = [x(typo_boo)]{}; // expected-error{{use of undeclared identifier 'typo_boo'; did you mean 'typo_foo'}}
}

namespace copy_evasion {
  struct A {
    A();
    A(const A&) = delete;
  };
  auto x = [a{A()}] {};
#if __cplusplus >= 201702L
  // ok, does not copy an 'A'
#else
  // expected-error@-4 {{call to deleted}}
  // expected-note@-7 {{deleted}}
#endif
}
