// RUN: %clang_cc1 -std=c++1y -verify -fsyntax-only -fblocks -emit-llvm-only %s
// DONTRUNYET: %clang_cc1 -std=c++1y -verify -fsyntax-only -fblocks -fdelayed-template-parsing %s -DDELAYED_TEMPLATE_PARSING
// DONTRUNYET: %clang_cc1 -std=c++1y -verify -fsyntax-only -fblocks -fms-extensions %s -DMS_EXTENSIONS
// DONTRUNYET: %clang_cc1 -std=c++1y -verify -fsyntax-only -fblocks -fdelayed-template-parsing -fms-extensions %s -DMS_EXTENSIONS -DDELAYED_TEMPLATE_PARSING

constexpr int ODRUSE_SZ = sizeof(char);

template<class T, int N>
void f(T, const int (&)[N]) { }

template<class T>
void f(const T&, const int (&)[ODRUSE_SZ]) { }

#define DEFINE_SELECTOR(x)   \
  int selector_ ## x[sizeof(x) == ODRUSE_SZ ? ODRUSE_SZ : ODRUSE_SZ + 5]

#define F_CALL(x, a) f(x, selector_ ## a)

// This is a risky assumption, because if an empty class gets captured by value
// the lambda's size will still be '1' 
#define ASSERT_NO_CAPTURES(L) static_assert(sizeof(L) == 1, "size of closure with no captures must be 1")
#define ASSERT_CLOSURE_SIZE_EXACT(L, N) static_assert(sizeof(L) == (N), "size of closure must be " #N)
#define ASSERT_CLOSURE_SIZE(L, N) static_assert(sizeof(L) >= (N), "size of closure must be >=" #N)


namespace sample {
  struct X {  
    int i;
    X(int i) : i(i) { }
  };
} 
 
namespace test_transformations_in_templates {
template<class T> void foo(T t) {
  auto L = [](auto a) { return a; };
}
template<class T> void foo2(T t) {
  auto L = [](auto a) -> void { 
    auto M = [](char b) -> void {
      auto N = [](auto c) -> void { 
        int selector[sizeof(c) == 1 ? 
                      (sizeof(b) == 1 ? 1 : 2) 
                      : 2
                    ]{};      
      };  
      N('a');
    };    
  };
  L(3.14);
}

void doit() {
  foo(3);
  foo('a');
  foo2('A');
}
}

namespace test_return_type_deduction {

void doit() {

  auto L = [](auto a, auto b) {
    if ( a > b ) return a;
    return b;
  };
  L(2, 4);
  {
    auto L2 = [](auto a, int i) {
      return a + i;
    };
    L2(3.14, 2);
  }
  {
    int a; //expected-note{{declared here}}
    auto B = []() { return ^{ return a; }; }; //expected-error{{cannot be implicitly capture}}\
                                              //expected-note{{begins here}}
  //[](){ return ({int b = 5; return 'c'; 'x';}); };

  //auto X = ^{ return a; };
  
  //auto Y = []() -> auto { return 3; return 'c'; };

  }  
}  
}


namespace test_no_capture{
void doit() {
  const int x = 10; //expected-note{{declared here}}
  {
    // should not capture 'x' - variable undergoes lvalue-to-rvalue
    auto L = [=](auto a) {
      int y = x;
      return a + y;
    };
    ASSERT_NO_CAPTURES(L);
  }
  {
    // should not capture 'x' - even though certain instantiations require
    auto L = [](auto a) { //expected-note{{begins here}}
      DEFINE_SELECTOR(a);
      F_CALL(x, a); //expected-error{{'x' cannot be implicitly captured}}
    };
    ASSERT_NO_CAPTURES(L);
    L('s'); //expected-note{{in instantiation of}}
  }
  {
    // Does not capture because no default capture in inner most lambda 'b'
    auto L = [=](auto a) {
      return [=](int p) {
        return [](auto b) {
          DEFINE_SELECTOR(a);
          F_CALL(x, a); 
          return 0;        
        }; 
      };
    };
    ASSERT_NO_CAPTURES(L);
  }  
}  // doit
} // namespace

