def auto_convert(
    *thunk_params: str,
) -> Callable[[Callable[P, R]], Callable[P, R]]:
    """
    Converts all named parameters to DeferredArrays.

    This function makes an immutable copy of any parameter that wasn't already
    a DeferredArray.
    """
    keys = OrderedSet(thunk_params)
    assert len(keys) == len(thunk_params)

    def decorator(func: Callable[P, R]) -> Callable[P, R]:
        assert not hasattr(
            func, "__wrapped__"
        ), "this decorator must be the innermost"

        # For each parameter specified by name, also consider the case where
        # it's passed as a positional parameter.
        params = signature(func).parameters
        extra = keys - OrderedSet(params)
        assert len(extra) == 0, f"unknown parameter(s): {extra}"
        indices = {idx for (idx, param) in enumerate(params) if param in keys}

        @wraps(func)
        def wrapper(*args: Any, **kwargs: Any) -> R:
            # Convert relevant arguments to DeferredArrays
            args = tuple(
                runtime.to_deferred_array(arg, read_only=True)
                if idx in indices and arg is not None
                else arg
                for (idx, arg) in enumerate(args)
            )
            for k, v in kwargs.items():
                if k in keys and v is not None:
                    kwargs[k] = runtime.to_deferred_array(v, read_only=True)

            return func(*args, **kwargs)

        return wrapper

    return decorator

    Converts all named parameters to DeferredArrays.

    This function makes an immutable copy of any parameter that wasn't already
    a DeferredArray.
    
cupynumeric/_thunk/deferred.py