[libcxx] r304101 - [coroutines] Add end-to-end tests within libc++

Eric Fiselier via cfe-commits cfe-commits at lists.llvm.org
Sun May 28 12:38:22 PDT 2017


Author: ericwf
Date: Sun May 28 14:38:21 2017
New Revision: 304101

URL: http://llvm.org/viewvc/llvm-project?rev=304101&view=rev
Log:
[coroutines] Add end-to-end tests within libc++

This patch adds end-to-end/breathing tests for coroutines
into libc++. The tests aren't specifically to test libc++ requirements
but instead are intented to ensure coroutines are working fine in general.

Although libc++ isn't exactly the most correct place for these tests
to live, there is one major advantage. The libc++ test suite is also
used by MSVC and by adding the tests here it ensures they will be
run against all currently available coroutine implementations.

Added:
    libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/
    libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/await_result.sh.cpp
    libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/bool_await_suspend.sh.cpp
    libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/expected.sh.cpp
    libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/fullexpr-dtor.sh.cpp
    libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/generator.sh.cpp
    libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/go.sh.cpp
    libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/multishot_func.sh.cpp
    libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/oneshot_func.sh.cpp
    libcxx/trunk/test/support/coroutine_types.h

Added: libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/await_result.sh.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/await_result.sh.cpp?rev=304101&view=auto
==============================================================================
--- libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/await_result.sh.cpp (added)
+++ libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/await_result.sh.cpp Sun May 28 14:38:21 2017
@@ -0,0 +1,72 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03, c++11
+// REQUIRES: fcoroutines-ts
+
+// RUN: %build -fcoroutines-ts
+// RUN: %run
+
+#include <experimental/coroutine>
+#include <cassert>
+
+using namespace std::experimental;
+
+struct coro_t {
+  struct promise_type {
+    coro_t get_return_object() {
+      coroutine_handle<promise_type>{};
+      return {};
+    }
+    suspend_never initial_suspend() { return {}; }
+    suspend_never final_suspend() { return {}; }
+    void return_void(){}
+    static void unhandled_exception() {}
+  };
+};
+
+struct B {
+  ~B() {}
+  bool await_ready() { return true; }
+  B await_resume() { return {}; }
+  template <typename F> void await_suspend(F) {}
+};
+
+
+struct A {
+  ~A(){}
+  bool await_ready() { return true; }
+  int await_resume() { return 42; }
+  template <typename F> void await_suspend(F) {}
+};
+
+int last_value = -1;
+void set_value(int x) {
+  last_value = x;
+}
+
+coro_t f(int n) {
+  if (n == 0) {
+    set_value(0);
+    co_return;
+  }
+  int val = co_await A{};
+  set_value(42);
+}
+
+coro_t g() { B val = co_await B{}; }
+
+int main() {
+  last_value = -1;
+  f(0);
+  assert(last_value == 0);
+  f(1);
+  assert(last_value == 42);
+}

Added: libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/bool_await_suspend.sh.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/bool_await_suspend.sh.cpp?rev=304101&view=auto
==============================================================================
--- libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/bool_await_suspend.sh.cpp (added)
+++ libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/bool_await_suspend.sh.cpp Sun May 28 14:38:21 2017
@@ -0,0 +1,67 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03, c++11
+// REQUIRES: fcoroutines-ts
+
+// RUN: %build -fcoroutines-ts
+// RUN: %run
+
+#include <experimental/coroutine>
+#include <cassert>
+
+using namespace std::experimental;
+
+struct coro_t {
+  struct promise_type {
+    coro_t get_return_object() {
+      coroutine_handle<promise_type>{};
+      return {};
+    }
+    suspend_never initial_suspend() { return {}; }
+    suspend_never final_suspend() { return {}; }
+    void return_void(){}
+    void unhandled_exception() {}
+  };
+};
+
+struct NoSuspend {
+  bool await_ready() { return false; }
+  void await_resume() {}
+  template <typename F> bool await_suspend(F) { return false; }
+};
+
+struct DoSuspend {
+  bool await_ready() { return false; }
+  void await_resume() {}
+  template <typename F> bool await_suspend(F) { return true; }
+};
+
+bool f_started, f_resumed = false;
+coro_t f() {
+  f_started = true;
+  co_await DoSuspend{};
+  f_resumed = true;
+}
+
+bool g_started, g_resumed = false;
+coro_t g() {
+  g_started = true;
+  co_await NoSuspend{};
+  g_resumed = true;
+}
+
+int main() {
+  assert(!f_started && !f_resumed && !g_started && !g_resumed);
+  f();
+  assert(f_started && !f_resumed);
+  g();
+  assert(g_started && g_resumed);
+}

