C++17: Class template argument deduction fails with `upcxx::future`

Issue #569 new
Colin MacLean created an issue

CTAD doesn’t work with upcxx::future, partly due to it being an alias

test2.cpp: In function ‘int main()’:
test2.cpp:21:48: error: class template argument deduction failed:
   21 |   upcxx::future fut = upcxx::make_future(4,3.14);
      |                                                ^
test2.cpp:21:48: error: call of overloaded ‘future1(upcxx::future<int, double>)’ is ambiguous
In file included from /home/colin/work/upcxx/build/bld/upcxx.assert1.optlev0.dbgsym1.gasnet_seq.smp/include/upcxx/backend_fwd.hpp:24,
                 from /home/colin/work/upcxx/build/bld/upcxx.assert1.optlev0.dbgsym1.gasnet_seq.smp/include/upcxx/backend.hpp:4,
                 from /home/colin/work/upcxx/build/bld/upcxx.assert1.optlev0.dbgsym1.gasnet_seq.smp/include/upcxx/allocate.hpp:8,
                 from /home/colin/work/upcxx/build/bld/upcxx.assert1.optlev0.dbgsym1.gasnet_seq.smp/include/upcxx/upcxx.hpp:5,
                 from test2.cpp:1:
/home/colin/work/upcxx/build/bld/upcxx.assert1.optlev0.dbgsym1.gasnet_seq.smp/include/upcxx/future/fwd.hpp:76:12: note: candidate: ‘future1(upcxx::detail::future1<upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_general>, T ...>)-> upcxx::detail::future1<upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_general>, T ...> [with T = {int, double}]’
   76 |     struct future1;
      |            ^~~~~~~
In file included from /home/colin/work/upcxx/build/bld/upcxx.assert1.optlev0.dbgsym1.gasnet_seq.smp/include/upcxx/future.hpp:5,
                 from /home/colin/work/upcxx/build/bld/upcxx.assert1.optlev0.dbgsym1.gasnet_seq.smp/include/upcxx/backend.hpp:5,
                 from /home/colin/work/upcxx/build/bld/upcxx.assert1.optlev0.dbgsym1.gasnet_seq.smp/include/upcxx/allocate.hpp:8,
                 from /home/colin/work/upcxx/build/bld/upcxx.assert1.optlev0.dbgsym1.gasnet_seq.smp/include/upcxx/upcxx.hpp:5,
                 from test2.cpp:1:
/home/colin/work/upcxx/build/bld/upcxx.assert1.optlev0.dbgsym1.gasnet_seq.smp/include/upcxx/future/future1.hpp:105:5: note: candidate: ‘future1(const upcxx::detail::future1<upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_general>, T ...>&)-> upcxx::detail::future1<upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_general>, T ...> [with T = {int, double}]’
  105 |     future1(future1 const&) = default;
      |     ^~~~~~~
/home/colin/work/upcxx/build/bld/upcxx.assert1.optlev0.dbgsym1.gasnet_seq.smp/include/upcxx/future/future1.hpp:107:5: note: candidate: ‘future1(const upcxx::detail::future1<Kind1, T ...>&)-> upcxx::detail::future1<upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_general>, T ...> [with T = {int, double}; Kind1 = upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_general>]’
  107 |     future1(future1<Kind1,T...> const &that): impl_(that.impl_) {}
      |     ^~~~~~~
/home/colin/work/upcxx/build/bld/upcxx.assert1.optlev0.dbgsym1.gasnet_seq.smp/include/upcxx/future/future1.hpp:109:5: note: candidate: ‘future1(upcxx::detail::future1<upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_general>, T ...>&&)-> upcxx::detail::future1<upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_general>, T ...> [with T = {int, double}]’
  109 |     future1(future1&&) = default;
      |     ^~~~~~~
/home/colin/work/upcxx/build/bld/upcxx.assert1.optlev0.dbgsym1.gasnet_seq.smp/include/upcxx/future/future1.hpp:111:5: note: candidate: ‘future1(upcxx::detail::future1<Kind1, T ...>&&)-> upcxx::detail::future1<upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_general>, T ...> [with T = {int, double}; Kind1 = upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_general>]’
  111 |     future1(future1<Kind1,T...> &&that):
      |     ^~~~~~~

Using upcxx::detail::future1 does deduce correctly.

The fix would either be to make upcxx::future a class inheriting from future1 instead of an alias or to specify a deduction guide, although this would only work >=C++20 and not in C++17 due to alias deductions not being available, yet.

Here’s a reproducer:

#include <upcxx/upcxx.hpp>

int main()
{
  upcxx::init();
  upcxx::future fut = upcxx::make_future();
  upcxx::finalize();
  return 0;
}

Compiling this in C++17 gives an error about CTAD of aliases not being available until C++20 and building in C++20 mode gives an error like above.

Looking at the UPC++ spec, upcxx::future is defined to be:

template<typename... T>
class future;

and not

template<typename... T>
using future = /* implementation defined */;

The distinction between a class and an alias matters, meaning our implementation of upcxx::future is non-compliant with our specification. If we go with the simpler solution of an alias CTAD (C++20), we should update the spec to make upcxx::future an alias. Otherwise CTAD would be expected to work in C++17 given the current spec.

Comments (3)

  1. Colin MacLean reporter
    • edited description

    Added reproducer and investigation regarding the current UPC++ spec.

  2. Log in to comment