Cannot use promise<T>::fulfill_result() when T is not MoveConstructible

Issue #402 resolved
Amir Kamil created an issue

Example of a minimal test case:

#include <upcxx/upcxx.hpp>
#include <iostream>

struct A {
  int x;
  A(int v) : x(v) {}
  A(const A&) = delete;
};

using namespace upcxx;

int main() {
  upcxx::init();

  promise<A> pro;
  pro.fulfill_result(3);

  upcxx::finalize();
}

This should be legal according to the language in spec PR 50, which only requires constructibility from the arguments. (The spec language predating that PR requires implicit convertibility, which still does not prohibit this code.)

The culprit is that promise<T...>::fulfill_result(U&&...) forces the arguments into a std::tuple<T...> before passing that off to future_header_result<T...>::construct_results(), which then constructs another std::tuple<T...> from the first. Constructing the first one works fine, since it invokes the constructor that takes int, but constructing the second tuple requires the presence of a move or copy constructor.

A slightly different issue exists in the undocumented promise<T...>::fulfill_result(std::tuple<U...>&&) – here, the argument tuple is passed directly to detail::promise_fulfill_result<T...>, which expects a std::tuple<T...>, not a std::tuple<U...>.

The fix for both is straightforward – add another set of template parameters U... to detail::promise_fulfill_result and have it take a std::tuple<U...> instead of a std::tuple<T...>. No changes are required to future_header_result<T...>::construct_results(), which already has this second set of template parameters.

Comments (2)

  1. Log in to comment