Added: libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/expected.sh.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/expected.sh.cpp?rev=304101&view=auto
==============================================================================
--- libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/expected.sh.cpp (added)
+++ libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/expected.sh.cpp Sun May 28 14:38:21 2017
@@ -0,0 +1,93 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03, c++11
+// REQUIRES: fcoroutines-ts
+
+// RUN: %build -fcoroutines-ts
+// RUN: %run
+
+#include <experimental/coroutine>
+#include <cassert>
+using namespace std::experimental;
+
+struct error {};
+
+template <typename T, typename Error = int>
+struct expected {
+
+  struct Data {
+    T val;
+    Error error;
+  };
+  Data data;
+
+  struct DataPtr {
+    Data *p;
+    ~DataPtr() { delete p; }
+  };
+
+  expected() {}
+  expected(T val) : data{std::move(val),{}} {}
+  expected(struct error, Error error) : data{{}, std::move(error)} {}
+  expected(DataPtr & p) : data{std::move(p.p->val), std::move(p.p->error)} {}
+
+  struct promise_type {
+    Data* data;
+    DataPtr get_return_object() { data = new Data; return {data}; }
+    suspend_never initial_suspend() { return {}; }
+    suspend_never final_suspend() { return {}; }
+    void return_value(T v) { data->val = std::move(v); data->error = {};}
+    void unhandled_exception() {}
+  };
+
+  bool await_ready() { return !data.error; }
+  T await_resume() { return std::move(data.val); }
+  void await_suspend(coroutine_handle<promise_type> h) {
+    h.promise().data->error =std::move(data.error);
+    h.destroy();
+  }
+
+  T const& value() { return data.val; }
+  Error const& error() { return data.error; }
+};
+
+expected<int> g() { return {0}; }
+expected<int> h() { return {error{}, 42}; }
+
+extern "C" void print(int);
+
+bool f1_started, f1_resumed = false;
+expected<int> f1() {
+  f1_started = true;
+  (void)(co_await g());
+  f1_resumed = true;
+  co_return 100;
+}
+
+bool f2_started, f2_resumed = false;
+expected<int> f2() {
+  f2_started = true;
+  (void)(co_await h());
+  f2_resumed = true;
+  co_return 200;
+}
+
+int main() {
+  auto c1 = f1();
+  assert(f1_started && f1_resumed);
+  assert(c1.value() == 100);
+  assert(c1.error() == 0);
+
+  auto c2 = f2();
+  assert(f2_started && !f2_resumed);
+  assert(c2.value() == 0);
+  assert(c2.error() == 42);
+}

