1 // This tests that the coroutine heap allocation elision optimization could happen succesfully.
2 // RUN: %clang_cc1 -no-opaque-pointers -triple x86_64-unknown-linux-gnu -std=c++20 -O2 -emit-llvm %s -o - | FileCheck %s
3 
4 #include "Inputs/coroutine.h"
5 #include "Inputs/numeric.h"
6 
7 template <typename T> struct generator {
8   struct promise_type {
9     T current_value;
yield_valuegenerator::promise_type10     std::suspend_always yield_value(T value) {
11       this->current_value = value;
12       return {};
13     }
initial_suspendgenerator::promise_type14     std::suspend_always initial_suspend() { return {}; }
final_suspendgenerator::promise_type15     std::suspend_always final_suspend() noexcept { return {}; }
get_return_objectgenerator::promise_type16     generator get_return_object() { return generator{this}; };
unhandled_exceptiongenerator::promise_type17     void unhandled_exception() {}
return_voidgenerator::promise_type18     void return_void() {}
19   };
20 
21   struct iterator {
22     std::coroutine_handle<promise_type> _Coro;
23     bool _Done;
24 
iteratorgenerator::iterator25     iterator(std::coroutine_handle<promise_type> Coro, bool Done)
26         : _Coro(Coro), _Done(Done) {}
27 
operator ++generator::iterator28     iterator &operator++() {
29       _Coro.resume();
30       _Done = _Coro.done();
31       return *this;
32     }
33 
operator ==generator::iterator34     bool operator==(iterator const &_Right) const {
35       return _Done == _Right._Done;
36     }
37 
operator !=generator::iterator38     bool operator!=(iterator const &_Right) const { return !(*this == _Right); }
operator *generator::iterator39     T const &operator*() const { return _Coro.promise().current_value; }
operator ->generator::iterator40     T const *operator->() const { return &(operator*()); }
41   };
42 
begingenerator43   iterator begin() {
44     p.resume();
45     return {p, p.done()};
46   }
47 
endgenerator48   iterator end() { return {p, true}; }
49 
50   generator(generator const &) = delete;
generatorgenerator51   generator(generator &&rhs) : p(rhs.p) { rhs.p = nullptr; }
52 
~generatorgenerator53   ~generator() {
54     if (p)
55       p.destroy();
56   }
57 
58 private:
generatorgenerator59   explicit generator(promise_type *p)
60       : p(std::coroutine_handle<promise_type>::from_promise(*p)) {}
61 
62   std::coroutine_handle<promise_type> p;
63 };
64 
65 template <typename T>
seq()66 generator<T> seq() {
67   for (T i = {};; ++i)
68     co_yield i;
69 }
70 
71 template <typename T>
take_until(generator<T> & g,T limit)72 generator<T> take_until(generator<T> &g, T limit) {
73   for (auto &&v : g)
74     if (v < limit)
75       co_yield v;
76     else
77       break;
78 }
79 
80 template <typename T>
multiply(generator<T> & g,T factor)81 generator<T> multiply(generator<T> &g, T factor) {
82   for (auto &&v : g)
83     co_yield v *factor;
84 }
85 
86 template <typename T>
add(generator<T> & g,T adder)87 generator<T> add(generator<T> &g, T adder) {
88   for (auto &&v : g)
89     co_yield v + adder;
90 }
91 
main()92 int main() {
93   auto s = seq<int>();
94   auto t = take_until(s, 10);
95   auto m = multiply(t, 2);
96   auto a = add(m, 110);
97   return std::accumulate(a.begin(), a.end(), 0);
98 }
99 
100 // CHECK-LABEL: define{{.*}} i32 @main(
101 //   CHECK: ret i32 1190
102 //   CHECK-NOT: call{{.*}}_Znwm
103