c++20boost-asioclang++c++-coroutineboost-beast

Why can't the result of boost::beast::http::async_write with token as_tuple(deferred) be converted to awaitable<tuple<error_code, size_t>>?


Clang tells me there is no viable conversion from the return value of boost::beast::http::async_write with completion token boost::asio::as_tuple(boost::asio::deferred) to awaitable<tuple<boost::system::error_code, unsigned long>>:

error: no viable conversion from returned value of type 'decltype(enable_if_t<enable_if_t<detail::are_completion_signatures<void (error_code, unsigned long)>::value, detail::async_result_has_initiate_memfn<as_tuple_t<deferred_t>, void (error_code, unsigned long)>>::value, async_result<decay_t<as_tuple_t<deferred_t>>, void (error_code, unsigned long)>>::initiate(static_cast<boost::beast::http::detail::run_write_msg_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp>> &&>(initiation), static_cast<boost::asio::as_tuple_t<boost::asio::deferred_t> &&>(token), static_cast<const boost::beast::http::message<false, boost::beast::http::basic_string_body<char>> *&&>(args), static_cast<std::integral_constant<bool, true> &&>(args)))' (aka 'deferred_async_operation<void (std::tuple<boost::system::error_code, unsigned long>), boost::asio::async_result<boost::asio::as_tuple_t<boost::asio::deferred_t>, void (boost::system::error_code, unsigned long)>::init_wrapper<boost::beast::http::detail::run_write_msg_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>>>, const boost::beast::http::message<false, boost::beast::http::basic_string_body<char, std::char_traits<char>, std::allocator<char>>, boost::beast::http::basic_fields<std::allocator<char>>> *, std::integral_constant<bool, true>>') to function return type 'boost::asio::awaitable<std::tuple<boost::system::error_code, std::size_t>>' (aka 'awaitable<tuple<boost::system::error_code, unsigned long>>')
  179 |return boost::beast::http::async_write(stream, typed_message, boost::asio::as_tuple(boost::asio::deferred));
      |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The function in question looks like this:

template<typename stream_t>
boost::asio::awaitable<void> response_worker(boost::asio::experimental::channel<void(boost::system::error_code, http_response_t)> &channel, stream_t &stream)
{
    while (true) {
        boost::system::error_code ec;
        std::variant<
            boost::beast::http::response<boost::beast::http::string_body>,
            boost::beast::http::response<boost::beast::http::empty_body>,
            boost::beast::http::response<boost::beast::http::dynamic_body>,
            boost::beast::http::response<boost::beast::http::file_body>,
            boost::beast::http::response<boost::beast::http::buffer_body>>
            message = co_await channel.async_receive(boost::asio::redirect_error(boost::asio::deferred, ec));
        if (ec == boost::asio::error::eof) {
            channel.close();
            co_return;
        }
        const auto [ec2, transferred] = co_await std::visit(
            [&stream](auto &typed_message) -> boost::asio::awaitable<std::tuple<boost::system::error_code, std::size_t>> {
                return boost::beast::http::async_write(stream, typed_message, boost::asio::as_tuple(boost::asio::deferred));
            },
            message);
        //error handling
    }
}

This used to compile and I'm incredibly confused by the error message, is there an error in my code or is my clang installation even more broken than I thought (libclang does not get the header search correct means I have to add -I/usr/lib/clang/19/include as a compiler flag if I understand it right)?


Solution

  • Turns out this was a compiler bug in the version of Clang I was using.

    You can see in this godbolt example how the minimally reproducible example compiles in Clang 20.1.0 but not Clang 19.1.10.

    The issue was caused by having the code contained in a template inside a module.