namespace test_capture_of_potentially_evaluated_expression {
void doit() {
  const int x = 5;
  {
    auto L = [=](auto a) {
      DEFINE_SELECTOR(a);
      F_CALL(x, a);
    };
    static_assert(sizeof(L) == 4, "Must be captured");
  }
  {
    int j = 0; //expected-note{{declared}}
    auto L = [](auto a) {  //expected-note{{begins here}}
      return j + 1; //expected-error{{cannot be implicitly captured}}
    };
  }
  {
    const int x = 10;
    auto L = [](auto a) {
      //const int y = 20;
      return [](int p) { 
        return [](auto b) { 
          DEFINE_SELECTOR(a);
          F_CALL(x, a);  
          return 0;        
        }; 
      };
    };
    auto M = L(3);
    auto N = M(5);
    
  }
  
  { // if the nested capture does not implicitly or explicitly allow any captures
    // nothing should capture - and instantiations will create errors if needed.
    const int x = 0;
    auto L = [=](auto a) { // <-- #A
      const int y = 0;
      return [](auto b) { // <-- #B
        int c[sizeof(b)];
        f(x, c);
        f(y, c);
        int i = x;
      };
    };
    ASSERT_NO_CAPTURES(L);
    auto M_int = L(2);
    ASSERT_NO_CAPTURES(M_int);
  }
  { // Permutations of this example must be thoroughly tested!
    const int x = 0;
    sample::X cx{5};
    auto L = [=](auto a) { 
      const int z = 3;
      return [&,a](auto b) {
        const int y = 5;    
        return [=](auto c) { 
          int d[sizeof(a) == sizeof(c) || sizeof(c) == sizeof(b) ? 2 : 1];
          f(x, d);
          f(y, d);
          f(z, d);
          decltype(a) A = a;
          decltype(b) B = b;
          const int &i = cx.i;
        }; 
      };
    };
    auto M = L(3)(3.5);
    M(3.14);
  }
}
namespace Test_no_capture_of_clearly_no_odr_use {
auto foo() {
 const int x = 10; 
 auto L = [=](auto a) {
    return  [=](auto b) {
      return [=](auto c) {
        int A = x;
        return A;
      };
    };
  };
  auto M = L(1);
  auto N = M(2.14);
  ASSERT_NO_CAPTURES(L);
  ASSERT_NO_CAPTURES(N);
  
  return 0;
}
}

namespace Test_capture_of_odr_use_var {
auto foo() {
 const int x = 10; 
 auto L = [=](auto a) {
    return  [=](auto b) {
      return [=](auto c) {
        int A = x;
        const int &i = x;
        decltype(a) A2 = a;
        return A;
      };
    };
  };
  auto M_int = L(1);
  auto N_int_int = M_int(2);
  ASSERT_CLOSURE_SIZE_EXACT(L, sizeof(x));
  // M_int captures both a & x   
  ASSERT_CLOSURE_SIZE_EXACT(M_int, sizeof(x) + sizeof(int));
  // N_int_int captures both a & x   
  ASSERT_CLOSURE_SIZE_EXACT(N_int_int, sizeof(x) + sizeof(int)); 
  auto M_double = L(3.14);
  ASSERT_CLOSURE_SIZE(M_double, sizeof(x) + sizeof(double));
  
  return 0;
}
auto run = foo();
}

}    
namespace more_nested_captures_1 {
template<class T> struct Y {
  static void f(int, double, ...) { }
  template<class R> 
  static void f(const int&, R, ...) { }
  template<class R>
  void foo(R t) {
    const int x = 10; //expected-note{{declared here}}
    auto L = [](auto a) { 
       return [=](auto b) {
        return [=](auto c) { 
          f(x, c, b, a);  //expected-error{{reference to local variable 'x'}}
          return 0; 
        };
      };
    };
    auto M = L(t);
    auto N = M('b');
    N(3.14);
    N(5);  //expected-note{{in instantiation of}}
  }
};
Y<int> yi;
int run = (yi.foo(3.14), 0); //expected-note{{in instantiation of}}
}


