Repository: cupy/cupy

Issue:
`expand_dims` fails to check duplicated axes
```
>>> cupy.expand_dims(cupy.ones(7), axis=(0, -3))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "cupy/cupy/_manipulation/dims.py", line 148, in expand_dims
    return _manipulation._expand_dims(a, axis)
  File "cupy/core/_routines_manipulation.pyx", line 223, in cupy.core._routines_manipulation._expand_dims
  File "cupy/core/_routines_manipulation.pyx", line 238, in cupy.core._routines_manipulation._expand_dims
  File "cupy/core/_routines_manipulation.pyx", line 317, in cupy.core._routines_manipulation._reshape
ValueError: cannot reshape array of size 7 into shape 49
```


Discussion:
`cupy.sum(cupy.ones(7), axis=(0, 0))` fails, too.

<details>

```
Traceback (most recent call last):
  File "cupy/core/_routines_manipulation.pyx", line 354, in cupy.core._routines_manipulation._transpose
ValueError: Invalid axes value: [0, 0]
Exception ignored in: 'cupy.core._reduction._set_permuted_args'
Traceback (most recent call last):
  File "cupy/core/_routines_manipulation.pyx", line 354, in cupy.core._routines_manipulation._transpose
ValueError: Invalid axes value: [0, 0]
Traceback (most recent call last):
  File ".../cupy/cuda/compiler.py", line 624, in compile
    nvrtc.compileProgram(self.ptr, options)
  File "cupy_backends/cuda/libs/nvrtc.pyx", line 108, in cupy_backends.cuda.libs.nvrtc.compileProgram
  File "cupy_backends/cuda/libs/nvrtc.pyx", line 120, in cupy_backends.cuda.libs.nvrtc.compileProgram
  File "cupy_backends/cuda/libs/nvrtc.pyx", line 58, in cupy_backends.cuda.libs.nvrtc.check_status
cupy_backends.cuda.libs.nvrtc.NVRTCError: NVRTC_ERROR_COMPILATION (6)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "cupy/core/_reduction.pyx", line 601, in cupy.core._reduction._SimpleReductionKernel._get_function
  File "cupy/_util.pyx", line 59, in cupy._util.memoize.decorator.ret
  File "cupy/core/_reduction.pyx", line 614, in cupy.core._reduction._SimpleReductionKernel_get_cached_function
  File "cupy/core/_reduction.pyx", line 122, in cupy.core._reduction._create_reduction_function
  File "cupy/core/core.pyx", line 1986, in cupy.core.core.compile_with_cache
  File ".../cupy/cuda/compiler.py", line 431, in compile_with_cache
    return _compile_with_cache_cuda(
  File ".../cupy/cuda/compiler.py", line 510, in _compile_with_cache_cuda
    ptx, mapping = compile_using_nvrtc(
  File ".../cupy/cuda/compiler.py", line 267, in compile_using_nvrtc
    return _compile(source, options, cu_path,
  File ".../cupy/cuda/compiler.py", line 251, in _compile
    ptx, mapping = prog.compile(options, log_stream)
  File ".../cupy/cuda/compiler.py", line 635, in compile
    raise CompileException(log, self.src, self.name, options,
cupy.cuda.compiler.CompileException: /tmp/tmp0pc8hgyk/5add8bcd83840060f03651289ab356d4_2.cubin.cu(34): error: no operator "[]" matches these operands
            operand types are: const CArray<double, 1, true, true> [ const ptrdiff_t * ]

1 error detected in the compilation of "/tmp/tmp0pc8hgyk/5add8bcd83840060f03651289ab356d4_2.cubin.cu".

Exception ignored in: 'cupy.core._reduction._AbstractReductionKernel._launch'
Traceback (most recent call last):
  File "cupy/core/_reduction.pyx", line 601, in cupy.core._reduction._SimpleReductionKernel._get_function
  File "cupy/_util.pyx", line 59, in cupy._util.memoize.decorator.ret
  File "cupy/core/_reduction.pyx", line 614, in cupy.core._reduction._SimpleReductionKernel_get_cached_function
  File "cupy/core/_reduction.pyx", line 122, in cupy.core._reduction._create_reduction_function
  File "cupy/core/core.pyx", line 1986, in cupy.core.core.compile_with_cache
  File ".../cupy/cuda/compiler.py", line 431, in compile_with_cache
    return _compile_with_cache_cuda(
  File ".../cupy/cuda/compiler.py", line 510, in _compile_with_cache_cuda
    ptx, mapping = compile_using_nvrtc(
  File ".../cupy/cuda/compiler.py", line 267, in compile_using_nvrtc
    return _compile(source, options, cu_path,
  File ".../cupy/cuda/compiler.py", line 251, in _compile
    ptx, mapping = prog.compile(options, log_stream)
  File ".../cupy/cuda/compiler.py", line 635, in compile
    raise CompileException(log, self.src, self.name, options,
cupy.cuda.compiler.CompileException: /tmp/tmp0pc8hgyk/5add8bcd83840060f03651289ab356d4_2.cubin.cu(34): error: no operator "[]" matches these operands
            operand types are: const CArray<double, 1, true, true> [ const ptrdiff_t * ]

1 error detected in the compilation of "/tmp/tmp0pc8hgyk/5add8bcd83840060f03651289ab356d4_2.cubin.cu".

array(1.)
```


hi, i encourter the same problem, can show your solutions? @leofang 

Resolving patch:
diff --git a/cupy/_manipulation/rearrange.py b/cupy/_manipulation/rearrange.py
--- a/cupy/_manipulation/rearrange.py
+++ b/cupy/_manipulation/rearrange.py
@@ -3,7 +3,6 @@
 import numpy
 
 import cupy
-from cupy._core import _reduction
 from cupy._core import internal
 
 
@@ -99,9 +98,13 @@ def roll(a, shift, axis=None):
     """
     if axis is None:
         return roll(a.ravel(), shift, 0).reshape(a.shape)
-    elif isinstance(shift, cupy.ndarray):
+
+    axes = (axis,) if numpy.isscalar(axis) else axis
+    axes = tuple([  # allow_duplicate
+        internal._normalize_axis_index(ax, a.ndim) for ax in axes
+    ])
+    if isinstance(shift, cupy.ndarray):
         shift = shift.ravel()
-        axes = _reduction._get_axis(axis, a.ndim)[0]
         n_axes = max(len(axes), shift.size)
         axes = numpy.broadcast_to(axes, (n_axes,))
         shift = cupy.broadcast_to(shift, (n_axes,))
@@ -122,9 +125,7 @@ def roll(a, shift, axis=None):
 
         return a[tuple(indices)]
     else:
-        axis = _reduction._get_axis(axis, a.ndim)[0]
-
-        broadcasted = numpy.broadcast(shift, axis)
+        broadcasted = numpy.broadcast(shift, axes)
         if broadcasted.nd > 1:
             raise ValueError(
                 '\'shift\' and \'axis\' should be scalars or 1D sequences')


Test patch:
diff --git a/tests/cupy_tests/core_tests/test_reduction.py b/tests/cupy_tests/core_tests/test_reduction.py
--- a/tests/cupy_tests/core_tests/test_reduction.py
+++ b/tests/cupy_tests/core_tests/test_reduction.py
@@ -113,6 +113,29 @@ def test_warns(self, xp, c_dtype, f_dtype):
         return out
 
 
+class TestSimpleReductionFunctionInvalidAxis:
+    @pytest.mark.parametrize('axis', [
+        2,
+        (-3,),
+        (0, 7),
+    ])
+    def test_axis_overrun(self, axis):
+        for xp in (numpy, cupy):
+            a = xp.ones((2, 2))
+            with pytest.raises(numpy.AxisError):
+                a.sum(axis=axis)
+
+    @pytest.mark.parametrize('axis', [
+        (1, 1),
+        (0, -2),
+    ])
+    def test_axis_repeated(self, axis):
+        for xp in (numpy, cupy):
+            a = xp.ones((2, 2))
+            with pytest.raises(ValueError):
+                a.sum(axis=axis)
+
+
 class ReductionKernelTestBase(AbstractReductionTestBase):
 
     def get_sum_func(self):