Added: libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/fullexpr-dtor.sh.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/fullexpr-dtor.sh.cpp?rev=304101&view=auto
==============================================================================
--- libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/fullexpr-dtor.sh.cpp (added)
+++ libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/fullexpr-dtor.sh.cpp Sun May 28 14:38:21 2017
@@ -0,0 +1,113 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03, c++11
+// REQUIRES: fcoroutines-ts
+
+// RUN: %build -fcoroutines-ts
+// RUN: %run
+
+#include <experimental/coroutine>
+#include <cassert>
+
+using namespace std::experimental;
+
+int alive = 0;
+int ctor_called = 0;
+int dtor_called = 0;
+void reset() {
+  assert(alive == 0);
+  alive = 0;
+  ctor_called = 0;
+  dtor_called = 0;
+}
+struct Noisy {
+  Noisy() { ++alive; ++ctor_called; }
+  Noisy(Noisy const&) = delete;
+  ~Noisy() { --alive; ++dtor_called; }
+};
+
+struct Bug {
+  bool await_ready() { return true; }
+  void await_suspend(std::experimental::coroutine_handle<>) {}
+  Noisy await_resume() { return {}; }
+};
+struct coro2 {
+  struct promise_type {
+    suspend_never initial_suspend() { return{}; }
+    suspend_never final_suspend() { return{}; }
+    coro2 get_return_object() { return{}; }
+    void return_void() {}
+    Bug yield_value(int) { return {}; }
+    void unhandled_exception() {}
+  };
+};
+
+// Checks that destructors are correctly invoked for the object returned by
+// coawait.
+// CHECK-LABEL: @a(
+coro2 a() {
+  reset();
+  {
+    auto x = co_await Bug{};
+    assert(alive == 1);
+    assert(ctor_called == 1);
+    assert(dtor_called == 0);
+  }
+  assert(alive == 0);
+  assert(dtor_called == 1);
+}
+
+coro2 b() {
+  reset();
+  {
+    co_await Bug{};
+    assert(ctor_called == 1);
+    assert(dtor_called == 1);
+    assert(alive == 0);
+  }
+  assert(ctor_called == 1);
+  assert(dtor_called == 1);
+  assert(alive == 0);
+
+}
+
+coro2 c() {
+  reset();
+  {
+    auto x = co_yield 42;
+    assert(alive == 1);
+    assert(ctor_called == 1);
+    assert(dtor_called == 0);
+  }
+  assert(alive == 0);
+  assert(ctor_called == 1);
+  assert(dtor_called == 1);
+}
+
+coro2 d() {
+  reset();
+  {
+    co_yield 42;
+    assert(ctor_called == 1);
+    assert(dtor_called == 1);
+    assert(alive == 0);
+  }
+  assert(alive == 0);
+  assert(ctor_called == 1);
+  assert(dtor_called == 1);
+}
+
+int main() {
+  a();
+  b();
+  c();
+  d();
+}

Added: libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/generator.sh.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/generator.sh.cpp?rev=304101&view=auto
==============================================================================
--- libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/generator.sh.cpp (added)
+++ libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/generator.sh.cpp Sun May 28 14:38:21 2017
@@ -0,0 +1,104 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03, c++11
+// REQUIRES: fcoroutines-ts
+
+// RUN: %build -fcoroutines-ts
+// RUN: %run
+
+#include <experimental/coroutine>
+#include <vector>
+#include <cassert>
+
+#include "coroutine_types.h"
+
+using namespace std::experimental;
+
+struct minig {
+  struct promise_type {
+    int current_value;
+    suspend_always yield_value(int value) {
+      this->current_value = value;
+      return {};
+    }
+    suspend_always initial_suspend() { return {}; }
+    suspend_always final_suspend() { return {}; }
+    minig get_return_object() { return minig{this}; };
+    void return_void() {}
+    void unhandled_exception() {}
+  };
+
+  bool move_next() {
+    p.resume();
+    return !p.done();
+  }
+  int current_value() { return p.promise().current_value; }
+
+  minig(minig &&rhs) : p(rhs.p) { rhs.p = nullptr; }
+
+  ~minig() {
+    if (p)
+      p.destroy();
+  }
+
+private:
+  explicit minig(promise_type *p)
+      : p(coroutine_handle<promise_type>::from_promise(*p)) {}
+
+  coroutine_handle<promise_type> p;
+};
+
+
+minig mini_count(int n) {
+  for (int i = 0; i < n; i++) {
+    co_yield i;
+  }
+}
+
+generator<int> count(int n) {
+  for (int i = 0; i < n; ++i)
+    co_yield i;
+}
+
+generator<int> range(int from, int n) {
+  for (int i = from; i < n; ++i)
+    co_yield i;
+}
+
+void test_count() {
+  const std::vector<int> expect = {0, 1, 2, 3, 4};
+  std::vector<int> got;
+  for (auto x : count(5))
+    got.push_back(x);
+  assert(expect == got);
+}
+
+void test_range() {
+  int sum = 0;
+   for (auto v: range(1, 20))
+      sum += v;
+   assert(sum == 190);
+}
+
+void test_mini_generator() {
+  int sum = 0;
+  auto g = mini_count(5);
+  while (g.move_next()) {
+     sum += g.current_value();
+  }
+  assert(sum == 10);
+}
+
+int main() {
+  test_count();
+  test_range();
+  test_mini_generator();
+}

