Repository: cupy/cupy

Issue:
cp.unique gives IndexError when passed an empty array.
```
cp.unique(cp.array([]))

IndexError: Index 0 is out of bounds for axis 0 with size 0
```

Rapids 21.06, CUDA 11.2
Docker install

```
OS                           : Linux-4.15.0-55-generic-x86_64-with-debian-bullseye-sid
CuPy Version                 : 9.0.0
NumPy Version                : 1.20.3
SciPy Version                : 1.6.0
Cython Build Version         : 0.29.23
Cython Runtime Version       : 0.29.23
CUDA Root                    : /opt/conda/envs/rapids
CUDA Build Version           : 11020
CUDA Driver Version          : 11020
CUDA Runtime Version         : 11020
cuBLAS Version               : 11301
cuFFT Version                : 10400
cuRAND Version               : 10203
cuSOLVER Version             : (11, 0, 2)
cuSPARSE Version             : 11301
NVRTC Version                : (11, 2)
Thrust Version               : 101000
CUB Build Version            : 101000
Jitify Build Version         : <unknown>
cuDNN Build Version          : None
cuDNN Version                : None
NCCL Build Version           : 20906
NCCL Runtime Version         : 20909
cuTENSOR Version             : None
cuSPARSELt Build Version     : None
Device 0 Name                : Tesla V100-SXM2-32GB
... and more devices
```


Discussion:
Could you please share the full traceback as well?

Guessing it would point to this line, but would be good to confirm that

https://github.com/cupy/cupy/blob/dddb367e03e413c5120521733e2694515a0d8f70/cupy/_manipulation/add_remove.py#L182
@jakirkham 

```
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-157-6673f0bd630e> in <module>
----> 1 cp.unique(cp.array([]))

/opt/conda/envs/rapids/lib/python3.7/site-packages/cupy/_manipulation/add_remove.py in unique(ar, return_index, return_inverse, return_counts, axis)
    180         aux = ar
    181     mask = cupy.empty(aux.shape, dtype=cupy.bool_)
--> 182     mask[0] = True
    183     mask[1:] = aux[1:] != aux[:-1]
    184 

cupy/_core/core.pyx in cupy._core.core.ndarray.__setitem__()

cupy/_core/_routines_indexing.pyx in cupy._core._routines_indexing._ndarray_setitem()

cupy/_core/_routines_indexing.pyx in cupy._core._routines_indexing._scatter_op()

cupy/_core/_routines_indexing.pyx in cupy._core._routines_indexing._simple_getitem()

IndexError: Index 0 is out of bounds for axis 0 with size 0
```
`mask[:1] = True` looks good.  (numpy/numpy#10498)

Resolving patch:
diff --git a/cupy/_manipulation/add_remove.py b/cupy/_manipulation/add_remove.py
--- a/cupy/_manipulation/add_remove.py
+++ b/cupy/_manipulation/add_remove.py
@@ -179,7 +179,7 @@ def unique(ar, return_index=False, return_inverse=False,
         ar.sort()
         aux = ar
     mask = cupy.empty(aux.shape, dtype=cupy.bool_)
-    mask[0] = True
+    mask[:1] = True
     mask[1:] = aux[1:] != aux[:-1]
 
     ret = aux[mask]


Test patch:
diff --git a/tests/cupy_tests/manipulation_tests/test_add_remove.py b/tests/cupy_tests/manipulation_tests/test_add_remove.py
--- a/tests/cupy_tests/manipulation_tests/test_add_remove.py
+++ b/tests/cupy_tests/manipulation_tests/test_add_remove.py
@@ -128,6 +128,26 @@ def test_unique_counts(self, xp, dtype):
         a = testing.shaped_random((100, 100), xp, dtype)
         return xp.unique(a, return_counts=True)[1]
 
+    @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
+    @testing.numpy_cupy_array_equal()
+    def test_unique_return_all(self, xp, dtype):
+        a = testing.shaped_random((100, 100), xp, dtype)
+        return xp.unique(
+            a, return_index=True, return_inverse=True, return_counts=True)
+
+    @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
+    @testing.numpy_cupy_array_equal()
+    def test_unique_empty(self, xp, dtype):
+        a = xp.empty((0,), dtype)
+        return xp.unique(a)
+
+    @testing.for_all_dtypes(no_float16=True, no_bool=True, no_complex=True)
+    @testing.numpy_cupy_array_equal()
+    def test_unique_empty_return_all(self, xp, dtype):
+        a = xp.empty((3, 0, 2), dtype)
+        return xp.unique(
+            a, return_index=True, return_inverse=True, return_counts=True)
+
 
 @testing.parameterize(*testing.product({
     'trim': ['fb', 'f', 'b']