Summary
tl;dr There is a discrepancy between the documented API of any_resource and its actual implementation. The docs say there is a constructor any_resource(resource_ref), but passing an object that converts to a resource_ref doesn't work. The real constructor behaves differently than the documented constructor.
Details
any_resource<Props...> can be constructed from a resource_ref<Props...> in pure C++, but this only works incidentally through the inherited forwarding constructor __basic_any(_Tp&&). Any proxy type wrapping resource_ref that provides operator T&() (e.g. Cython's __Pyx_FakeReference) breaks template argument deduction, because the SFINAE constraint __satisfies<decay_t<_Tp>, _Interface> evaluates against the proxy type rather than resource_ref.
The documented API in basic_any_resource (line 506 of any_resource.h) declares:
template <_ResourceKind _OtherKind, class... _OtherProperties>
basic_any_resource(basic_resource_ref<_OtherKind, _OtherProperties...> __res);
But the real any_resource struct at line 268 only has:
_CCCL_DELEGATE_CONSTRUCTORS(any_resource, ::cuda::__basic_any, __iasync_resource<_Properties...>);
which expands to using __base::__base, inheriting only the constrained forwarding constructor. There is no dedicated non-template overload for resource_ref.
Impact
This affects Cython (used by RAPIDS RMM, cuDF, etc.) which wraps C++ intermediate values in __Pyx_FakeReference<T> — a proxy with operator T&(). Constructing any_resource from a resource_ref obtained from Cython's optional::value() fails at compile time.
More generally, any codegen or proxy pattern that stores a resource_ref in a wrapper with operator T&() will hit the same issue.
Proposed Fix
Add a non-template constructor overload to any_resource (and any_synchronous_resource) for resource_ref / synchronous_resource_ref, matching the documented basic_any_resource API. This allows implicit conversion from proxy types via operator T&(), because non-template function parameters participate in implicit conversion sequences.
Minimal Reproducer
https://godbolt.org/z/jhPMfxGvs
#include <cuda/memory_resource>
struct minimal_resource {
void* allocate(cuda::stream_ref, std::size_t bytes, std::size_t = 256) {
return ::operator new(bytes);
}
void deallocate(cuda::stream_ref, void* ptr, std::size_t, std::size_t = 256) noexcept {
::operator delete(ptr);
}
void* allocate_sync(std::size_t bytes, std::size_t = 256) {
return ::operator new(bytes);
}
void deallocate_sync(void* ptr, std::size_t, std::size_t = 256) noexcept {
::operator delete(ptr);
}
friend bool operator==(minimal_resource const&, minimal_resource const&) noexcept { return true; }
friend bool operator!=(minimal_resource const&, minimal_resource const&) noexcept { return false; }
constexpr friend void get_property(minimal_resource const&, cuda::mr::device_accessible) noexcept {}
};
static_assert(cuda::mr::resource_with<minimal_resource, cuda::mr::device_accessible>);
// Minimal proxy with operator T&(), similar to Cython's __Pyx_FakeReference
template <typename T>
struct value_proxy {
value_proxy(T const& ref) : ptr(const_cast<T*>(&ref)) {}
operator T& () { return *ptr; }
T* ptr;
};
int main()
{
minimal_resource mr;
cuda::mr::resource_ref<cuda::mr::device_accessible> ref(mr);
// OK: any_resource from a resource_ref lvalue
cuda::mr::any_resource<cuda::mr::device_accessible> a(ref);
// ERROR: any_resource from a proxy wrapping resource_ref
value_proxy<cuda::mr::resource_ref<cuda::mr::device_accessible>> proxy(ref);
cuda::mr::any_resource<cuda::mr::device_accessible> b(proxy); // fails
return 0;
}
Compile with: g++ -std=c++20 -I<cccl>/libcudacxx/include -I<cccl>/thrust -I<cccl>/cub -isystem /usr/local/cuda/include mre.cpp
Error (truncated)
error: no matching function for call to 'any_resource<device_accessible>::any_resource(value_proxy<resource_ref<device_accessible>>&)'
note: candidate: '__basic_any(_Tp&&) requires !(__is_basic_any<_Up>) && (__satisfies<_Up, _Interface, ...>)'
note: constraints not satisfied
CCCL version
CCCL 3.4.0 (via RMM 26.06)
Summary
tl;dr There is a discrepancy between the documented API of
any_resourceand its actual implementation. The docs say there is a constructorany_resource(resource_ref), but passing an object that converts to aresource_refdoesn't work. The real constructor behaves differently than the documented constructor.Details
any_resource<Props...>can be constructed from aresource_ref<Props...>in pure C++, but this only works incidentally through the inherited forwarding constructor__basic_any(_Tp&&). Any proxy type wrappingresource_refthat providesoperator T&()(e.g. Cython's__Pyx_FakeReference) breaks template argument deduction, because the SFINAE constraint__satisfies<decay_t<_Tp>, _Interface>evaluates against the proxy type rather thanresource_ref.The documented API in
basic_any_resource(line 506 ofany_resource.h) declares:But the real
any_resourcestruct at line 268 only has:_CCCL_DELEGATE_CONSTRUCTORS(any_resource, ::cuda::__basic_any, __iasync_resource<_Properties...>);which expands to
using __base::__base, inheriting only the constrained forwarding constructor. There is no dedicated non-template overload forresource_ref.Impact
This affects Cython (used by RAPIDS RMM, cuDF, etc.) which wraps C++ intermediate values in
__Pyx_FakeReference<T>— a proxy withoperator T&(). Constructingany_resourcefrom aresource_refobtained from Cython'soptional::value()fails at compile time.More generally, any codegen or proxy pattern that stores a
resource_refin a wrapper withoperator T&()will hit the same issue.Proposed Fix
Add a non-template constructor overload to
any_resource(andany_synchronous_resource) forresource_ref/synchronous_resource_ref, matching the documentedbasic_any_resourceAPI. This allows implicit conversion from proxy types viaoperator T&(), because non-template function parameters participate in implicit conversion sequences.Minimal Reproducer
https://godbolt.org/z/jhPMfxGvs
Compile with:
g++ -std=c++20 -I<cccl>/libcudacxx/include -I<cccl>/thrust -I<cccl>/cub -isystem /usr/local/cuda/include mre.cppError (truncated)
CCCL version
CCCL 3.4.0 (via RMM 26.06)