// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++20 -fsyntax-only -verify -Wall -Wextra -Wno-c++23-lambda-attributes
#include "Inputs/std-coroutine.h"

using std::suspend_always;
using std::suspend_never;


namespace std {
  struct nothrow_t {};
  constexpr nothrow_t nothrow = {};
}

using SizeT = decltype(sizeof(int));

void* operator new(SizeT __sz, const std::nothrow_t&) noexcept;

template <typename T> struct [[clang::coro_return_type]] Gen {
  struct promise_type {
    Gen<T> get_return_object() {
      return {};
    }
    static Gen<T> get_return_object_on_allocation_failure() {
      return {};
    }
    suspend_always initial_suspend();
    suspend_always final_suspend() noexcept;
    void unhandled_exception();
    void return_value(T t);

    template <typename U>
    auto await_transform(const Gen<U> &) {
      struct awaitable {
        bool await_ready() noexcept { return false; }
        void await_suspend(std::coroutine_handle<>) noexcept {}
        U await_resume() noexcept { return {}; }
      };
      return awaitable{};
    }
  };
};

Gen<int> foo_coro(int b);
Gen<int> foo_coro(int b) { co_return b; }

[[clang::coro_wrapper]] Gen<int> marked_wrapper1(int b) { return foo_coro(b); }

// expected-error@+1 {{neither a coroutine nor a coroutine wrapper}}
Gen<int> non_marked_wrapper(int b) { return foo_coro(b); }

namespace using_decl {
template <typename T> using Co = Gen<T>;

[[clang::coro_wrapper]] Co<int> marked_wrapper1(int b) { return foo_coro(b); }

// expected-error@+1 {{neither a coroutine nor a coroutine wrapper}}
Co<int> non_marked_wrapper(int b) { return foo_coro(b); }
} // namespace using_decl

namespace lambdas {

void foo() {
  auto coro_lambda = []() -> Gen<int> {
    co_return 1;
  };
  // expected-error@+1 {{neither a coroutine nor a coroutine wrapper}}
  auto not_allowed_wrapper = []() -> Gen<int> {
    return foo_coro(1);
  };
  auto allowed_wrapper = [] [[clang::coro_wrapper]] () -> Gen<int> {
    return foo_coro(1);
  };
}

Gen<int> coro_containing_lambda() {
  // expected-error@+1 {{neither a coroutine nor a coroutine wrapper}}
  auto wrapper_lambda = []() -> Gen<int> {
    return foo_coro(1);
  };
  co_return co_await wrapper_lambda();
}
} // namespace lambdas

namespace std_function {
namespace std {
template <typename> class function;

template <typename ReturnValue, typename... Args>
class function<ReturnValue(Args...)> {
public:
  template <typename T> function &operator=(T) {}
  template <typename T> function(T) {}
  // expected-error@+1 {{neither a coroutine nor a coroutine wrapper}}
  ReturnValue operator()(Args... args) const {
    return callable_->Invoke(args...);  // expected-note {{in instantiation of member}}
  }

private:
  class Callable {
  public:
    // expected-error@+1 {{neither a coroutine nor a coroutine wrapper}}
    ReturnValue Invoke(Args...) const { return {}; }
  };
  Callable* callable_;
};
} // namespace std

void use_std_function() {
  std::function<int(bool)> foo = [](bool b) { return b ? 1 : 2; };
  // expected-error@+1 {{neither a coroutine nor a coroutine wrapper}}
  std::function<Gen<int>(bool)> test1 = [](bool b) {
    return foo_coro(b);
  };
  std::function<Gen<int>(bool)> test2 = [](bool) -> Gen<int> {
    co_return 1;
  };
  std::function<Gen<int>(bool)> test3 = foo_coro;

  foo(true);   // Fine.
  test1(true); // expected-note 2 {{in instantiation of member}}
  test2(true);
  test3(true);
}
} // namespace std_function

// different_promise_type
class [[clang::coro_return_type]] Task{};
struct my_promise_type {
  Task get_return_object() {
    return {};
  }
  suspend_always initial_suspend();
  suspend_always final_suspend() noexcept;
  void unhandled_exception();
};
namespace std {
template<> class coroutine_traits<Task, int> {
    using promise_type = my_promise_type;
};
} // namespace std
// expected-error@+1 {{neither a coroutine nor a coroutine wrapper}}
Task foo(int) { return Task{}; }