namespace more_nested_captures_1_1 {
template<class T> struct Y {
  static void f(int, double, ...) { }
  template<class R> 
  static void f(const int&, R, ...) { }
  template<class R>
  void foo(R t) {
    const int x = 10; //expected-note{{declared here}}
    auto L = [](auto a) { 
       return [=](char b) {
        return [=](auto c) { 
          f(x, c, b, a);  //expected-error{{reference to local variable 'x'}}
          return 0; 
        };
      };
    };
    auto M = L(t);
    auto N = M('b');
    N(3.14);
    N(5);  //expected-note{{in instantiation of}}
  }
};
Y<int> yi;
int run = (yi.foo(3.14), 0); //expected-note{{in instantiation of}}
}
namespace more_nested_captures_1_2 {
template<class T> struct Y {
  static void f(int, double, ...) { }
  template<class R> 
  static void f(const int&, R, ...) { }
  template<class R>
  void foo(R t) {
    const int x = 10; 
    auto L = [=](auto a) { 
       return [=](char b) {
        return [=](auto c) { 
          f(x, c, b, a);  
          return 0; 
        };
      };
    };
    auto M = L(t);
    auto N = M('b');
    N(3.14);
    N(5);  
  }
};
Y<int> yi;
int run = (yi.foo(3.14), 0); 
}

namespace more_nested_captures_1_3 {
template<class T> struct Y {
  static void f(int, double, ...) { }
  template<class R> 
  static void f(const int&, R, ...) { }
  template<class R>
  void foo(R t) {
    const int x = 10; //expected-note{{declared here}}
    auto L = [=](auto a) { 
       return [](auto b) {
        const int y = 0;
        return [=](auto c) { 
          f(x, c, b);  //expected-error{{reference to local variable 'x'}}
          f(y, b, c);
          return 0; 
        };
      };
    };
    auto M = L(t);
    auto N = M('b');
    N(3.14);
    N(5);  //expected-note{{in instantiation of}}
  }
};
Y<int> yi;
int run = (yi.foo(3.14), 0); //expected-note{{in instantiation of}}
}


namespace more_nested_captures_1_4 {
template<class T> struct Y {
  static void f(int, double, ...) { }
  template<class R> 
  static void f(const int&, R, ...) { }
  template<class R>
  void foo(R t) {
    const int x = 10; //expected-note{{declared here}}
    auto L = [=](auto a) {
       T t2{t};       
       return [](auto b) {
        const int y = 0; //expected-note{{declared here}}
        return [](auto c) { //expected-note 2{{lambda expression begins here}}
          f(x, c);  //expected-error{{variable 'x'}}
          f(y, c);  //expected-error{{variable 'y'}}
          return 0; 
        };
      };
    };
    auto M = L(t);
    auto N_char = M('b');
    N_char(3.14);
    auto N_double = M(3.14);
    N_double(3.14);
    N_char(3);  //expected-note{{in instantiation of}}
  }
};
Y<int> yi;
int run = (yi.foo('a'), 0); //expected-note{{in instantiation of}}
}


namespace more_nested_captures_2 {
template<class T> struct Y {
  static void f(int, double) { }
  template<class R> 
  static void f(const int&, R) { }
  template<class R> 
  void foo(R t) {
    const int x = 10;
    auto L = [=](auto a) { 
       return [=](auto b) {
        return [=](auto c) { 
          f(x, c);  
          return 0; 
        };
      };
    };
    auto M = L(t);
    auto N = M('b');
    N(3);
    N(3.14);
  }
};
Y<int> yi;
int run = (yi.foo(3.14), 0);

}

namespace more_nested_captures_3 {
template<class T> struct Y {
  static void f(int, double) { }
  template<class R> 
  static void f(const int&, R) { }
  template<class R> 
  void foo(R t) {
    const int x = 10; //expected-note{{declared here}}
    auto L = [](auto a) { 
       return [=](auto b) {
        return [=](auto c) { 
          f(x, c);   //expected-error{{reference to local variable 'x'}}
          return 0; 
        };
      };
    };
    auto M = L(t);
    auto N = M('b');
    N(3); //expected-note{{in instantiation of}}
    N(3.14);
  }
};
Y<int> yi;
int run = (yi.foo(3.14), 0); //expected-note{{in instantiation of}}

}

namespace more_nested_captures_4 {
template<class T> struct Y {
  static void f(int, double) { }
  template<class R> 
  static void f(const int&, R) { }
  template<class R> 
  void foo(R t) {
    const int x = 10;  //expected-note{{'x' declared here}}
    auto L = [](auto a) { 
       return [=](char b) {
        return [=](auto c) { 
          f(x, c);  //expected-error{{reference to local variable 'x'}}
          return 0; 
        };
      };
    };
    auto M = L(t);
    auto N = M('b');
    N(3); //expected-note{{in instantiation of}}
    N(3.14);
  }
};
Y<int> yi;
int run = (yi.foo(3.14), 0); //expected-note{{in instantiation of}}

}

namespace more_nested_captures_5 {
template<class T> struct Y {
  static void f(int, double) { }
  template<class R> 
  static void f(const int&, R) { }
  template<class R> 
  void foo(R t) {
    const int x = 10;
    auto L = [=](auto a) { 
       return [=](char b) {
        return [=](auto c) { 
          f(x, c);   
          return 0; 
        };
      };
    };
    auto M = L(t);
    auto N = M('b');
    N(3); 
    N(3.14);
  }
};
Y<int> yi;
int run = (yi.foo(3.14), 0);

}

namespace lambdas_in_NSDMIs {
template<class T>
  struct L {
      T t{};
      T t2 = ([](auto a) { return [](auto b) { return b; };})(t)(t);    
      T t3 = ([](auto a) { return a; })(t);    
  };
  L<int> l; 
  int run = l.t2; 
}
namespace test_nested_decltypes_in_trailing_return_types {
int foo() {
  auto L = [](auto a) {
      return [](auto b, decltype(a) b2) -> decltype(a) {
        return decltype(a){};
      };
  };
  auto M = L(3.14);
  M('a', 6.26);
  return 0;
}
}

namespace more_this_capture_1 {
struct X {
  void f(int) { }
  static void f(double) { }
  void foo() {
    {
      auto L = [=](auto a) {
        f(a);
      };
      L(3);
      L(3.13);
    }
    {
      auto L = [](auto a) {
        f(a); //expected-error{{this}}
      };
      L(3.13);
      L(2); //expected-note{{in instantiation}}
    }
  }
  
  int g() {
    auto L = [=](auto a) { 
      return [](int i) {
        return [=](auto b) {
          f(b); 
          int x = i;
        };
      };
    };
    auto M = L(0.0); 
    auto N = M(3);
    N(5.32); // OK 
    return 0;
  }
};
int run = X{}.g();
}
namespace more_this_capture_1_1 {
struct X {
  void f(int) { }
  static void f(double) { }
  
  int g() {
    auto L = [=](auto a) { 
      return [](int i) {
        return [=](auto b) {
          f(decltype(a){}); //expected-error{{this}}
          int x = i;
        };
      };
    };
    auto M = L(0.0);  
    auto N = M(3);
    N(5.32); // OK 
    L(3); // expected-note{{instantiation}}
    return 0;
  }
};
int run = X{}.g();
}

namespace more_this_capture_1_1_1 {
struct X {
  void f(int) { }
  static void f(double) { }
  
  int g() {
    auto L = [=](auto a) { 
      return [](auto b) {
        return [=](int i) {
          f(b); 
          f(decltype(a){}); //expected-error{{this}}
        };
      };
    };
    auto M = L(0.0);  // OK
    auto N = M(3.3); //OK
    auto M_int = L(0); //expected-note{{instantiation}}
    return 0;
  }
};
int run = X{}.g();
}


namespace more_this_capture_1_1_1_1 {
struct X {
  void f(int) { }
  static void f(double) { }
  
  int g() {
    auto L = [=](auto a) { 
      return [](auto b) {
        return [=](int i) {
          f(b); //expected-error{{this}}
          f(decltype(a){}); 
        };
      };
    };
    auto M_double = L(0.0);  // OK
    auto N = M_double(3); //expected-note{{instantiation}}
    
    return 0;
  }
};
int run = X{}.g();
}