Added: libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/go.sh.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/go.sh.cpp?rev=304101&view=auto
==============================================================================
--- libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/go.sh.cpp (added)
+++ libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/go.sh.cpp Sun May 28 14:38:21 2017
@@ -0,0 +1,182 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03, c++11
+// REQUIRES: fcoroutines-ts
+
+// RUN: %build -fcoroutines-ts
+// RUN: %run
+
+#include <experimental/coroutine>
+#include <cassert>
+
+using namespace std::experimental;
+
+bool cancel = false;
+
+struct goroutine
+{
+  static int const N = 10;
+  static int count;
+  static coroutine_handle<> stack[N];
+
+  static void schedule(coroutine_handle<>& rh)
+  {
+    assert(count < N);
+    stack[count++] = rh;
+    rh = nullptr;
+  }
+
+  ~goroutine() {}
+
+  static void go(goroutine) {}
+
+  static void run_one()
+  {
+    assert(count > 0);
+    stack[--count]();
+  }
+
+  struct promise_type
+  {
+    suspend_never initial_suspend() {
+      return {};
+    }
+    suspend_never final_suspend() {
+      return {};
+    }
+    void return_void() {}
+    goroutine get_return_object() {
+      return{};
+    }
+    void unhandled_exception() {}
+  };
+};
+int goroutine::count;
+coroutine_handle<> goroutine::stack[N];
+
+coroutine_handle<goroutine::promise_type> workaround;
+
+class channel;
+
+struct push_awaiter {
+  channel* ch;
+  bool await_ready() {return false; }
+  void await_suspend(coroutine_handle<> rh);
+  void await_resume() {}
+};
+
+struct pull_awaiter {
+  channel * ch;
+
+  bool await_ready();
+  void await_suspend(coroutine_handle<> rh);
+  int await_resume();
+};
+
+class channel
+{
+  using T = int;
+
+  friend struct push_awaiter;
+  friend struct pull_awaiter;
+
+  T const* pvalue = nullptr;
+  coroutine_handle<> reader = nullptr;
+  coroutine_handle<> writer = nullptr;
+public:
+  push_awaiter push(T const& value)
+  {
+    assert(pvalue == nullptr);
+    assert(!writer);
+    pvalue = &value;
+
+    return { this };
+  }
+
+  pull_awaiter pull()
+  {
+    assert(!reader);
+
+    return { this };
+  }
+
+  void sync_push(T const& value)
+  {
+    assert(!pvalue);
+    pvalue = &value;
+    assert(reader);
+    reader();
+    assert(!pvalue);
+    reader = nullptr;
+  }
+
+  auto sync_pull()
+  {
+    while (!pvalue) goroutine::run_one();
+    auto result = *pvalue;
+    pvalue = nullptr;
+    if (writer)
+    {
+      auto wr = writer;
+      writer = nullptr;
+      wr();
+    }
+    return result;
+  }
+};
+
+void push_awaiter::await_suspend(coroutine_handle<> rh)
+{
+  ch->writer = rh;
+  if (ch->reader) goroutine::schedule(ch->reader);
+}
+
+
+bool pull_awaiter::await_ready() {
+  return !!ch->writer;
+}
+void pull_awaiter::await_suspend(coroutine_handle<> rh) {
+  ch->reader = rh;
+}
+int pull_awaiter::await_resume() {
+  auto result = *ch->pvalue;
+  ch->pvalue = nullptr;
+  if (ch->writer) {
+    //goroutine::schedule(ch->writer);
+    auto wr = ch->writer;
+    ch->writer = nullptr;
+    wr();
+  }
+  return result;
+}
+
+goroutine pusher(channel& left, channel& right)
+{
+  for (;;) {
+    auto val = co_await left.pull();
+    co_await right.push(val + 1);
+  }
+}
+
+const int N = 100; //100'000'000;
+const int repeat = 1;
+
+channel* c = new channel[N + 1];
+
+int main() {
+  for (int i = 0; i < N; ++i)
+    goroutine::go(pusher(c[i], c[i + 1]));
+
+  c[0].sync_push(0);
+  int result = c[N].sync_pull();
+
+  assert(result == 100);
+}

