// RUN: %clang_cc1 -std=c++23 -verify -fsyntax-only %s

template <typename T, typename U>
constexpr bool is_same = false;

template <typename T>
constexpr bool is_same<T, T> = true;

void f() {

  int y;

  static_assert(is_same<const int &,
                        decltype([x = 1] -> decltype((x)) { return x; }())>);

  static_assert(is_same<int &,
                        decltype([x = 1] mutable -> decltype((x)) { return x; }())>);

  static_assert(is_same<const int &,
                        decltype([=] -> decltype((y)) { return y; }())>);

  static_assert(is_same<int &,
                        decltype([=] mutable -> decltype((y)) { return y; }())>);

  static_assert(is_same<const int &,
                        decltype([=] -> decltype((y)) { return y; }())>);

  static_assert(is_same<int &,
                        decltype([=] mutable -> decltype((y)) { return y; }())>);

  auto ref = [&x = y](
                 decltype([&](decltype(x)) { return 0; }) y) {
    return x;
  };
}

void test_noexcept() {

  int y;

  static_assert(noexcept([x = 1] noexcept(is_same<const int &, decltype((x))>) {}()));
  static_assert(noexcept([x = 1] mutable noexcept(is_same<int &, decltype((x))>) {}()));
  static_assert(noexcept([y] noexcept(is_same<const int &, decltype((y))>) {}()));
  static_assert(noexcept([y] mutable noexcept(is_same<int &, decltype((y))>) {}()));
  static_assert(noexcept([=] noexcept(is_same<const int &, decltype((y))>) {}()));
  static_assert(noexcept([=] mutable noexcept(is_same<int &, decltype((y))>) {}()));
  static_assert(noexcept([&] noexcept(is_same<int &, decltype((y))>) {}()));
  static_assert(noexcept([&] mutable noexcept(is_same<int &, decltype((y))>) {}()));
}

template<typename T>
void test_requires() {

  int x;

  [x = 1]() requires is_same<const int &, decltype((x))> {}
  ();
  [x = 1]() mutable requires is_same<int &, decltype((x))> {}
  ();
  [x]() requires is_same<const int &, decltype((x))> {}
  ();
  [x]() mutable requires is_same<int &, decltype((x))> {}
  ();
  [=]() requires is_same<const int &, decltype((x))> {}
  ();
  [=]() mutable requires is_same<int &, decltype((x))> {}
  ();
  [&]() requires is_same<int &, decltype((x))> {}
  ();
  [&]() mutable requires is_same<int &, decltype((x))> {}
  ();
  [&x]() requires is_same<int &, decltype((x))> {}
  ();
  [&x]() mutable requires is_same<int &, decltype((x))> {}
  ();

  [x = 1]() requires is_same<const int &, decltype((x))> {} ();
  [x = 1]() mutable requires is_same<int &, decltype((x))> {} ();
}

void use() {
  test_requires<int>();
}

void err() {
  int y, z;
  (void)[x = 1]<typename T>
  requires(is_same<const int &, decltype((x))>) {};

  (void)[x = 1]<typename T = decltype((x))>{};

  (void)[=]<typename T = decltype((y))>{};

  (void)[z]<typename T = decltype((z))>{};
}

void gnu_attributes() {
  int y;
  (void)[=]() __attribute__((diagnose_if(!is_same<decltype((y)), const int &>, "wrong type", "warning"))){}();
  // expected-warning@-1 {{wrong type}} expected-note@-1{{'diagnose_if' attribute on 'operator()'}}
  (void)[=]() __attribute__((diagnose_if(!is_same<decltype((y)), int &>, "wrong type", "warning"))){}();

  (void)[=]() __attribute__((diagnose_if(!is_same<decltype((y)), int &>, "wrong type", "warning"))) mutable {}();
  (void)[=]() __attribute__((diagnose_if(!is_same<decltype((y)), const int &>, "wrong type", "warning"))) mutable {}();
  // expected-warning@-1 {{wrong type}} expected-note@-1{{'diagnose_if' attribute on 'operator()'}}


  (void)[x=1]() __attribute__((diagnose_if(!is_same<decltype((x)), const int &>, "wrong type", "warning"))){}();
  // expected-warning@-1 {{wrong type}} expected-note@-1{{'diagnose_if' attribute on 'operator()'}}
  (void)[x=1]() __attribute__((diagnose_if(!is_same<decltype((x)), int &>, "wrong type", "warning"))){}();

  (void)[x=1]() __attribute__((diagnose_if(!is_same<decltype((x)), int &>, "wrong type", "warning"))) mutable {}();
  (void)[x=1]() __attribute__((diagnose_if(!is_same<decltype((x)), const int &>, "wrong type", "warning"))) mutable {}();
  // expected-warning@-1 {{wrong type}} expected-note@-1{{'diagnose_if' attribute on 'operator()'}}
}

void nested() {
  int x, y, z;
  (void)[&](
      decltype([&](
                   decltype([=](
                                decltype([&](
                                             decltype([&](decltype(x)) {})) {})) {})) {})){};

  (void)[&](
      decltype([&](
                   decltype([&](
                                decltype([&](
                                             decltype([&](decltype(y)) {})) {})) {})) {})){};

  (void)[=](
      decltype([=](
                   decltype([=](
                                decltype([=](
                                             decltype([&]<decltype(z)> {})) {})) {})) {})){};
}

template <typename T, typename U>
void dependent(U&& u) {
  [&]() requires is_same<decltype(u), T> {}();
}

template <typename T>
void dependent_init_capture(T x = 0) {
  [ y = x + 1, x ]() mutable -> decltype(y + x)
  requires(is_same<decltype((y)), int &>
        && is_same<decltype((x)), int &>) {
    return y;
  }
  ();
  [ y = x + 1, x ]() -> decltype(y + x)
  requires(is_same<decltype((y)), const int &>
        && is_same<decltype((x)), const int &>) {
    return y;
  }
  ();
}