namespace more_this_capture_2 {
struct X {
  void f(int) { }
  static void f(double) { }
  
  int g() {
    auto L = [=](auto a) { 
      return [](int i) {
        return [=](auto b) {
          f(b); //expected-error{{'this' cannot}}
          int x = i;
        };
      };
    };
    auto M = L(0.0); 
    auto N = M(3);
    N(5); // NOT OK expected-note{{in instantiation of}}
    return 0;
  }
};
int run = X{}.g();
}
namespace diagnose_errors_early_in_generic_lambdas {

int foo()
{

  { // This variable is used and must be caught early, do not need instantiation
    const int x = 0; //expected-note{{declared}}
    auto L = [](auto a) { //expected-note{{begins}}
      const int &r = x; //expected-error{{variable}}      
    };
  }
  { // This variable is not used 
    const int x = 0; 
    auto L = [](auto a) { 
      int i = x;       
    };
  }
  { 
  
    const int x = 0; //expected-note{{declared}}
    auto L = [=](auto a) { // <-- #A
      const int y = 0;
      return [](auto b) { //expected-note{{begins}}
        int c[sizeof(b)];
        f(x, c);
        f(y, c);
        int i = x;
        // This use will always be an error regardless of instantatiation
        // so diagnose this early.
        const int &r = x; //expected-error{{variable}}
      };
    };
    
  }
  return 0;
}

int run = foo();
}

namespace generic_nongenerics_interleaved_1 {
int foo() {
  {
    auto L = [](int a) {
      int y = 10;
      return [=](auto b) { 
        return a + y;
      };
    };
    auto M = L(3);
    M(5);
  }
  {
    int x;
    auto L = [](int a) {
      int y = 10;
      return [=](auto b) { 
        return a + y;
      };
    };
    auto M = L(3);
    M(5);
  }
  {
    // FIXME: why are there 2 error messages here?
    int x;
    auto L = [](auto a) { //expected-note {{declared here}}
      int y = 10; //expected-note {{declared here}}
      return [](int b) { //expected-note 2{{expression begins here}}
        return [=] (auto c) {
          return a + y; //expected-error 2{{cannot be implicitly captured}}
        };
      };
    };
  }
  {
    int x;
    auto L = [](auto a) { 
      int y = 10; 
      return [=](int b) { 
        return [=] (auto c) {
          return a + y; 
        };
      };
    };
  }
  return 1;
}

int run = foo();
}
namespace dont_capture_refs_if_initialized_with_constant_expressions {

auto foo(int i) {
  // This is surprisingly not odr-used within the lambda!
  static int j;
  j = i;
  int &ref_j = j;
  return [](auto a) { return ref_j; }; // ok
}

template<class T>
auto foo2(T t) {
  // This is surprisingly not odr-used within the lambda!
  static T j;
  j = t;
  T &ref_j = j;
  return [](auto a) { return ref_j; }; // ok
}

int do_test() {
  auto L = foo(3);
  auto L_int = L(3);
  auto L_char = L('a');
  auto L1 = foo2(3.14);
  auto L1_int = L1(3);
  auto L1_char = L1('a');
  return 0;
}

} // dont_capture_refs_if_initialized_with_constant_expressions