Added: libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/multishot_func.sh.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/multishot_func.sh.cpp?rev=304101&view=auto
==============================================================================
--- libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/multishot_func.sh.cpp (added)
+++ libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/multishot_func.sh.cpp Sun May 28 14:38:21 2017
@@ -0,0 +1,91 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03, c++11
+// REQUIRES: fcoroutines-ts
+
+// RUN: %build -fcoroutines-ts
+// RUN: %run
+
+#include <experimental/coroutine>
+#include <cassert>
+
+using namespace std::experimental;
+
+// This file tests, multishot, movable std::function like thing using coroutine
+// for compile-time type erasure and unerasure.
+template <typename R> struct func {
+  struct Input {R a, b;};
+
+  struct promise_type {
+    Input* I;
+    R result;
+    func get_return_object() { return {this}; }
+    suspend_always initial_suspend() { return {}; }
+    suspend_never final_suspend() { return {}; }
+    void return_void() {}
+    template <typename F>
+    suspend_always yield_value(F&& f) {
+      result = f(I->a, I->b);
+      return {};
+    }
+    void unhandled_exception() {}
+  };
+
+  R operator()(Input I) {
+    h.promise().I = &I;
+    h.resume();
+    R result = h.promise().result;
+    return result;
+  };
+
+  func() {}
+  func(func &&rhs) : h(rhs.h) { rhs.h = nullptr; }
+  func(func const &) = delete;
+
+  func &operator=(func &&rhs) {
+    if (this != &rhs) {
+      if (h)
+        h.destroy();
+      h = rhs.h;
+      rhs.h = nullptr;
+    }
+    return *this;
+  }
+
+  template <typename F> static func Create(F f) {
+    for (;;) {
+      co_yield f;
+    }
+  }
+
+  template <typename F> func(F f) : func(Create(f)) {}
+
+  ~func() {
+    if (h)
+      h.destroy();
+  }
+
+private:
+  func(promise_type *promise)
+      : h(coroutine_handle<promise_type>::from_promise(*promise)) {}
+  coroutine_handle<promise_type> h;
+};
+
+int Do(int acc, int n, func<int> f) {
+  for (int i = 0; i < n; ++i)
+    acc = f({acc, i});
+  return acc;
+}
+
+int main() {
+  int result = Do(1, 10, [](int a, int b) {return a + b;});
+  assert(result == 46);
+}

