Repository: cython/cython

Issue:
Cannot copy C arrays in nogil functions
This code:

```
cdef void foo() nogil:
    cdef double[16] x
    cdef double[16] y
    y[:] = x[:]
    y = x
```

throws the compile time error "Slicing Python object not allowed without gil" for both assignments. However, the generated code in both cases is a simple memcpy and doesn't require the GIL.


Discussion:

Resolving patch:
diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py
--- a/Cython/Compiler/ExprNodes.py
+++ b/Cython/Compiler/ExprNodes.py
@@ -5230,8 +5230,10 @@ class SliceIndexNode(ExprNode):
     #  start     ExprNode or None
     #  stop      ExprNode or None
     #  slice     ExprNode or None   constant slice object
+    #  nogil     bool               used internally
 
     subexprs = ['base', 'start', 'stop', 'slice']
+    nogil = False
 
     slice = None
 
@@ -5417,7 +5419,10 @@ def analyse_as_type(self, env):
                     base_type, MemoryView.get_axes_specs(env, [slice_node]))
         return None
 
-    nogil_check = Node.gil_error
+    def nogil_check(self, env):
+        self.nogil = env.nogil
+        return super(SliceIndexNode, self).nogil_check(env)
+
     gil_message = "Slicing Python object"
 
     get_slice_utility_code = TempitaUtilityCode.load(
@@ -5685,11 +5690,15 @@ def generate_slice_guard_code(self, code, target_size):
 
         if runtime_check:
             code.putln("if (unlikely((%s) != (%s))) {" % (runtime_check, target_size))
+            if self.nogil:
+                code.put_ensure_gil()
             code.putln(
                 'PyErr_Format(PyExc_ValueError, "Assignment to slice of wrong length,'
                 ' expected %%" CYTHON_FORMAT_SSIZE_T "d, got %%" CYTHON_FORMAT_SSIZE_T "d",'
                 ' (Py_ssize_t)(%s), (Py_ssize_t)(%s));' % (
                     target_size, runtime_check))
+            if self.nogil:
+                code.put_release_ensured_gil()
             code.putln(code.error_goto(self.pos))
             code.putln("}")
 


Test patch:
diff --git a/tests/errors/nogil.pyx b/tests/errors/nogil.pyx
--- a/tests/errors/nogil.pyx
+++ b/tests/errors/nogil.pyx
@@ -97,6 +97,15 @@ cdef int fstrings(int x, object obj) except -1 nogil:
     f"{x}"
     f"{obj}"
 
+cdef void slice_array() nogil:
+    with gil:
+        b = [1, 2, 3, 4]
+    cdef int[4] a = b[:]
+
+cdef int[:] main() nogil:
+    cdef int[4] a = [1,2,3,4]
+    return a
+
 
 _ERRORS = u"""
 4:5: Function with Python return type cannot be declared nogil
@@ -169,4 +178,8 @@ _ERRORS = u"""
 97:6: String formatting not allowed without gil
 98:4: Discarding owned Python object not allowed without gil
 98:6: String formatting not allowed without gil
+
+103:21: Coercion from Python not allowed without the GIL
+103:21: Slicing Python object not allowed without gil
+107:11: Operation not allowed without gil
 """
diff --git a/tests/run/nogil.pyx b/tests/run/nogil.pyx
--- a/tests/run/nogil.pyx
+++ b/tests/run/nogil.pyx
@@ -104,3 +104,78 @@ def test_unraisable():
     finally:
         sys.stderr = old_stderr
     return stderr.getvalue().strip()
+
+
+cdef int initialize_array() nogil:
+    cdef int[4] a = [1, 2, 3, 4]
+    return a[0] + a[1] + a[2] + a[3]
+
+cdef int copy_array() nogil:
+    cdef int[4] a
+    a[:] = [0, 1, 2, 3]
+    return a[0] + a[1] + a[2] + a[3]
+
+cdef double copy_array2() nogil:
+    cdef double[4] x = [1.0, 3.0, 5.0, 7.0]
+    cdef double[4] y
+    y[:] = x[:]
+    return y[0] + y[1] + y[2] + y[3]
+
+cdef double copy_array3() nogil:
+    cdef double[4] x = [2.0, 4.0, 6.0, 8.0]
+    cdef double[4] y
+    y = x
+    return y[0] + y[1] + y[2] + y[3]
+
+cdef void copy_array_exception(int n) nogil:
+    cdef double[5] a = [1,2,3,4,5]
+    cdef double[6] b
+    b[:n] = a
+
+def test_initalize_array():
+    """
+    >>> test_initalize_array()
+    10
+    """
+    return initialize_array()
+
+def test_copy_array():
+    """
+    >>> test_copy_array()
+    6
+    """
+    return copy_array()
+
+def test_copy_array2():
+    """
+    >>> test_copy_array2()
+    16.0
+    """
+    return copy_array2()
+
+def test_copy_array3():
+    """
+    >>> test_copy_array3()
+    20.0
+    """
+    return copy_array3()
+
+def test_copy_array_exception(n):
+    """
+    >>> test_copy_array_exception(20)
+    Traceback (most recent call last):
+        ...
+    ValueError: Assignment to slice of wrong length, expected 5, got 20
+    """
+    copy_array_exception(n)
+
+def test_copy_array_exception_nogil(n): 
+    """
+    >>> test_copy_array_exception_nogil(20)
+    Traceback (most recent call last):
+        ...
+    ValueError: Assignment to slice of wrong length, expected 5, got 20
+    """
+    cdef int cn = n
+    with nogil:
+        copy_array_exception(cn)