namespace test_conversion_to_fptr {

template<class T> struct X {

  T (*fp)(T) = [](auto a) { return a; };
  
};

X<int> xi;

template<class T> 
void fooT(T t, T (*fp)(T) = [](auto a) { return a; }) {
  fp(t);
}

int test() {
{
  auto L = [](auto a) { return a; };
  int (*fp)(int) = L;
  fp(5);
  L(3);
  char (*fc)(char) = L;
  fc('b');
  L('c');
  double (*fd)(double) = L;
  fd(3.14);
  fd(6.26);
  L(4.25);
}
{
  auto L = [](auto a) ->int { return a; }; //expected-note 2{{candidate template ignored}}
  int (*fp)(int) = L;
  char (*fc)(char) = L; //expected-error{{no viable conversion}}
  double (*fd)(double) = L; //expected-error{{no viable conversion}}
}
{
  int x = 5;
  auto L = [=](auto b, char c = 'x') {
    int i = x;
    return [](auto a) ->decltype(a) { return a; };
  };
  int (*fp)(int) = L(8);
  fp(5);
  L(3);
  char (*fc)(char) = L('a');
  fc('b');
  L('c');
  double (*fd)(double) = L(3.14);
  fd(3.14);
  fd(6.26);

}
{
 auto L = [=](auto b) {
    return [](auto a) ->decltype(b)* { return (decltype(b)*)0; };
  };
  int* (*fp)(int) = L(8);
  fp(5);
  L(3);
  char* (*fc)(char) = L('a');
  fc('b');
  L('c');
  double* (*fd)(double) = L(3.14);
  fd(3.14);
  fd(6.26);
}
{
 auto L = [=](auto b) {
    return [](auto a) ->decltype(b)* { return (decltype(b)*)0; }; //expected-note{{candidate template ignored}}
  };
  char* (*fp)(int) = L('8');
  fp(5);
  char* (*fc)(char) = L('a');
  fc('b');
  double* (*fi)(int) = L(3.14);
  fi(5);
  int* (*fi2)(int) = L(3.14); //expected-error{{no viable conversion}}
}

{
 auto L = [=](auto b) {
    return [](auto a) { 
      return [=](auto c) { 
        return [](auto d) ->decltype(a + b + c + d) { return d; }; 
      }; 
    }; 
  };
  int (*fp)(int) = L('8')(3)(short{});
  double (*fs)(char) = L(3.14)(short{})('4');
}

  fooT(3);
  fooT('a');
  fooT(3.14);
  fooT("abcdefg");
  return 0;
}
int run2 = test();

}


namespace this_capture {
void f(char, int) { }
template<class T> 
void f(T, const int&) { }

struct X {
  int x = 0;
  void foo() {
    auto L = [=](auto a) {
         return [=](auto b) {
            //f(a, x++);
            x++;
         };
    };
    L('a')(5);
    L('b')(4);
    L(3.14)('3');
    
  }

};

int run = (X{}.foo(), 0);

namespace this_capture_unresolvable {
struct X {
  void f(int) { }
  static void f(double) { }
  
  int g() {
    auto lam = [=](auto a) { f(a); }; // captures 'this'
    lam(0); // ok.
    lam(0.0); // ok.
    return 0;
  }
  int g2() {
    auto lam = [](auto a) { f(a); }; // expected-error{{'this'}}
    lam(0); // expected-note{{in instantiation of}}
    lam(0.0); // ok.
    return 0;
  }
  double (*fd)(double) = [](auto a) { f(a); return a; };
  
};

int run = X{}.g();

}

namespace check_nsdmi_and_this_capture_of_member_functions {

struct FunctorDouble {
  template<class T> FunctorDouble(T t) { t(2.14); };
};
struct FunctorInt {
  template<class T> FunctorInt(T t) { t(2); }; //expected-note{{in instantiation of}}
};

template<class T> struct YUnresolvable {
  void f(int) { }
  static void f(double) { }
  
  T t = [](auto a) { f(a); return a; }; 
  T t2 = [=](auto b) { f(b); return b; };
};

template<class T> struct YUnresolvable2 {
  void f(int) { }
  static void f(double) { }
  
  T t = [](auto a) { f(a); return a; }; //expected-error{{'this'}} \
                                        //expected-note{{in instantiation of}}
  T t2 = [=](auto b) { f(b); return b; };
};


YUnresolvable<FunctorDouble> yud;
// This will cause an error since it call's with an int and calls a member function.
YUnresolvable2<FunctorInt> yui;


template<class T> struct YOnlyStatic {
  static void f(double) { }
  
  T t = [](auto a) { f(a); return a; };
};
YOnlyStatic<FunctorDouble> yos;
template<class T> struct YOnlyNonStatic {
  void f(int) { }
  
  T t = [](auto a) { f(a); return a; }; //expected-error{{'this'}}
};


}


namespace check_nsdmi_and_this_capture_of_data_members {

struct FunctorDouble {
  template<class T> FunctorDouble(T t) { t(2.14); };
};
struct FunctorInt {
  template<class T> FunctorInt(T t) { t(2); }; 
};

template<class T> struct YThisCapture {
  const int x = 10;
  static double d; 
  T t = [](auto a) { return x; }; //expected-error{{'this'}}
  T t2 = [](auto b) {  return d; };
  T t3 = [this](auto a) {
          return [=](auto b) {
            return x;
         };
  };
  T t4 = [=](auto a) {
          return [=](auto b) {
            return x;
         };
  };
  T t5 = [](auto a) {
          return [=](auto b) {
            return x;  //expected-error{{'this'}}
         };
  };
};

template<class T> double YThisCapture<T>::d = 3.14;


}


#ifdef DELAYED_TEMPLATE_PARSING
template<class T> void foo_no_error(T t) { 
  auto L = []()  
    { return t; }; 
}
template<class T> void foo(T t) { //expected-note 2{{declared here}}
  auto L = []()  //expected-note 2{{begins here}}
    { return t; }; //expected-error 2{{cannot be implicitly captured}}
}
template void foo(int); //expected-note{{in instantiation of}}

#else

template<class T> void foo(T t) { //expected-note{{declared here}}
  auto L = []()  //expected-note{{begins here}}
    { return t; }; //expected-error{{cannot be implicitly captured}}
}

#endif
}