Added: libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/oneshot_func.sh.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/oneshot_func.sh.cpp?rev=304101&view=auto
==============================================================================
--- libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/oneshot_func.sh.cpp (added)
+++ libcxx/trunk/test/std/experimental/language.support/support.coroutines/end.to.end/oneshot_func.sh.cpp Sun May 28 14:38:21 2017
@@ -0,0 +1,87 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03, c++11
+// REQUIRES: fcoroutines-ts
+
+// RUN: %build -fcoroutines-ts
+// RUN: %run
+
+#include <experimental/coroutine>
+#include <vector>
+#include <cassert>
+
+using namespace std::experimental;
+
+// This file tests, one shot, movable std::function like thing using coroutine
+// for compile-time type erasure and unerasure.
+
+template <typename R> struct func {
+  struct promise_type {
+    R result;
+    func get_return_object() { return {this}; }
+    suspend_always initial_suspend() { return {}; }
+    suspend_always final_suspend() { return {}; }
+    void return_value(R v) { result = v; }
+    void unhandled_exception() {}
+  };
+
+  R operator()() {
+    h.resume();
+    R result = h.promise().result;
+    h.destroy();
+    h = nullptr;
+    return result;
+  };
+
+  func() {}
+  func(func &&rhs) : h(rhs.h) { rhs.h = nullptr; }
+  func(func const &) = delete;
+
+  func &operator=(func &&rhs) {
+    if (this != &rhs) {
+      if (h)
+        h.destroy();
+      h = rhs.h;
+      rhs.h = nullptr;
+    }
+    return *this;
+  }
+
+  template <typename F> static func Create(F f) { co_return f(); }
+
+  template <typename F> func(F f) : func(Create(f)) {}
+
+  ~func() {
+    if (h)
+      h.destroy();
+  }
+
+private:
+  func(promise_type *promise)
+      : h(coroutine_handle<promise_type>::from_promise(*promise)) {}
+  coroutine_handle<promise_type> h;
+};
+
+std::vector<int> yielded_values = {};
+int yield(int x) { yielded_values.push_back(x); return x + 1; }
+float fyield(int x) { yielded_values.push_back(x); return x + 2; }
+
+void Do1(func<int> f) { yield(f()); }
+void Do2(func<double> f) { yield(f()); }
+
+int main() {
+  Do1([] { return yield(43); });
+  assert((yielded_values == std::vector<int>{43, 44}));
+
+  yielded_values = {};
+  Do2([] { return fyield(44); });
+  assert((yielded_values == std::vector<int>{44, 46}));
+}

Added: libcxx/trunk/test/support/coroutine_types.h
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/support/coroutine_types.h?rev=304101&view=auto
==============================================================================
--- libcxx/trunk/test/support/coroutine_types.h (added)
+++ libcxx/trunk/test/support/coroutine_types.h Sun May 28 14:38:21 2017
@@ -0,0 +1,75 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SUPPORT_COROUTINE_TYPES_H
+#define SUPPORT_COROUTINE_TYPES_H
+
+#include <experimental/coroutine>
+
+template <typename Ty> struct generator {
+  struct promise_type {
+    Ty current_value;
+    std::experimental::suspend_always yield_value(Ty value) {
+      this->current_value = value;
+      return {};
+    }
+    std::experimental::suspend_always initial_suspend() { return {}; }
+    std::experimental::suspend_always final_suspend() { return {}; }
+    generator get_return_object() { return generator{this}; };
+    void return_void() {}
+    void unhandled_exception() {}
+  };
+
+  struct iterator {
+    std::experimental::coroutine_handle<promise_type> _Coro;
+    bool _Done;
+
+    iterator(std::experimental::coroutine_handle<promise_type> Coro, bool Done)
+        : _Coro(Coro), _Done(Done) {}
+
+    iterator &operator++() {
+      _Coro.resume();
+      _Done = _Coro.done();
+      return *this;
+    }
+
+    bool operator==(iterator const &_Right) const {
+      return _Done == _Right._Done;
+    }
+
+    bool operator!=(iterator const &_Right) const { return !(*this == _Right); }
+
+    Ty const &operator*() const { return _Coro.promise().current_value; }
+
+    Ty const *operator->() const { return &(operator*()); }
+  };
+
+  iterator begin() {
+    p.resume();
+    return {p, p.done()};
+  }
+
+  iterator end() { return {p, true}; }
+
+  generator(generator &&rhs) : p(rhs.p) { rhs.p = nullptr; }
+
+  ~generator() {
+    if (p)
+      p.destroy();
+  }
+
+private:
+  explicit generator(promise_type *p)
+      : p(std::experimental::coroutine_handle<promise_type>::from_promise(*p)) {}
+
+  std::experimental::coroutine_handle<promise_type> p;
+};
+
+#endif // SUPPORT_COROUTINE_TYPES_H




More information about the cfe-commits mailing list