title: c++ coroutine continuous loop causes stack overflow
body: <p>I got a stack overflow exception in visual studio inside a coroutine in a loop and found that the loop had a bug that prevented it from termination but I wondered why the stack was overflowed ? the coroutine might not even was using the stack but the heap instead and even if the stack was used there was no any recursive calls at all</p>
  <p>after some experiments I could reproduce the crash with :</p>
  <ul>
  <li>msvc 19.28</li>
  <li>g++-10 on wsl and mingw64</li>
  <li>clang-cl 10 on windows and clang++-10 on linux</li>
  </ul>
  <p>this code causes the stack overflow:</p>
  <pre><code>#include &lt;stdexcept&gt;
  #include &lt;utility&gt;
  #include &lt;cstdio&gt;
  
  #ifdef __clang__
  #ifdef _WIN32
  #pragma message &quot;using clang coroutine header&quot;
  #include &quot;clang-cl-coro.h&quot;
  #else
  #pragma message &quot;using coroutine experimental header&quot;
  #include &lt;experimental/coroutine&gt;
  #endif
  
  namespace std
  {
      template&lt;class P = void&gt;
      using coroutine_handle = experimental::coroutine_handle&lt;P&gt;;
  
      using suspend_never = experimental::suspend_never;
  
      using suspend_always = experimental::suspend_always;
  
  }
  
  #else
  #pragma message &quot;using coroutine header&quot;
  #include &lt;coroutine&gt;
  #endif
  
  class vtask
  {
      inline static size_t task_count = 0;
  
  public:
  
      struct promise_type
      {
          inline static size_t promise_count = 0;
  
          std::coroutine_handle&lt;&gt; waiter;
          std::exception_ptr ex_ptr = nullptr;
  
          struct resume_waiter
          {
              inline static size_t awaiter_count = 0;
  
              std::coroutine_handle&lt;&gt; waiter;
  
              resume_waiter(std::coroutine_handle&lt;&gt; waiter) noexcept : waiter{ waiter }
              {
                  ++awaiter_count;
                  printf(&quot;[%zu] resume_waiter(std::coroutine_handle&lt;&gt; waiter)\n&quot;, awaiter_count);
              }
  
              ~resume_waiter()
              {
                  --awaiter_count;
                  printf(&quot;[%zu] ~resume_waiter()\n&quot;, awaiter_count);
              }
  
              bool await_ready() const noexcept { return false; }
  
              auto await_suspend(std::coroutine_handle&lt;&gt;) noexcept
              {
                  return waiter;
              }
  
              void await_resume() const noexcept {}
          };
  
          promise_type()
          {
              ++promise_count;
              printf(&quot;[%zu] vtask::promise_type()\n&quot;, promise_count);
  
          }
  
          ~promise_type()
          {
              --promise_count;
              printf(&quot;[%zu] ~vtask::promise_type()\n&quot;, promise_count);
          }
  
          vtask get_return_object() { return { *this }; }
  
          constexpr std::suspend_always initial_suspend() noexcept { return {}; }
  
          resume_waiter final_suspend() const noexcept { return { waiter }; }
  
          void unhandled_exception() noexcept
          {
              ex_ptr = std::current_exception();
          }
  
          void return_void() const noexcept {}
      };
  
      vtask(promise_type&amp; p) : coro{ std::coroutine_handle&lt;promise_type&gt;::from_promise(p) }
      {
          ++task_count;
          printf(&quot;[%zu] vtask(promise_type&amp; p)\n&quot;, task_count);
      }
  
      vtask(vtask&amp;&amp; other) noexcept : coro{ std::exchange(other.coro, nullptr) }
      {
          ++task_count;
          printf(&quot;[%zu] vtask(vtask&amp;&amp; other)\n&quot;, task_count);
      }
  
      ~vtask()
      {
          if (coro)
              coro.destroy();
          --task_count;
          printf(&quot;[%zu] ~vtask()\n&quot;, task_count);
      }
  
      bool await_ready() const noexcept
      {
          return false;
      }
  
      void await_suspend(std::coroutine_handle&lt;&gt; waiter)
      {
          coro.promise().waiter = waiter;
          coro.resume();
      }
  
      void await_resume() noexcept {}
  
  private:
      std::coroutine_handle&lt;promise_type&gt; coro;
  };
  
  struct detached_task
  {
      struct promise_type
      {
          inline static size_t promise_count = 0;
  
          promise_type()
          {
              ++promise_count;
              printf(&quot;[%zu] detached_task::promise_type()\n&quot;, promise_count);
          }
  
          ~promise_type()
          {
              --promise_count;
              printf(&quot;[%zu] ~detached_task::promise_type()\n&quot;, promise_count);
          }
  
          detached_task get_return_object() { return {}; }
  
          std::suspend_never initial_suspend() noexcept { return {}; }
  
          std::suspend_never final_suspend() noexcept { return {}; }
  
          void unhandled_exception() noexcept
          {
              std::terminate();
          }
  
          constexpr void return_void() const noexcept {}
      };
  
      inline static size_t task_count = 0;
  
      detached_task()
      {
          ++task_count;
          printf(&quot;[%zu] detached_task()\n&quot;, task_count);
      }
  
      ~detached_task()
      {
          --task_count;
          printf(&quot;[%zu] ~detached_task()\n&quot;, task_count);
      }
  };
  
  vtask do_stackoverflow() { co_return; }
  
  detached_task stackoverflow()
  {
      for (;;)
          co_await do_stackoverflow();
  }
  
  int main()
  {
      stackoverflow();
  }
  </code></pre>
  <p>command lines used:</p>
  <p><code>cl /std:c++latest coro-stackoverflow.cpp /EHsc</code> for msvc</p>
  <p><code>g++ -std=c++20 coro-stackoverflow.cpp -fcoroutines</code> for mingw64</p>
  <p><code>clang-cl /std:c++latest coro-stackoverflow.cpp /EHsc</code></p>
  <p><code>g++-10 -std=c++20 coro-stackoverflow.cpp -fcoroutines -o overflow.bug</code> on wsl</p>
  <p><code>clang++-10 -std=c++20 -stdlib=libc++ coro-stackoverflow.cpp -o overflow-clang.bug</code> on wsl</p>
  <p>and this is the clang coro header on windows:</p>
  <pre><code>#pragma once
  
  namespace std { namespace experimental { inline namespace coroutines_v1 {
  
  template &lt;typename R, typename...&gt; struct coroutine_traits {
    using promise_type = typename R::promise_type;
  };
  
  template &lt;typename Promise = void&gt; struct coroutine_handle;
  
  template &lt;&gt; struct coroutine_handle&lt;void&gt; {
    static coroutine_handle from_address(void *addr) noexcept {
      coroutine_handle me;
      me.ptr = addr;
      return me;
    }
    void operator()() { resume(); }
    void *address() const { return ptr; }
    void resume() const { __builtin_coro_resume(ptr); }
    void destroy() const { __builtin_coro_destroy(ptr); }
    bool done() const { return __builtin_coro_done(ptr); }
    coroutine_handle &amp;operator=(decltype(nullptr)) {
      ptr = nullptr;
      return *this;
    }
    coroutine_handle(decltype(nullptr)) : ptr(nullptr) {}
    coroutine_handle() : ptr(nullptr) {}
  //  void reset() { ptr = nullptr; } // add to P0057?
    explicit operator bool() const { return ptr; }
  
  protected:
    void *ptr;
  };
  
  template &lt;typename Promise&gt; struct coroutine_handle : coroutine_handle&lt;&gt; {
    using coroutine_handle&lt;&gt;::operator=;
  
    static coroutine_handle from_address(void *addr) noexcept {
      coroutine_handle me;
      me.ptr = addr;
      return me;
    }
  
    Promise &amp;promise() const {
      return *reinterpret_cast&lt;Promise *&gt;(
          __builtin_coro_promise(ptr, alignof(Promise), false));
    }
    static coroutine_handle from_promise(Promise &amp;promise) {
      coroutine_handle p;
      p.ptr = __builtin_coro_promise(&amp;promise, alignof(Promise), true);
      return p;
    }
  };
  
    template &lt;typename _PromiseT&gt;
    bool operator==(coroutine_handle&lt;_PromiseT&gt; const&amp; _Left,
      coroutine_handle&lt;_PromiseT&gt; const&amp; _Right) noexcept
    {
      return _Left.address() == _Right.address();
    }
  
    template &lt;typename _PromiseT&gt;
    bool operator!=(coroutine_handle&lt;_PromiseT&gt; const&amp; _Left,
      coroutine_handle&lt;_PromiseT&gt; const&amp; _Right) noexcept
    {
      return !(_Left == _Right);
    }
  
    template &lt;typename _PromiseT&gt;
    bool operator==(coroutine_handle&lt;_PromiseT&gt; const&amp; _Left,
       std::nullptr_t) noexcept
    {
        return _Left.address() == nullptr;
    }
  
    template &lt;typename _PromiseT&gt;
    bool operator==(std::nullptr_t, coroutine_handle&lt;_PromiseT&gt; const&amp; _Right) noexcept
    {
        return _Right.address() == nullptr;
    }
  
    template &lt;typename _PromiseT&gt;
    bool operator!=(coroutine_handle&lt;_PromiseT&gt; const&amp; _Left,
       std::nullptr_t) noexcept
    {
  
  
      return !(_Left == nullptr);
    }
  
    template &lt;typename _PromiseT&gt;
    bool operator!=(std::nullptr_t, coroutine_handle&lt;_PromiseT&gt; const&amp; _Right) noexcept
    {
        return _Right.address() != nullptr;
    }
  
  struct suspend_always {
    bool await_ready() { return false; }
    void await_suspend(coroutine_handle&lt;&gt;) {}
    void await_resume() {}
  };
  struct suspend_never {
    bool await_ready() { return true; }
    void await_suspend(coroutine_handle&lt;&gt;) {}
    void await_resume() {}
  };
  
  }}}
  </code></pre>
  <p>on windows the crash occurs early for msvc and clang-cl builds but mingw64 and wsl builds takes more time</p>
  <p>gcc 10.1 which I used seems to have a knows bug where the task is constructed twice but destroyed once leaking a task each iteration which seems to cause the overflow</p>
  <p>but clang and msvc don't have this bug and they also crash !</p>
  <p>Edit: tried gcc 10.3 mingw64 and it doesn't have the mentioned gcc bug but it also cause a stack overflow ! even faster the the bugged compiler ! maybe this behavior is expected ?</p>
  <p>I can't figure out which is wrong with the code above</p>