namespace no_this_capture_for_static {

struct X {
  static void f(double) { }
  
  int g() {
    auto lam = [=](auto a) { f(a); }; 
    lam(0); // ok.
    ASSERT_NO_CAPTURES(lam);
    return 0;
  }
};

int run = X{}.g();
}

namespace this_capture_for_non_static {

struct X {
  void f(double) { }
  
  int g() {
    auto L = [=](auto a) { f(a); }; 
    L(0); 
    auto L2 = [](auto a) { f(a); }; //expected-error {{cannot be implicitly captured}}
    return 0;
  }
};

int run = X{}.g();
}

namespace this_captures_with_num_args_disambiguation {

struct X {
  void f(int) { }
  static void f(double, int i) { }
  int g() {
    auto lam = [](auto a) { f(a, a); }; 
    lam(0);
    return 0;
  }
};

int run = X{}.g();
}
namespace enclosing_function_is_template_this_capture {
// Only error if the instantiation tries to use the member function.
struct X {
  void f(int) { }
  static void f(double) { }
  template<class T>
  int g(T t) {
    auto L = [](auto a) { f(a); }; //expected-error{{'this'}} 
    L(t); // expected-note{{in instantiation of}}
    return 0;
  }
};

int run = X{}.g(0.0); // OK.
int run2 = X{}.g(0);  // expected-note{{in instantiation of}}


}

namespace enclosing_function_is_template_this_capture_2 {
// This should error, even if not instantiated, since
// this would need to be captured.
struct X {
  void f(int) { }
  template<class T>
  int g(T t) {
    auto L = [](auto a) { f(a); }; //expected-error{{'this'}} 
    L(t); 
    return 0;
  }
};

}


namespace enclosing_function_is_template_this_capture_3 {
// This should not error, this does not need to be captured.
struct X {
  static void f(int) { }
  template<class T>
  int g(T t) {
    auto L = [](auto a) { f(a); };  
    L(t); 
    return 0;
  }
};

int run = X{}.g(0.0); // OK.
int run2 = X{}.g(0);  // OK.

}

namespace nested_this_capture_1 {
struct X {
  void f(int) { }
  static void f(double) { }
  
  int g() {
    auto L = [=](auto a) { 
      return [this]() {
        return [=](auto b) {
          f(b); 
        };
      };
    };
    auto M = L(0);
    auto N = M();
    N(5);    
    return 0;
  }
};

int run = X{}.g();

}


namespace nested_this_capture_2 {
struct X {
  void f(int) { }
  static void f(double) { }
  
  int g() {
    auto L = [=](auto a) { 
      return [&]() {
        return [=](auto b) {
          f(b);  
        };
      };
    };
    auto M = L(0);
    auto N = M();
    N(5);   
    N(3.14);    
    return 0;
  }
};

int run = X{}.g();

}

namespace nested_this_capture_3_1 {
struct X {
  template<class T>
  void f(int, T t) { }
  template<class T>
  static void f(double, T t) { }
  
