Skip to content

any_resource(resource_ref) construction breaks with proxy types due to missing non-template overload #8316

@bdice

Description

@bdice

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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    In Review

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions