86 lines · cpp
1// An end-to-end test to make sure things get processed correctly.2// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s -O3 | \3// RUN: FileCheck %s4 5#include "Inputs/coroutine.h"6 7struct SomeAwaitable {8 // Resume the supplied handle once the awaitable becomes ready,9 // returning a handle that should be resumed now for the sake of symmetric transfer.10 // If the awaitable is already ready, return an empty handle without doing anything.11 //12 // Defined in another translation unit. Note that this may contain13 // code that synchronizees with another thread.14 std::coroutine_handle<> Register(std::coroutine_handle<>);15};16 17// Defined in another translation unit.18void DidntSuspend();19 20struct Awaiter {21 SomeAwaitable&& awaitable;22 bool suspended;23 24 bool await_ready() { return false; }25 26 std::coroutine_handle<> await_suspend(const std::coroutine_handle<> h) {27 // Assume we will suspend unless proven otherwise below. We must do28 // this *before* calling Register, since we may be destroyed by another29 // thread asynchronously as soon as we have registered.30 suspended = true;31 32 // Attempt to hand off responsibility for resuming/destroying the coroutine.33 const auto to_resume = awaitable.Register(h);34 35 if (!to_resume) {36 // The awaitable is already ready. In this case we know that Register didn't37 // hand off responsibility for the coroutine. So record the fact that we didn't38 // actually suspend, and tell the compiler to resume us inline.39 suspended = false;40 return h;41 }42 43 // Resume whatever Register wants us to resume.44 return to_resume;45 }46 47 void await_resume() {48 // If we didn't suspend, make note of that fact.49 if (!suspended) {50 DidntSuspend();51 }52 }53};54 55struct MyTask{56 struct promise_type {57 MyTask get_return_object() { return {}; }58 std::suspend_never initial_suspend() { return {}; }59 std::suspend_always final_suspend() noexcept { return {}; }60 void unhandled_exception();61 62 Awaiter await_transform(SomeAwaitable&& awaitable) {63 return Awaiter{static_cast<SomeAwaitable&&>(awaitable)};64 }65 };66};67 68MyTask FooBar() {69 co_await SomeAwaitable();70}71 72// CHECK-LABEL: @_Z6FooBarv73// CHECK: %[[to_resume:.*]] = {{.*}}call ptr @_ZN13SomeAwaitable8RegisterESt16coroutine_handleIvE74// CHECK-NEXT: %[[to_bool:.*]] = icmp eq ptr %[[to_resume]], null75// CHECK-NEXT: br i1 %[[to_bool]], label %[[then:.*]], label %[[else:.*]]76 77// CHECK: [[then]]:78// We only access the coroutine frame conditionally as the sources did.79// CHECK: store i8 0,80// CHECK-NEXT: br label %[[else]]81 82// CHECK: [[else]]:83// No more access to the coroutine frame until suspended.84// CHECK-NOT: store85// CHECK: }86