Skip to content

Ambiguous get_property overload with CCCL's dynamic_accessibility_property #2322

@bdice

Description

@bdice

Summary

RMM's cccl_async_resource_ref wrapper defines an unconstrained friend get_property template that becomes ambiguous with CCCL's new default get_property for dynamic_accessibility_property. This breaks downstream consumers (e.g. raft) that use cuda::forward_property with rmm::device_async_resource_ref as the upstream type.

The CCCL nightly build failure is here: https://github.com/NVIDIA/cccl/actions/runs/23279043739/job/67688181940

Root Cause

CCCL main added dynamic_accessibility_property support (NVIDIA/cccl#7727), which introduces two new get_property overloads:

  1. forward_property::__fn (line ~128 of get_property.h): a friend that forwards dynamic_accessibility_property to upstream_resource() via an unqualified call to get_property.
  2. cuda::mr::get_property (line ~191 of get_property.h): a default implementation that infers accessibility from static properties.

When the upstream type is rmm::device_async_resource_ref (i.e. rmm::detail::cccl_async_resource_ref<resource_ref<device_accessible>>), the unqualified forwarding call finds two equally-viable candidates via ADL:

  • rmm::detail::get_property(cccl_async_resource_ref const&, Property) — unconstrained friend template at line 353 of cccl_adaptors.hpp
  • cuda::mr::get_property(const _Resource&, dynamic_accessibility_property) — the CCCL default

Neither is more specialized than the other, so the compiler rejects the call as ambiguous.

Minimal Reproducer

Godbolt: https://godbolt.org/z/6exqE6Kv6

The following reproducer depends only on CCCL headers (no RMM headers needed). Build with:

nvcc -std=c++20 -x cu get_property_repro.cpp \
  -I<cccl>/libcudacxx/include \
  -I<cccl>/cub \
  -I<cccl>/thrust/thrust/cmake/../..
#include <cuda/memory_resource>

// Mock of rmm::device_async_resource_ref.
// The only detail that matters: a wrapper in a non-cuda::mr namespace
// with an unconstrained friend get_property template.
namespace mock_rmm::detail {

template <typename ResourceType>
class cccl_async_resource_ref {
 public:
  cccl_async_resource_ref() = default;
  explicit cccl_async_resource_ref(ResourceType ref) : ref_{ref} {}

  template <typename Property>
  friend auto constexpr get_property(cccl_async_resource_ref const& r, Property p) noexcept
    -> decltype(get_property(std::declval<ResourceType const&>(), p))
  {
    return get_property(r.ref_, p);
  }

 private:
  ResourceType ref_;
};

}  // namespace mock_rmm::detail

using mock_device_async_resource_ref =
  mock_rmm::detail::cccl_async_resource_ref<cuda::mr::resource_ref<cuda::mr::device_accessible>>;

// Adaptor using cuda::forward_property with the mock upstream.
struct adaptor : cuda::forward_property<adaptor, mock_device_async_resource_ref> {
  explicit adaptor(mock_device_async_resource_ref upstream) : upstream_{upstream} {}

  mock_device_async_resource_ref upstream_resource() const { return upstream_; }

  void* allocate(::cuda::stream_ref, std::size_t, std::size_t) { return nullptr; }
  void  deallocate(::cuda::stream_ref, void*, std::size_t, std::size_t) noexcept {}
  void* allocate_sync(std::size_t, std::size_t) { return nullptr; }
  void  deallocate_sync(void*, std::size_t, std::size_t) noexcept {}

  friend bool operator==(adaptor const&, adaptor const&) { return false; }

 private:
  mock_device_async_resource_ref upstream_;
};

// Type-erasing `adaptor` into resource_ref triggers the ambiguity.
template <typename T>
void trigger(T& resource)
{
  cuda::mr::resource_ref<cuda::mr::device_accessible> erased{resource};
  (void)erased;
}

template void trigger<adaptor>(adaptor&);

Compiler Output (nvcc 13.0, CCCL main)

get_property.h(128): error: more than one instance of "get_property" matches the argument list:
    function template "auto mock_rmm::detail::get_property(..., Property)" (line 46)
    function template "cuda::mr::get_property(const _Resource &, dynamic_accessibility_property)" (line 191)
    argument types are: (mock_device_async_resource_ref, dynamic_accessibility_property)

Possible Fixes

We must resolve #2011 fully, and #2301 in particular, to fix the root cause of this problem.

There may be a workaround where we constrain the friend get_property in RMM's cccl_async_resource_ref to exclude dynamic_accessibility_property (or any property that already has a more-specific overload in cuda::mr), e.g.:

template <typename Property,
          std::enable_if_t<!std::is_same_v<Property, cuda::mr::dynamic_accessibility_property>>* = nullptr>
friend auto constexpr get_property(cccl_async_resource_ref const& r, Property p) noexcept
  -> decltype(get_property(std::declval<ResourceType const&>(), p))
{
  return get_property(r.ref_, p);
}

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Status

To-do

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions