Skip to content

execution continues after co_return #726

@ARG-Helper

Description

@ARG-Helper

https://compiler-explorer.com/z/jPrq9Mxno

#include <cstdio>
#include <iostream>
#include <coroutine>
struct P {
    std::suspend_always initial_suspend()
    {
        return {};
    }
    void return_void() const noexcept
    {
      std::cout<<"return_void()\n";
    }
    std::coroutine_handle<> get_return_object()
    {
        return std::coroutine_handle<P>::from_promise(*this);
    };
    void unhandled_exception() { throw; }
    std::suspend_never final_suspend() noexcept
    {
        return {};
    }
};
struct R {
    R( std::coroutine_handle<> d) noexcept
        : data(d)
    {
    }
    std::coroutine_handle<> data;
    using promise_type = P;
};
R funcA(){
  std::cout<<"funcA_1\n";
  co_return;
  std::cout<<"funcA_2\n";
}
int main()
{
  funcA().data.resume();
  return	0;
}

This code gets translated into

/*************************************************************************************
 * NOTE: The coroutine transformation you've enabled is a hand coded transformation! *
 *       Most of it is _not_ present in the AST. What you see is an approximation.   *
 *************************************************************************************/
#include <cstdio>
#include <iostream>
#include <coroutine>
struct P
{
  inline std::suspend_always initial_suspend()
  {
    return {};
  }
  
  inline void return_void() const noexcept
  {
    std::operator<<(std::cout, "return_void()\n");
  }
  
  inline std::coroutine_handle<void> get_return_object()
  {
    return std::coroutine_handle<P>::from_promise(*this).operator std::coroutine_handle<void>();
  }
  
  inline void unhandled_exception()
  {
    throw ;
  }
  
  inline std::suspend_never final_suspend() noexcept
  {
    return {};
  }
  
  // inline constexpr P() noexcept = default;
};


struct R : public std::suspend_always
{
  inline R(std::coroutine_handle<void> d) noexcept
  : std::suspend_always()
  , data{std::coroutine_handle<void>(d)}
  {
  }
  
  std::coroutine_handle<void> data;
  inline std::coroutine_handle<void> await_suspend(std::coroutine_handle<void> h) const noexcept
  {
    return std::coroutine_handle<void>(this->data);
  }
  
  using promise_type = P;
};


struct __funcAFrame
{
  void (*resume_fn)(__funcAFrame *);
  void (*destroy_fn)(__funcAFrame *);
  std::__coroutine_traits_impl<R>::promise_type __promise;
  int __suspend_index;
  bool __initial_await_suspend_called;
  std::suspend_always __suspend_36_3;
  std::suspend_never __suspend_36_3_1;
};

R funcA()
{
  /* Allocate the frame including the promise */
  /* Note: The actual parameter new is __builtin_coro_size */
  __funcAFrame * __f = reinterpret_cast<__funcAFrame *>(operator new(sizeof(__funcAFrame)));
  __f->__suspend_index = 0;
  __f->__initial_await_suspend_called = false;
  
  /* Construct the promise. */
  new (&__f->__promise)std::__coroutine_traits_impl<R>::promise_type{};
  
  /* Forward declare the resume and destroy function. */
  void __funcAResume(__funcAFrame * __f);
  void __funcADestroy(__funcAFrame * __f);
  
  /* Assign the resume and destroy function pointers. */
  __f->resume_fn = &__funcAResume;
  __f->destroy_fn = &__funcADestroy;
  
  /* Call the made up function with the coroutine body for initial suspend.
     This function will be called subsequently by coroutine_handle<>::resume()
     which calls __builtin_coro_resume(__handle_) */
  __funcAResume(__f);
  
  
  return R(std::coroutine_handle<void>(static_cast<std::coroutine_handle<void> &&>(__coro_gro)));
}

/* This function invoked by coroutine_handle<>::resume() */
void __funcAResume(__funcAFrame * __f)
{
  try 
  {
    /* Create a switch to get to the correct resume point */
    switch(__f->__suspend_index) {
      case 0: break;
      case 1: goto __resume_funcA_1;
      case 2: goto __resume_funcA_2;
    }
    
    /* co_await insights.cpp:36 */
    __f->__suspend_36_3 = __f->__promise.initial_suspend();
    if(!__f->__suspend_36_3.await_ready()) {
      __f->__suspend_36_3.await_suspend(std::coroutine_handle<P>::from_address(static_cast<void *>(__f)).operator std::coroutine_handle<void>());
      __f->__suspend_index = 1;
      __f->__initial_await_suspend_called = true;
      return;
    } 
    
    __resume_funcA_1:
    __f->__suspend_36_3.await_resume();
    std::operator<<(std::cout, "funcA_1\n");
    /* co_return insights.cpp:38 */
    __f->__promise.return_void();
    std::operator<<(std::cout, "funcA_2\n");
    /* co_return insights.cpp:36 */
    __f->__promise.return_void()/* implicit */;
    goto __final_suspend;
  } catch(...) {
    if(!__f->__initial_await_suspend_called) {
      throw ;
    } 
    
    __f->__promise.unhandled_exception();
  }
  
  __final_suspend:
  
  /* co_await insights.cpp:36 */
  __f->__suspend_36_3_1 = __f->__promise.final_suspend();
  if(!__f->__suspend_36_3_1.await_ready()) {
    __f->__suspend_36_3_1.await_suspend(std::coroutine_handle<P>::from_address(static_cast<void *>(__f)).operator std::coroutine_handle<void>());
    __f->__suspend_index = 2;
    return;
  } 
  
  __resume_funcA_2:
  __f->destroy_fn(__f);
}

/* This function invoked by coroutine_handle<>::destroy() */
void __funcADestroy(__funcAFrame * __f)
{
  /* destroy all variables with dtors */
  __f->~__funcAFrame();
  /* Deallocating the coroutine frame */
  /* Note: The actual argument to delete is __builtin_coro_frame with the promise as parameter */
  operator delete(static_cast<void *>(__f), sizeof(__funcAFrame));
}


int main()
{
  static_cast<const std::coroutine_handle<void> &&>(funcA().data).resume();
  return 0;
}

missing an goto __final_suspend; after the first __f->__promise.return_void();
std::operator<<(std::cout, "funcA_2\n"); should be dead code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions