brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.4 KiB · 7fc9995 Raw
238 lines · cpp
1// This is reduced test case from https://github.com/llvm/llvm-project/issues/59723.2// This is not a minimal reproducer intentionally to check the compiler's ability.3// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fcxx-exceptions\4// RUN:     -fexceptions -O2 -emit-llvm %s -o - | FileCheck %s5 6#include "Inputs/coroutine.h"7 8// executor and operation base9 10class bug_any_executor;11 12struct bug_async_op_base13{14	void invoke();15 16protected:17 18	~bug_async_op_base() = default;19};20 21class bug_any_executor22{23	using op_type = bug_async_op_base;24 25public:26 27	virtual ~bug_any_executor() = default;28 29	// removing noexcept enables clang to find that the pointer has escaped30	virtual void post(op_type& op) noexcept = 0;31 32	virtual void wait() noexcept = 0;33};34 35class bug_thread_executor : public bug_any_executor36{37 38public:39 40	void start()41	{42		43	}44 45	~bug_thread_executor()46	{47	}48 49	// although this implementation is not realy noexcept due to allocation but I have a real one that is and required to be noexcept50	virtual void post(bug_async_op_base& op) noexcept override;51 52	virtual void wait() noexcept override53	{54		55	}56};57 58// task and promise59 60struct bug_final_suspend_notification61{62	virtual std::coroutine_handle<> get_waiter() = 0;63};64 65class bug_task;66 67class bug_task_promise68{69	friend bug_task;70public:71 72	bug_task get_return_object() noexcept;73 74	constexpr std::suspend_always initial_suspend() noexcept { return {}; }75 76	std::suspend_always final_suspend() noexcept 77	{78		return {};79	}80 81	void unhandled_exception() noexcept;82 83	constexpr void return_void() const noexcept {}84 85	void get_result() const86	{87		88	}89};90 91template <class T, class U>92T exchange(T &&t, U &&u) {93    T ret = t;94    t = u;95    return ret;96}97 98class bug_task99{100	friend bug_task_promise;101	using handle = std::coroutine_handle<>;102	using promise_t = bug_task_promise;103 104	bug_task(handle coro, promise_t* p) noexcept : this_coro{ coro }, this_promise{ p }105	{106	107	}108 109public:110	using promise_type = bug_task_promise;111 112    bug_task(bug_task&& other) noexcept113		: this_coro{ exchange(other.this_coro, nullptr) }, this_promise{ exchange(other.this_promise, nullptr) } { 114		115	}116 117	~bug_task()118	{119		if (this_coro)120			this_coro.destroy();121	}122 123	constexpr bool await_ready() const noexcept124	{125		return false;126	}127 128	handle await_suspend(handle waiter) noexcept129	{130		return this_coro;131	}132 133	void await_resume() 134	{135		return this_promise->get_result();136	}137 138	handle this_coro;139	promise_t* this_promise;140};141 142bug_task bug_task_promise::get_return_object() noexcept143{144	return { std::coroutine_handle<bug_task_promise>::from_promise(*this), this };145}146 147// spawn operation and spawner148 149template<class Handler>150class bug_spawn_op final : public bug_async_op_base, bug_final_suspend_notification151{152	Handler handler;153	bug_task task_;154 155public:156 157	bug_spawn_op(Handler handler, bug_task&& t)158		: handler { handler }, task_{ static_cast<bug_task&&>(t) } {}159 160	virtual std::coroutine_handle<> get_waiter() override161	{162		handler();163		return std::noop_coroutine();164	}165};166 167class bug_spawner;168 169struct bug_spawner_awaiter170{171	bug_spawner& s;172	std::coroutine_handle<> waiter;173 174	bug_spawner_awaiter(bug_spawner& s) : s{ s } {}175 176	bool await_ready() const noexcept;177 178	void await_suspend(std::coroutine_handle<> coro);179 180	void await_resume() {}181};182 183class bug_spawner184{185	friend bug_spawner_awaiter;186 187	struct final_handler_t188	{189		bug_spawner& s;190 191		void operator()()192		{193			s.awaiter_->waiter.resume();194		}195	};196 197public:198 199	bug_spawner(bug_any_executor& ex) : ex_{ ex } {}200 201	void spawn(bug_task&& t) {202		using op_t = bug_spawn_op<final_handler_t>;203		// move task into ptr204		op_t* ptr = new op_t(final_handler_t{ *this }, static_cast<bug_task&&>(t));205		++count_;206		ex_.post(*ptr); // ptr escapes here thus task escapes but clang can't deduce that unless post() is not noexcept207	}208 209	bug_spawner_awaiter wait() noexcept { return { *this }; }210 211private:212	bug_any_executor& ex_; // if bug_thread_executor& is used instead enables clang to detect the escape of the promise213	bug_spawner_awaiter* awaiter_ = nullptr;214	unsigned count_ = 0;215};216 217// test case218 219bug_task bug_spawned_task(int id, int inc)220{221	co_return;222}223 224struct A {225    A();226};227 228void throwing_fn(bug_spawner& s) {229	s.spawn(bug_spawned_task(1, 2));230    throw A{};231}232 233// Check that the coroutine frame of bug_spawned_task are allocated from operator new.234// CHECK: define{{.*}}@_Z11throwing_fnR11bug_spawner235// CHECK-NOT: alloc236// CHECK: %[[CALL:.+]] = {{.*}}@_Znwm(i64{{.*}} 24)237// CHECK: store ptr @_Z16bug_spawned_taskii.resume, ptr %[[CALL]]238