template <typename T, typename...>
struct extract_type {
  using type = T;
};

template <typename... T>
void dependent_variadic_capture(T... x) {
  [... y = x, x... ](auto...) mutable -> typename extract_type<decltype(y)...>::type requires((is_same<decltype((y)), int &> && ...) && (is_same<decltype((x)), int &> && ...)) {
    return 0;
  }
  (x...);
  [... y = x, x... ](auto...) -> typename extract_type<decltype(y)...>::type requires((is_same<decltype((y)), const int &> && ...) && (is_same<decltype((x)), const int &> && ...)) {
    return 0;
  }
  (x...);
}

void test_dependent() {
  int v   = 0;
  int & r = v;
  const int & cr = v;
  dependent<int&>(v);
  dependent<int&>(r);
  dependent<const int&>(cr);
  dependent_init_capture(0);
  dependent_variadic_capture(1, 2, 3, 4);
}

void check_params() {
  int i = 0;
  int &j = i;
  (void)[=](decltype((j)) jp, decltype((i)) ip) {
    static_assert(is_same<const int&, decltype((j))>);
    static_assert(is_same<const int &, decltype((i))>);
    static_assert(is_same<int &, decltype((jp))>);
    static_assert(is_same<int &, decltype((ip))>);
  };

  (void)[=](decltype((j)) jp, decltype((i)) ip) mutable {
    static_assert(is_same<int &, decltype((j))>);
    static_assert(is_same<int &, decltype((i))>);
    static_assert(is_same<int &, decltype((jp))>);
    static_assert(is_same<int &, decltype((ip))>);
    static_assert(is_same<int &, decltype(jp)>);
    static_assert(is_same<int &, decltype(ip)>);
  };

  (void)[a = 0](decltype((a)) ap) mutable {
    static_assert(is_same<int &, decltype((a))>);
    static_assert(is_same<int, decltype(a)>);
    static_assert(is_same<int &, decltype(ap)>);
  };
  (void)[a = 0](decltype((a)) ap) {
    static_assert(is_same<const int &, decltype((a))>);
    static_assert(is_same<int, decltype(a)>);
    static_assert(is_same<int&, decltype((ap))>);
  };
}

template <typename T>
void check_params_tpl() {
  T i = 0;
  T &j = i;
  (void)[=](decltype((j)) jp, decltype((i)) ip) {
    static_assert(is_same<const int&, decltype((j))>);
    static_assert(is_same<const int &, decltype((i))>);
    static_assert(is_same<const int &, decltype((jp))>);
    static_assert(is_same<const int &, decltype((ip))>);
  };

  (void)[=](decltype((j)) jp, decltype((i)) ip) mutable {
    static_assert(is_same<int &, decltype((j))>);
    static_assert(is_same<int &, decltype((i))>);
    static_assert(is_same<int &, decltype((jp))>);
    static_assert(is_same<int &, decltype((ip))>);
    static_assert(is_same<int &, decltype(jp)>);
    static_assert(is_same<int &, decltype(ip)>);
  };

  (void)[a = 0](decltype((a)) ap) mutable {
    static_assert(is_same<int &, decltype((a))>);
    static_assert(is_same<int, decltype(a)>);
    static_assert(is_same<int &, decltype(ap)>);
  };
  (void)[a = 0](decltype((a)) ap) {
    static_assert(is_same<const int &, decltype((a))>);
    static_assert(is_same<int, decltype(a)>);
    static_assert(is_same<int&, decltype((ap))>);
  };
}

namespace GH61267 {
template <typename> concept C = true;

template<typename>
void f(int) {
  int i;
  [i]<C P>(P) {}(0);
  i = 4;
}

void test() { f<int>(0);  }

}

namespace GH65067 {

template <typename> class a {
public:
  template <typename b> void c(b f) { d<int>(f)(0); }
  template <typename, typename b> auto d(b f) {
    return [f = f](auto arg) -> a<decltype(f(arg))> { return {}; };
  }
};
a<void> e;
auto fn1() {
  e.c([](int) {});
}

}

namespace GH63675 {

template <class _Tp> _Tp __declval();
struct __get_tag {
  template <class _Tag> void operator()(_Tag);
};
template <class _ImplFn> struct __basic_sender {
  using __tag_t = decltype(__declval<_ImplFn>()(__declval<__get_tag>()));
  _ImplFn __impl_;
};
auto __make_basic_sender = []<class... _Children>(
                               _Children... __children) {
  return __basic_sender{[... __children = __children]<class _Fun>(
                     _Fun __fun) -> decltype(__fun(__children...)) {}};
};
void __trans_tmp_1() {
  __make_basic_sender(__trans_tmp_1);
}

}

namespace GH115931 {

struct Range {};

template <Range>
struct LengthPercentage {};

void reflectSum() {
  Range resultR;
  [&] (auto) -> LengthPercentage<resultR> { 
    return {};
  }(0);
}

} // namespace GH115931

namespace GH47400 {

struct Foo {};

template <int, Foo> struct Arr {};

template <int> struct S {};

constexpr void foo() {
  constexpr Foo f;
  [&]<int is>() {
    [&](Arr<is, f>) {}({}); // f constitutes an ODR-use
  }.template operator()<42>();

  constexpr int C = 1;
  [] {
    [](S<C>) { }({}); // ... while C doesn't
  }();
}

} // namespace GH47400

namespace GH84961 {

template <typename T> void g(const T &t) {}

template <typename T> void f(const T &t) {
  [t] { g(t); }();
}

void h() {
  f(h);
}

} // namespace GH84961