  int g() {
    auto L = [=](auto a) { 
      return [&](auto c) {
        return [=](auto b) {
          f(b, c); 
        };
      };
    };
    auto M = L(0);
    auto N = M('a');
    N(5); 
    N(3.14);    
    return 0;
  }
};

int run = X{}.g();

}


namespace nested_this_capture_3_2 {
struct X {
  void f(int) { }
  static void f(double) { }
  
  int g() {
    auto L = [=](auto a) { 
      return [](int i) {
        return [=](auto b) {
          f(b); //expected-error {{'this' cannot}}
          int x = i;
        };
      };
    };
    auto M = L(0.0); 
    auto N = M(3);
    N(5); //expected-note {{in instantiation of}}
    N(3.14); // OK.    
    return 0;
  }
};

int run = X{}.g();

}

namespace nested_this_capture_4 {
struct X {
  void f(int) { }
  static void f(double) { }
  
  int g() {
    auto L = [](auto a) { 
      return [=](auto i) {
        return [=](auto b) {
          f(b); //expected-error {{'this' cannot}}
          int x = i;
        };
      };
    };
    auto M = L(0.0); 
    auto N = M(3);
    N(5); //expected-note {{in instantiation of}}
    N(3.14); // OK.    
    return 0;
  }
};

int run = X{}.g();

}
namespace capture_enclosing_function_parameters {


inline auto foo(int x) {
  int i = 10;
  auto lambda = [=](auto z) { return x + z; };
  return lambda;
}

int foo2() {
  auto L = foo(3);
  L(4);
  L('a');
  L(3.14);
  return 0;
}

inline auto foo3(int x) {
  int local = 1;
  auto L = [=](auto a) {
        int i = a[local];    
        return  [=](auto b) mutable {
          auto n = b;
          return [&, n](auto c) mutable {
            ++local;
            return ++x;
          };
        };
  };
  auto M = L("foo-abc");
  auto N = M("foo-def");
  auto O = N("foo-ghi");
  
  return L;
}

int main() {
  auto L3 = foo3(3);
  auto M3 = L3("L3-1");
  auto N3 = M3("M3-1");
  auto O3 = N3("N3-1");
  N3("N3-2");
  M3("M3-2");
  M3("M3-3");
  L3("L3-2");
}
} // end ns

namespace capture_arrays {

inline int sum_array(int n) {
  int array2[5] = { 1, 2, 3, 4, 5};
  
  auto L = [=](auto N) -> int {  
    int sum = 0;
    int array[5] = { 1, 2, 3, 4, 5 };
    sum += array2[sum];
    sum += array2[N];    
    return 0;
  };
  L(2);
  return L(n);
}
}

namespace capture_non_odr_used_variable_because_named_in_instantiation_dependent_expressions {

// even though 'x' is not odr-used, it should be captured.

int test() {
  const int x = 10;
  auto L = [=](auto a) {
    (void) +x + a;
  };
  ASSERT_CLOSURE_SIZE_EXACT(L, sizeof(x));
}

} //end ns
#ifdef MS_EXTENSIONS
namespace explicit_spec {
template<class R> struct X {
  template<class T> int foo(T t) {
    auto L = [](auto a) { return a; };
    L(&t);
    return 0;
  }
  
  template<> int foo<char>(char c) { //expected-warning{{explicit specialization}}
    const int x = 10;
    auto LC = [](auto a) { return a; };
    R r;
    LC(&r);
    auto L = [=](auto a) {
      return [=](auto b) {
        int d[sizeof(a)];
        f(x, d);
      };
    };
    auto M = L(1);
    
    ASSERT_NO_CAPTURES(M);
    return 0;
  }
  
}; 

int run_char = X<int>{}.foo('a');
int run_int = X<double>{}.foo(4);
}
#endif // MS_EXTENSIONS

namespace nsdmi_capturing_this {
struct X {
  int m = 10;
  int n = [this](auto) { return m; }(20);
};

template<class T>
struct XT {
  T m = 10;
  T n = [this](auto) { return m; }(20);
};

XT<int> xt{};


}
